Add certificate_manager depends + Add property facturae_receiver_transaction_reference, facturae_item_description and facturae_article_code
#047304 #159379
This commit is contained in:
parent
94eab8c166
commit
9b9fe4ee67
|
@ -1,18 +1,21 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.pool import Pool
|
||||
from . import company
|
||||
from . import account
|
||||
from . import invoice
|
||||
from . import party
|
||||
from . import payment_type
|
||||
from . import account
|
||||
from .invoice import FACTURAE_SCHEMA_VERSION
|
||||
|
||||
__all__ = [FACTURAE_SCHEMA_VERSION]
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
account.Configuration,
|
||||
account.ConfigurationFacturae,
|
||||
account.TaxTemplate,
|
||||
account.Tax,
|
||||
company.Company,
|
||||
invoice.Invoice,
|
||||
invoice.InvoiceLine,
|
||||
invoice.CreditInvoiceStart,
|
||||
|
|
52
account.py
52
account.py
|
@ -1,10 +1,8 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['TaxTemplate', 'Tax']
|
||||
|
||||
from trytond.model import ModelSQL, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.modules.company.model import CompanyValueMixin
|
||||
|
||||
REPORT_TYPES = [
|
||||
(None, ""),
|
||||
|
@ -43,6 +41,50 @@ REPORT_TYPES = [
|
|||
]
|
||||
|
||||
|
||||
class Configuration(metaclass=PoolMeta):
|
||||
__name__ = 'account.configuration'
|
||||
facturae_certificate = fields.MultiValue(
|
||||
fields.Many2One('certificate', "Factura-e Certificate",
|
||||
help='Certificate to sign Factura-e'))
|
||||
facturae_service = fields.MultiValue(
|
||||
fields.Selection('get_facturae_service', "Factura-e Service",
|
||||
help='Service to be used when post the invoice'))
|
||||
invoice_facturae_after = fields.TimeDelta("Send Factura-e after",
|
||||
help="Grace period after which the invoice will be sent to the facturae "
|
||||
"service. Applies only if a worker queue is activated.")
|
||||
|
||||
@classmethod
|
||||
def default_facturae_service(cls, **pattern):
|
||||
return cls.multivalue_model('facturae_service').default_facturae_service()
|
||||
|
||||
@classmethod
|
||||
def get_facturae_service(cls):
|
||||
pool = Pool()
|
||||
ConfigurationFacturae = pool.get('account.configuration.facturae')
|
||||
return ConfigurationFacturae.fields_get(['facturae_service'])['facturae_service']['selection']
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in {'facturae_certificate', 'facturae_service'}:
|
||||
return pool.get('account.configuration.facturae')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
|
||||
class ConfigurationFacturae(ModelSQL, CompanyValueMixin):
|
||||
"Account Configuration Factura-e"
|
||||
__name__ = 'account.configuration.facturae'
|
||||
facturae_certificate = fields.Many2One('certificate', "Factura-e Certificate",
|
||||
help='Certificate to sign Factura-e')
|
||||
facturae_service = fields.Selection([
|
||||
(None, ''),
|
||||
], "Factura-e Service")
|
||||
|
||||
@staticmethod
|
||||
def default_facturae_service():
|
||||
return None
|
||||
|
||||
|
||||
class TaxTemplate(metaclass=PoolMeta):
|
||||
__name__ = 'account.tax.template'
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<!-- account.configuration -->
|
||||
<record model="ir.ui.view" id="configuration_view_form">
|
||||
<field name="model">account.configuration</field>
|
||||
<field name="inherit" ref="account.configuration_view_form"/>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
14
company.py
14
company.py
|
@ -1,14 +0,0 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['Company']
|
||||
|
||||
|
||||
class Company(metaclass=PoolMeta):
|
||||
__name__ = 'company.company'
|
||||
facturae_certificate = fields.Binary('Factura-e Certificate',
|
||||
help='The certificate to generate the XAdES electronic firm for '
|
||||
'invoices.')
|
||||
|
12
company.xml
12
company.xml
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="company_view_form">
|
||||
<field name="model">company.company</field>
|
||||
<field name="inherit" ref="company.company_view_form"/>
|
||||
<field name="name">company_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
150
invoice.py
150
invoice.py
|
@ -25,6 +25,8 @@ from trytond import backend
|
|||
from trytond.i18n import gettext
|
||||
from trytond.exceptions import UserError
|
||||
|
||||
FACTURAE_SCHEMA_VERSION = '3.2.2'
|
||||
|
||||
# Get from XSD scheme of Facturae 3.2.2
|
||||
# http://www.facturae.gob.es/formato/Versiones/Facturaev3_2_2.xml
|
||||
RECTIFICATIVE_REASON_CODES = [
|
||||
|
@ -142,16 +144,17 @@ class Invoice(metaclass=PoolMeta):
|
|||
filename='invoice_facturae_filename')
|
||||
invoice_facturae_filename = fields.Function(fields.Char(
|
||||
'Factura-e filename'), 'get_invoice_facturae_filename')
|
||||
invoice_facturae_sent = fields.Boolean('Factura-e Sent')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Invoice, cls).__setup__()
|
||||
cls._check_modify_exclude.add('invoice_facturae')
|
||||
cls._check_modify_exclude |= {'invoice_facturae', 'invoice_facturae_sent'}
|
||||
cls._buttons.update({
|
||||
'generate_facturae_wizard': {
|
||||
'invisible': ((Eval('type') != 'out')
|
||||
| ~Eval('state').in_(['posted', 'paid'])),
|
||||
'readonly': Bool(Eval('invoice_facturae')),
|
||||
'readonly': Bool(Eval('invoice_facturae_sent')),
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -163,6 +166,7 @@ class Invoice(metaclass=PoolMeta):
|
|||
default = default.copy()
|
||||
default.setdefault('invoice_facturae', None)
|
||||
default.setdefault('rectificative_reason_code', None)
|
||||
default.setdefault('invoice_facturae_sent', None)
|
||||
return super(Invoice, cls).copy(invoices, default=default)
|
||||
|
||||
def get_credited_invoices(self, name):
|
||||
|
@ -207,6 +211,28 @@ class Invoice(metaclass=PoolMeta):
|
|||
if ml.account.type.receivable],
|
||||
key=attrgetter('maturity_date'))
|
||||
|
||||
@classmethod
|
||||
def draft(cls, invoices):
|
||||
invoice_facturae_sends = [invoice for invoice in invoices if invoice.invoice_facturae_sent]
|
||||
if invoice_facturae_sends:
|
||||
names = ', '.join(m.rec_name for m in invoice_facturae_sends[:5])
|
||||
if len(invoice_facturae_sends) > 5:
|
||||
names += '...'
|
||||
raise UserError(gettext('account_invoice_facturae.msg_draft_invoice_facturae_sent',
|
||||
invoices=names))
|
||||
|
||||
cls.write(invoices, {
|
||||
'invoice_facturae': None,
|
||||
})
|
||||
super(Invoice, cls).draft(invoices)
|
||||
|
||||
@classmethod
|
||||
def post(cls, invoices):
|
||||
super(Invoice, cls).post(invoices)
|
||||
|
||||
for invoice in invoices:
|
||||
cls.__queue__.generate_facturae(invoice)
|
||||
|
||||
def _credit(self, **values):
|
||||
credit = super(Invoice, self)._credit(**values)
|
||||
rectificative_reason_code = Transaction().context.get(
|
||||
|
@ -221,23 +247,38 @@ class Invoice(metaclass=PoolMeta):
|
|||
def generate_facturae_wizard(cls, invoices):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def generate_facturae_default(cls, invoices, certificate_password):
|
||||
to_write = ([],)
|
||||
for invoice in invoices:
|
||||
if invoice.invoice_facturae:
|
||||
continue
|
||||
facturae_content = invoice.get_facturae()
|
||||
invoice._validate_facturae(facturae_content)
|
||||
def generate_facturae(self, certificate=None, service=None):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('account.configuration')
|
||||
Invoice = pool.get('account.invoice')
|
||||
|
||||
config = Configuration(1)
|
||||
transaction = Transaction()
|
||||
|
||||
if not self.invoice_facturae:
|
||||
facturae_content = self.get_facturae()
|
||||
self._validate_facturae(facturae_content)
|
||||
if backend.name != 'sqlite':
|
||||
invoice_facturae = invoice._sign_facturae(
|
||||
facturae_content, certificate_password)
|
||||
invoice_facturae = self._sign_facturae(facturae_content,
|
||||
'default', certificate)
|
||||
else:
|
||||
invoice_facturae = facturae_content
|
||||
to_write[0].append(invoice)
|
||||
to_write += ({'invoice_facturae': invoice_facturae},)
|
||||
if to_write[0]:
|
||||
cls.write(*to_write)
|
||||
self.invoice_facturae = invoice_facturae
|
||||
self.save()
|
||||
|
||||
# send facturae to service
|
||||
if not service and config.facturae_service:
|
||||
service = config.facturae_service
|
||||
if self.invoice_facturae and service:
|
||||
with transaction.set_context(
|
||||
queue_scheduled_at=config.invoice_facturae_after):
|
||||
Invoice.__queue__.send_facturae(self, service)
|
||||
|
||||
def send_facturae(self, service):
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
method = 'send_facturae_%s' % service
|
||||
getattr(Invoice, method)(self)
|
||||
|
||||
def get_facturae(self):
|
||||
jinja_env = Environment(
|
||||
|
@ -427,32 +468,41 @@ class Invoice(metaclass=PoolMeta):
|
|||
invoice=self.rec_name, message=e))
|
||||
return True
|
||||
|
||||
def _sign_facturae(self, xml_string, certificate_password):
|
||||
def _sign_facturae(self, xml_string, service='default', certificate=None):
|
||||
"""
|
||||
Inspired by https://github.com/pedrobaeza/l10n-spain/blob/d01d049934db55130471e284012be7c860d987eb/l10n_es_facturae/wizard/create_facturae.py
|
||||
"""
|
||||
if not self.company.facturae_certificate:
|
||||
raise UserError(gettext(
|
||||
'account_invoice_facturae.missing_certificate',
|
||||
company=self.company.rec_name))
|
||||
Configuration = Pool().get('account.configuration')
|
||||
|
||||
if not certificate:
|
||||
certificate = Configuration(1).facturae_certificate
|
||||
if not certificate:
|
||||
raise UserError(gettext(
|
||||
'account_invoice_facturae.msg_missing_certificate'))
|
||||
|
||||
certificate_facturae = certificate.pem_certificate
|
||||
certificate_password = certificate.certificate_password
|
||||
|
||||
logger = logging.getLogger('account_invoice_facturae')
|
||||
|
||||
unsigned_file = NamedTemporaryFile(suffix='.xml', delete=False)
|
||||
unsigned_file.write(xml_string)
|
||||
unsigned_file.close()
|
||||
with NamedTemporaryFile(suffix='.xml', delete=False) as unsigned_file:
|
||||
unsigned_file.write(xml_string)
|
||||
|
||||
cert_file = NamedTemporaryFile(suffix='.pfx', delete=False)
|
||||
cert_file.write(self.company.facturae_certificate)
|
||||
cert_file.close()
|
||||
with NamedTemporaryFile(suffix='.pfx', delete=False) as cert_file:
|
||||
cert_file.write(certificate_facturae)
|
||||
|
||||
def _sign_file(cert, password, request):
|
||||
# get key and certificates from PCK12 file
|
||||
(
|
||||
private_key,
|
||||
certificate,
|
||||
additional_certificates,
|
||||
) = pkcs12.load_key_and_certificates(cert, password)
|
||||
try:
|
||||
(
|
||||
private_key,
|
||||
certificate,
|
||||
additional_certificates,
|
||||
) = pkcs12.load_key_and_certificates(cert, password)
|
||||
except ValueError as e:
|
||||
logger.warning("Error load_key_and_certificates file",
|
||||
exc_info=True)
|
||||
raise UserError(str(e))
|
||||
|
||||
# DER is an ASN.1 encoding type
|
||||
crt = certificate.public_bytes(serialization.Encoding.DER)
|
||||
|
@ -656,7 +706,7 @@ class Invoice(metaclass=PoolMeta):
|
|||
return etree.tostring(root, xml_declaration=True, encoding="UTF-8")
|
||||
|
||||
signed_file_content = _sign_file(
|
||||
self.company.facturae_certificate,
|
||||
certificate_facturae,
|
||||
certificate_password.encode(),
|
||||
xml_string,
|
||||
)
|
||||
|
@ -670,6 +720,23 @@ class Invoice(metaclass=PoolMeta):
|
|||
class InvoiceLine(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
@property
|
||||
def facturae_article_code(self):
|
||||
return self.product and self.product.code or ''
|
||||
|
||||
@property
|
||||
def facturae_item_description(self):
|
||||
return (
|
||||
(self.description and self.description[:2500])
|
||||
or (self.product and self.product.rec_name[:2500])
|
||||
or '#'+str(self.id)
|
||||
)
|
||||
|
||||
@property
|
||||
def facturae_receiver_transaction_reference(self):
|
||||
# TODO Issuer/ReceiverTransactionDate (sale, contract...)
|
||||
return ''
|
||||
|
||||
@property
|
||||
def taxes_outputs(self):
|
||||
"""Return list of 'impuestos repecutidos'"""
|
||||
|
@ -723,17 +790,17 @@ class GenerateFacturaeStart(ModelView):
|
|||
'Generate Factura-e file - Start'
|
||||
__name__ = 'account.invoice.generate_facturae.start'
|
||||
service = fields.Selection([
|
||||
('default', 'Default'),
|
||||
], 'Service', required=True)
|
||||
certificate_password = fields.Char('Certificate Password',
|
||||
(None, ''),
|
||||
], 'Service')
|
||||
certificate_facturae = fields.Many2One('certificate',
|
||||
'Certificate Factura-e',
|
||||
states={
|
||||
'required': Eval('service') == 'default',
|
||||
'invisible': Eval('service') != 'default',
|
||||
'invisible': ~Bool(Eval('service')),
|
||||
}, depends=['service'])
|
||||
|
||||
@staticmethod
|
||||
def default_service():
|
||||
return 'default'
|
||||
return None
|
||||
|
||||
|
||||
class GenerateFacturae(Wizard):
|
||||
|
@ -750,6 +817,7 @@ class GenerateFacturae(Wizard):
|
|||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
invoices = Invoice.browse(Transaction().context['active_ids'])
|
||||
service = 'generate_facturae_%s' % self.start.service
|
||||
getattr(Invoice, service)(invoices, self.start.certificate_password)
|
||||
for invoice in invoices:
|
||||
invoice.generate_facturae(certificate=self.start.certificate_facturae,
|
||||
service=self.start.service)
|
||||
return 'end'
|
||||
|
|
92
locale/ca.po
92
locale/ca.po
|
@ -2,6 +2,22 @@
|
|||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:account.configuration,certificate_service_facturae:"
|
||||
msgid "Certificate Service Post Factura-e"
|
||||
msgstr "Servei certificat comptabilitar Factura-e"
|
||||
|
||||
msgctxt "field:account.configuration,invoice_facturae_after:"
|
||||
msgid "Send Factura-e after"
|
||||
msgstr "Envia "
|
||||
|
||||
msgctxt "field:account.configuration.facturae,certificate_service_facturae:"
|
||||
msgid "Certificate Service Post Factura-e"
|
||||
msgstr "Servei certificat comptabilitar Factura-e"
|
||||
|
||||
msgctxt "field:account.configuration.facturae,company:"
|
||||
msgid "Company"
|
||||
msgstr "Empresa"
|
||||
|
||||
msgctxt "field:account.invoice,credited_invoices:"
|
||||
msgid "Credited Invoices"
|
||||
msgstr "Factures abonades"
|
||||
|
@ -22,6 +38,10 @@ msgctxt "field:account.invoice.credit.start,rectificative_reason_code:"
|
|||
msgid "Rectificative Reason Code"
|
||||
msgstr "Codi motiu rectificació"
|
||||
|
||||
msgctxt "field:account.invoice.generate_facturae.start,certificate_facturae:"
|
||||
msgid "Certificate Factura-e"
|
||||
msgstr "Certificat"
|
||||
|
||||
msgctxt "field:account.invoice.generate_facturae.start,certificate_password:"
|
||||
msgid "Certificate Password"
|
||||
msgstr "Contrasenya certificat"
|
||||
|
@ -42,10 +62,6 @@ msgctxt "field:account.tax.template,report_type:"
|
|||
msgid "Report Type"
|
||||
msgstr "Tipus informe"
|
||||
|
||||
msgctxt "field:company.company,facturae_certificate:"
|
||||
msgid "Factura-e Certificate"
|
||||
msgstr "Certificat Factura-e"
|
||||
|
||||
msgctxt "field:party.party,facturae_person_type:"
|
||||
msgid "Person Type"
|
||||
msgstr "Tipus persona"
|
||||
|
@ -70,9 +86,13 @@ msgctxt "field:party.party,unidad_tramitadora:"
|
|||
msgid "Unidad tramitadora"
|
||||
msgstr "Unidad tramitadora"
|
||||
|
||||
msgctxt "help:company.company,facturae_certificate:"
|
||||
msgid "The certificate to generate the XAdES electronic firm for invoices."
|
||||
msgstr "El certificat per generar la firma electrònica XAdES de les factures."
|
||||
msgctxt "help:account.configuration,certificate_service_facturae:"
|
||||
msgid "Service to be used when post the invoice"
|
||||
msgstr "El servei que s'utilitzarà quan es comptabilitzi una factura"
|
||||
|
||||
msgctxt "model:account.configuration.facturae,name:"
|
||||
msgid "Account Configuration Factura-e"
|
||||
msgstr "Configuració comptable Factura-e"
|
||||
|
||||
msgctxt "model:account.invoice.generate_facturae.start,name:"
|
||||
msgid "Generate Factura-e file - Start"
|
||||
|
@ -84,8 +104,8 @@ msgstr "Genera Factura-e"
|
|||
|
||||
msgctxt "model:ir.message,text:company_address_fields"
|
||||
msgid ""
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in default address of "
|
||||
"company\\'s Party \"%(party)s\"."
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in default address"
|
||||
" of company\\'s Party \"%(party)s\"."
|
||||
msgstr ""
|
||||
"Falta el carrer, codi postal, ciutat, província o país de la direcció per "
|
||||
"defecte del tercer de l'empresa \"%(party)s\"."
|
||||
|
@ -100,42 +120,40 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.message,text:error_signing"
|
||||
msgid ""
|
||||
"Error signing invoice \"%(invoice)s\".\\nMessage returned by signing "
|
||||
"process: %(process_output)s"
|
||||
"Error signing invoice \"%(invoice)s\".\n"
|
||||
"Message returned by signing process: %(process_output)s"
|
||||
msgstr ""
|
||||
"Error firmant la factura \"%(invoice)s\".\n"
|
||||
"Missatge retornat pel procés de firma: %(process_output)s"
|
||||
|
||||
msgctxt "model:ir.message,text:incompatible_facturae_type_account_bank"
|
||||
msgid ""
|
||||
"The Factura-e Type and Account Bank Kind of Payment Type \"%(payment_type)s\""
|
||||
" are not compatible."
|
||||
"The Factura-e Type and Account Bank Kind of Payment Type "
|
||||
"\"%(payment_type)s\" are not compatible."
|
||||
msgstr ""
|
||||
"El Tipus Fatura-e i el Tipus compte bancari del Tipus de pagament "
|
||||
"\"%(payment_type)s\" no són compatibles."
|
||||
|
||||
msgctxt "model:ir.message,text:invalid_factura_xml_file"
|
||||
msgid ""
|
||||
"The Factura-e file (XML) generated for invoice \"%(invoice)s\" is not valid "
|
||||
"against the oficial XML Schema Definition:\\n%(message)s"
|
||||
"The Factura-e file (XML) generated for invoice \"%(invoice)s\" is not valid against the oficial XML Schema Definition:\n"
|
||||
"%(message)s"
|
||||
msgstr ""
|
||||
"El fitxer Factura-e (XML) generat per la factura \"%(invoice)s\" no és vàlid segons el XML Schema Definition oficial:\n"
|
||||
"%(message)s"
|
||||
|
||||
msgctxt "model:ir.message,text:invoice_address_fields"
|
||||
msgid ""
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address of "
|
||||
"invoice \"%(invoice)s\"."
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address"
|
||||
" of invoice \"%(invoice)s\"."
|
||||
msgstr ""
|
||||
"Falta el carrer, codi postal, ciutat, subdivisió o país a l'adreça de la "
|
||||
"factura \"%(invoice)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:missing_account_bank_module"
|
||||
msgid ""
|
||||
"You must to install \"account_bank\" module to inform the Bank Account in "
|
||||
"invoices.\\nThe payment type \"%(payment_type)s\" used in invoice "
|
||||
"\"%(invoice)s\" is configured as \"Direct Debit\" or \"Credit Transfer\" and"
|
||||
" it requires the company/party bank account."
|
||||
"You must to install \"account_bank\" module to inform the Bank Account in invoices.\n"
|
||||
"The payment type \"%(payment_type)s\" used in invoice \"%(invoice)s\" is configured as \"Direct Debit\" or \"Credit Transfer\" and it requires the company/party bank account."
|
||||
msgstr ""
|
||||
"S'ha d'instal·lar el mòdul \"account_bank\" per poder indicar el compte bancari a les factures.\n"
|
||||
"El tipus de pagament \"%(payment_type)s\" utilitzat a la factura \"%(invoice)s\" està configurat com \"Rebut domiciliat\" o \"Transferència\" i es requereix el copmte bancari de l'empresa o del tercer."
|
||||
|
@ -145,10 +163,6 @@ msgid "The Bank Account is missing in some move lines of invoice \"%(invoice)s\"
|
|||
msgstr ""
|
||||
"Falta el compte bancari en algun dels apunts de la factura \"%(invoice)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:missing_certificate"
|
||||
msgid "Missing Factura-e Certificate in company \"%(company)s\"."
|
||||
msgstr "Falta el Certificat Factura-e a l'empresa \"%(company)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:missing_iban"
|
||||
msgid ""
|
||||
"The Bank Account \"%(bank_account)s\" used in invoice \"%(invoice)s\" "
|
||||
|
@ -172,6 +186,15 @@ msgstr ""
|
|||
"Falta el Tipus Factura-e al tipus de pagament \"%(payment_type)s\" utilitzat"
|
||||
" a la factura \"%(invoice)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_missing_certificate"
|
||||
msgid "Missing default configuration Factura-e Certificate."
|
||||
msgstr "Falta la configuració del Certificat Factura-e"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_missing_password_certificate"
|
||||
msgid "Missing Password Factura-e Certificate in company \"%(company)s\"."
|
||||
msgstr ""
|
||||
"Falta la contrasenya del certificat Factura-e a l'empresa \"%(company)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:no_rate"
|
||||
msgid "No rate found for currency \"%(currency)s\" on \"%(date)s\""
|
||||
msgstr ""
|
||||
|
@ -188,9 +211,8 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.message,text:party_name_surname"
|
||||
msgid ""
|
||||
"The name of \"%(party)s\" of invoice \"%(invoice)s\" doesn\\'t contain the "
|
||||
"Name and the, at least, First Surname.\\n They must to be separated by one "
|
||||
"space: Name Surname."
|
||||
"The name of \"%(party)s\" of invoice \"%(invoice)s\" doesn\\'t contain the Name and the, at least, First Surname.\n"
|
||||
"They must to be separated by one space: Name Surname."
|
||||
msgstr ""
|
||||
"El nom del tercer \"%(party)s\" de la factura \"%(invoice)s\" no conté el nom i, al menys, el primer cognom.\n"
|
||||
"Aquests han d'anar separats per un espai: Nom Cognom."
|
||||
|
@ -205,7 +227,7 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.model.button,string:generate_facturae_button"
|
||||
msgid "Generate Facturae"
|
||||
msgstr "Genera Facturae"
|
||||
msgstr "Crea Facturae"
|
||||
|
||||
msgctxt "selection:account.invoice,rectificative_reason_code:"
|
||||
msgid ""
|
||||
|
@ -755,6 +777,10 @@ msgctxt "selection:party.party,facturae_residence_type:"
|
|||
msgid "Resident in other EU country"
|
||||
msgstr "Resident a la Unió Europea (excepte Espanya)"
|
||||
|
||||
msgctxt "view:account.configuration:"
|
||||
msgid "Factura-e"
|
||||
msgstr "Factura-e "
|
||||
|
||||
msgctxt "view:account.invoice.generate_facturae.start:"
|
||||
msgid ""
|
||||
"It will generate a Factura-e file for selected invoices, will sign them with"
|
||||
|
@ -769,6 +795,14 @@ msgctxt "view:account.invoice:"
|
|||
msgid "Generate Factura-e"
|
||||
msgstr "Genera Factura-e"
|
||||
|
||||
msgctxt "view:account.tax.template:"
|
||||
msgid "Factura-e"
|
||||
msgstr "Factura-e"
|
||||
|
||||
msgctxt "view:account.tax:"
|
||||
msgid "Factura-e"
|
||||
msgstr "Factura-e"
|
||||
|
||||
msgctxt "view:party.party:"
|
||||
msgid "Factura-e"
|
||||
msgstr "Factura-e"
|
||||
|
|
68
locale/es.po
68
locale/es.po
|
@ -2,6 +2,22 @@
|
|||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:account.configuration,certificate_service_facturae:"
|
||||
msgid "Certificate Service Post Factura-e"
|
||||
msgstr "Servicio certificado contabilizar Factura-e"
|
||||
|
||||
msgctxt "field:account.configuration,invoice_facturae_after:"
|
||||
msgid "Send Factura-e after"
|
||||
msgstr "Enviar Factura-e después de"
|
||||
|
||||
msgctxt "field:account.configuration.facturae,certificate_service_facturae:"
|
||||
msgid "Certificate Service Post Factura-e"
|
||||
msgstr "Servicio certificado contabilizar Factura-e"
|
||||
|
||||
msgctxt "field:account.configuration.facturae,company:"
|
||||
msgid "Company"
|
||||
msgstr "Empresa"
|
||||
|
||||
msgctxt "field:account.invoice,credited_invoices:"
|
||||
msgid "Credited Invoices"
|
||||
msgstr "Facturas abonadas"
|
||||
|
@ -22,6 +38,10 @@ msgctxt "field:account.invoice.credit.start,rectificative_reason_code:"
|
|||
msgid "Rectificative Reason Code"
|
||||
msgstr "Código motivo rectificación"
|
||||
|
||||
msgctxt "field:account.invoice.generate_facturae.start,certificate_facturae:"
|
||||
msgid "Certificate Factura-e"
|
||||
msgstr "Certificado Factura-e"
|
||||
|
||||
msgctxt "field:account.invoice.generate_facturae.start,certificate_password:"
|
||||
msgid "Certificate Password"
|
||||
msgstr "Contraseña certificado"
|
||||
|
@ -42,10 +62,6 @@ msgctxt "field:account.tax.template,report_type:"
|
|||
msgid "Report Type"
|
||||
msgstr "Tipo informe"
|
||||
|
||||
msgctxt "field:company.company,facturae_certificate:"
|
||||
msgid "Factura-e Certificate"
|
||||
msgstr "Certificado Factura-e"
|
||||
|
||||
msgctxt "field:party.party,facturae_person_type:"
|
||||
msgid "Person Type"
|
||||
msgstr "Tipo persona"
|
||||
|
@ -70,10 +86,13 @@ msgctxt "field:party.party,unidad_tramitadora:"
|
|||
msgid "Unidad tramitadora"
|
||||
msgstr "Unidad tramitadora"
|
||||
|
||||
msgctxt "help:company.company,facturae_certificate:"
|
||||
msgid "The certificate to generate the XAdES electronic firm for invoices."
|
||||
msgstr ""
|
||||
"El certificado para generar la firma electrónica XAdES de las facturas."
|
||||
msgctxt "help:account.configuration,certificate_service_facturae:"
|
||||
msgid "Service to be used when post the invoice"
|
||||
msgstr "El servicio que se utilitzará cuando se contabilize una factura"
|
||||
|
||||
msgctxt "model:account.configuration.facturae,name:"
|
||||
msgid "Account Configuration Factura-e"
|
||||
msgstr "Configuración contable Factura-e"
|
||||
|
||||
msgctxt "model:account.invoice.generate_facturae.start,name:"
|
||||
msgid "Generate Factura-e file - Start"
|
||||
|
@ -85,8 +104,8 @@ msgstr "Generar Factura-e"
|
|||
|
||||
msgctxt "model:ir.message,text:company_address_fields"
|
||||
msgid ""
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in default address of "
|
||||
"company\\'s Party \"%(party)s\"."
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in default address"
|
||||
" of company\\'s Party \"%(party)s\"."
|
||||
msgstr ""
|
||||
"Falta la calle, código postal, ciudad, provincia o país de la dirección por "
|
||||
"defecto del tercero de la empresa \"%(party)s\"."
|
||||
|
@ -109,8 +128,8 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.message,text:incompatible_facturae_type_account_bank"
|
||||
msgid ""
|
||||
"The Factura-e Type and Account Bank Kind of Payment Type \"%(payment_type)s\""
|
||||
" are not compatible."
|
||||
"The Factura-e Type and Account Bank Kind of Payment Type "
|
||||
"\"%(payment_type)s\" are not compatible."
|
||||
msgstr ""
|
||||
"El Tipo Factura-e y el Tipo cuenta bancaria del Tipo de pago "
|
||||
"\"%(payment_type)s\" no son compatibles."
|
||||
|
@ -125,8 +144,8 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.message,text:invoice_address_fields"
|
||||
msgid ""
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address of "
|
||||
"invoice \"%(invoice)s\"."
|
||||
"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address"
|
||||
" of invoice \"%(invoice)s\"."
|
||||
msgstr ""
|
||||
"Falta la calle, código postal, ciudad, província o país de la dirección de "
|
||||
"la factura \"%(invoice)s\"."
|
||||
|
@ -145,10 +164,6 @@ msgstr ""
|
|||
"Falta la cuenta bancaria en alguno de los apuntes de la factura "
|
||||
"\"%(invoice)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:missing_certificate"
|
||||
msgid "Missing Factura-e Certificate in company \"%(company)s\"."
|
||||
msgstr "Falta el Certificado Factura-e en la empresa \"%(company)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:missing_iban"
|
||||
msgid ""
|
||||
"The Bank Account \"%(bank_account)s\" used in invoice \"%(invoice)s\" "
|
||||
|
@ -170,6 +185,21 @@ msgstr ""
|
|||
"Falta el Tipo Factura-e en el tipo de pago \"%(payment_type)s\" usado en la "
|
||||
"factura \"%(invoice)s\""
|
||||
|
||||
msgctxt "model:ir.message,text:msg_missing_certificate"
|
||||
msgid "Missing default configuration Factura-e Certificate."
|
||||
msgstr "Falta la configuració del Certificat Factura-e"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_missing_certificate_service"
|
||||
msgid "Missing default \"%(service)s\" Factura-e Certificate."
|
||||
msgstr ""
|
||||
"Falta el por defecto del servicio \"%(service)s\" del certificado Factura-e."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_missing_password_certificate"
|
||||
msgid "Missing Password Factura-e Certificate in company \"%(company)s\"."
|
||||
msgstr ""
|
||||
"Falta la contraseña del certificado Factura-e Certificate en la empresa "
|
||||
"\"%(company)s\"."
|
||||
|
||||
msgctxt "model:ir.message,text:no_rate"
|
||||
msgid "No rate found for currency \"%(currency)s\" on \"%(date)s\""
|
||||
msgstr ""
|
||||
|
@ -202,7 +232,7 @@ msgstr ""
|
|||
|
||||
msgctxt "model:ir.model.button,string:generate_facturae_button"
|
||||
msgid "Generate Facturae"
|
||||
msgstr ""
|
||||
msgstr "Crear Facturae"
|
||||
|
||||
msgctxt "selection:account.invoice,rectificative_reason_code:"
|
||||
msgid "Applicable Date/Period"
|
||||
|
|
11
message.xml
11
message.xml
|
@ -3,8 +3,11 @@
|
|||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data grouped="1">
|
||||
<record model="ir.message" id="missing_certificate">
|
||||
<field name="text">Missing Factura-e Certificate in company "%(company)s".</field>
|
||||
<record model="ir.message" id="msg_missing_certificate">
|
||||
<field name="text">Missing default configuration Factura-e Certificate.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_missing_password_certificate">
|
||||
<field name="text">Missing Password Factura-e Certificate in company "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="company_vat_identifier">
|
||||
<field name="text">Missing VAT Identifier in company\'s Party "%(party)s", or its length is not between 3 and 30.</field>
|
||||
|
@ -55,6 +58,8 @@ Message returned by signing process: %(process_output)s</field>
|
|||
<record model="ir.message" id="incompatible_facturae_type_account_bank">
|
||||
<field name="text">The Factura-e Type and Account Bank Kind of Payment Type "%(payment_type)s" are not compatible.</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.message" id="msg_draft_invoice_facturae_sent">
|
||||
<field name="text">Can not draft "%(invoices)s" that have been sent to Factura-e.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
<StartDate>{{ invoice.credited_invoices[0].move.period.start_date.isoformat() }}</StartDate>
|
||||
<EndDate>{{ invoice.credited_invoices[0].move.period.end_date.isoformat() }}</EndDate>
|
||||
</TaxPeriod>
|
||||
{# TODO: Methods not supported:
|
||||
{# TODO: Methods not supported:
|
||||
- 02 (solo se anotan los detalles ya rectificados)
|
||||
- 03 (Rectificación por descuento por volumen de operaciones durante un periodo)
|
||||
- 04 (Autorizadas por la Agencia Tributaria)
|
||||
|
@ -167,6 +167,7 @@
|
|||
{% endif %}
|
||||
<TaxCurrencyCode>EUR</TaxCurrencyCode>
|
||||
<LanguageName>{{ invoice.party_lang[:2] if invoice.party_lang else 'es' }}</LanguageName>
|
||||
<ReceiverTransactionReference>{{ invoice.reference and invoice.reference[:20] or '' }}</ReceiverTransactionReference>
|
||||
</InvoiceIssueData>
|
||||
<TaxesOutputs>
|
||||
{% for invoice_tax in invoice.taxes_outputs %}
|
||||
|
@ -247,14 +248,14 @@
|
|||
<InvoiceLine>
|
||||
{# TODO: optional, not supported
|
||||
- Issuer/ReceiverContractReference, Issuer/ReceiverContractDate (contract)
|
||||
- Issuer/ReceiverTransactionReference, Issuer/ReceiverTransactionDate (sale, contract...)
|
||||
- FileReference, FileDate
|
||||
- SequenceNumber
|
||||
- DeliveryNotesReferences (account_invoice_stock)
|
||||
- TransactionDate
|
||||
- Extensions
|
||||
#}
|
||||
<ItemDescription>{{ (line.description and line.description[:2500]) or (line.product and line.product.rec_name[:2500]) or '#'+line.id|string }}</ItemDescription>
|
||||
<ReceiverTransactionReference>{{ line.facturae_receiver_transaction_reference }}</ReceiverTransactionReference>
|
||||
<ItemDescription>{{ line.facturae_item_description }}</ItemDescription>
|
||||
<Quantity>{{ line.quantity }}</Quantity>
|
||||
<UnitOfMeasure>{{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }}</UnitOfMeasure>
|
||||
<UnitPriceWithoutTax>{{ Currency.compute(invoice.currency, line.unit_price, euro) }}</UnitPriceWithoutTax>
|
||||
|
@ -339,7 +340,7 @@
|
|||
{% endif %}
|
||||
{# TODO: SpecialTaxableEvent not supported #}
|
||||
{% if line.product and line.product.code %}
|
||||
<ArticleCode>{{ line.product.code[:20] }}</ArticleCode>
|
||||
<ArticleCode>{{ line.facturae_article_code[:20] }}</ArticleCode>
|
||||
{% endif %}
|
||||
</InvoiceLine>
|
||||
{% endfor %}
|
||||
|
|
|
@ -23,6 +23,8 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
|
|||
'Test invoice generation'
|
||||
|
||||
pool = Pool()
|
||||
Configuration = pool.get('account.configuration')
|
||||
Certificate = pool.get('certificate')
|
||||
Account = pool.get('account.account')
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
Invoice = pool.get('account.invoice')
|
||||
|
@ -61,12 +63,15 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
|
|||
company.party.save()
|
||||
company.save()
|
||||
|
||||
# Save certificate into company
|
||||
with open(os.path.join(
|
||||
CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file:
|
||||
company.facturae_certificate = cert_file.read()
|
||||
|
||||
with set_company(company):
|
||||
certificate = Certificate()
|
||||
certificate.name = 'dummy Certificate'
|
||||
# Save certificate
|
||||
with open(os.path.join(
|
||||
CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file:
|
||||
certificate.pem_certificate = cert_file.read()
|
||||
certificate.save()
|
||||
|
||||
create_chart(company, tax=True)
|
||||
|
||||
fiscalyear = set_invoice_sequences(get_fiscalyear(company))
|
||||
|
@ -199,7 +204,7 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
|
|||
invoice.save()
|
||||
Invoice.post([invoice])
|
||||
|
||||
Invoice.generate_facturae_default([invoice], 'privatepassword')
|
||||
invoice.generate_facturae()
|
||||
self.assertNotEqual(invoice.invoice_facturae, None)
|
||||
self.assertEqual(invoice.invoice_facturae_filename, 'facturae-1.xsig')
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@ depends:
|
|||
account
|
||||
account_invoice
|
||||
account_payment_type
|
||||
certificate_manager
|
||||
extras_depend:
|
||||
account_bank
|
||||
account_es
|
||||
xml:
|
||||
company.xml
|
||||
account.xml
|
||||
invoice.xml
|
||||
party.xml
|
||||
payment_type.xml
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook" position="before">
|
||||
<newline/>
|
||||
<label name="facturae_certificate"/>
|
||||
<field name="facturae_certificate"/>
|
||||
</xpath>
|
||||
</data>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license types. -->
|
||||
<data>
|
||||
<xpath expr="/form/separator[@id='invoice']" position="before">
|
||||
<separator id="facturae" string="Factura-e" colspan="4"/>
|
||||
<label name="facturae_certificate"/>
|
||||
<field name="facturae_certificate"/>
|
||||
<label name="facturae_service"/>
|
||||
<field name="facturae_service"/>
|
||||
<label name="invoice_facturae_after"/>
|
||||
<field name="invoice_facturae_after"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</data>
|
|
@ -10,7 +10,7 @@
|
|||
yalign="0.0" xalign="0.0" xexpand="1"/>
|
||||
<label name="service"/>
|
||||
<field name="service"/>
|
||||
<label name="certificate_password"/>
|
||||
<field name="certificate_password" widget="password"/>
|
||||
<label name="certificate_facturae"/>
|
||||
<field name="certificate_facturae"/>
|
||||
</group>
|
||||
</form>
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
<label name="invoice_facturae"/>
|
||||
<field name="invoice_facturae"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='info']/field[@name='cancel_move']"
|
||||
position="after">
|
||||
<label name="invoice_facturae_sent"/>
|
||||
<field name="invoice_facturae_sent"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/group[@id='buttons']" position="inside">
|
||||
<button name="generate_facturae_wizard"
|
||||
string="Generate Factura-e" icon="tryton-lock"/>
|
||||
|
|
Loading…
Reference in New Issue