diff --git a/__init__.py b/__init__.py index 85e7df6..415abe4 100644 --- a/__init__.py +++ b/__init__.py @@ -1,10 +1,7 @@ # 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 account -from . import invoice -from . import party -from . import payment_type +from . import account, invoice, party, company, payment_type from .invoice import FACTURAE_SCHEMA_VERSION __all__ = [FACTURAE_SCHEMA_VERSION] @@ -20,8 +17,9 @@ def register(): invoice.InvoiceLine, invoice.CreditInvoiceStart, invoice.GenerateFacturaeStart, - party.Party, + party.Address, payment_type.PaymentType, + company.Company, module='account_invoice_facturae', type_='model') Pool.register( invoice.CreditInvoice, diff --git a/account.py b/account.py index a0698e4..d529ca3 100644 --- a/account.py +++ b/account.py @@ -77,7 +77,7 @@ class ConfigurationFacturae(ModelSQL, CompanyValueMixin): facturae_certificate = fields.Many2One('certificate', "Factura-e Certificate", help='Certificate to sign Factura-e') facturae_service = fields.Selection([ - (None, ''), + (None, 'Only Generate Facturae'), ], "Factura-e Service") @staticmethod diff --git a/company.py b/company.py new file mode 100644 index 0000000..4964f3c --- /dev/null +++ b/company.py @@ -0,0 +1,24 @@ +# 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 + + +class Company(metaclass=PoolMeta): + __name__ = 'company.company' + + facturae_person_type = fields.Selection([ + (None, ''), + ('J', 'Legal Entity'), + ('F', 'Individual'), + ], 'Person Type', sort=False) + facturae_residence_type = fields.Selection([ + (None, ''), + ('R', 'Resident in Spain'), + ('U', 'Resident in other EU country'), + ('E', 'Foreigner'), + ], 'Residence Type', sort=False) + oficina_contable = fields.Char('Oficina contable') + organo_gestor = fields.Char('Organo gestor') + unidad_tramitadora = fields.Char('Unidad tramitadora') + organo_proponente = fields.Char('Organo proponente') diff --git a/company.xml b/company.xml new file mode 100644 index 0000000..89ab3fa --- /dev/null +++ b/company.xml @@ -0,0 +1,12 @@ + + + + + + company.company + + company_form + + + diff --git a/invoice.py b/invoice.py index bd9ff57..762e94a 100644 --- a/invoice.py +++ b/invoice.py @@ -14,9 +14,6 @@ from decimal import Decimal from jinja2 import Environment, FileSystemLoader from lxml import etree from operator import attrgetter -from tempfile import NamedTemporaryFile -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import pkcs12 from trytond.model import ModelView, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval @@ -25,6 +22,8 @@ from trytond.wizard import Wizard, StateView, StateTransition, Button from trytond import backend from trytond.i18n import gettext from trytond.exceptions import UserError +from trytond.modules.certificate_manager.certificate_manager import ( + ENCODING_DER) FACTURAE_SCHEMA_VERSION = '3.2.2' @@ -319,13 +318,13 @@ class Invoice(metaclass=PoolMeta): "Missing some tax in invoice %s" % self.id) for field in FACe_REQUIRED_FIELDS: - for party in [self.party, self.company.party]: - if not getattr(party, field): - raise UserError(gettext( - 'account_invoice_facturae.party_facturae_fields', - party=party.rec_name, - invoice=self.rec_name, - field=field)) + if (not getattr(self.invoice_address, field) + or not getattr(self.company, field)): + raise UserError(gettext( + 'account_invoice_facturae.party_facturae_fields', + party=party.rec_name, + invoice=self.rec_name, + field=field)) if (not self.company.party.tax_identifier or len(self.company.party.tax_identifier.code) < 3 or len(self.company.party.tax_identifier.code) > 30): @@ -351,7 +350,7 @@ class Invoice(metaclass=PoolMeta): 'account_invoice_facturae.party_vat_identifier', party=self.party.rec_name, invoice=self.rec_name)) - if (self.party.facturae_person_type == 'F' + if (self.invoice_address.facturae_person_type == 'F' and len(self.party.name.split(' ', 2)) < 2): raise UserError(gettext( 'account_invoice_facturae.party_name_surname', @@ -475,7 +474,8 @@ class Invoice(metaclass=PoolMeta): """ Inspired by https://github.com/pedrobaeza/l10n-spain/blob/d01d049934db55130471e284012be7c860d987eb/l10n_es_facturae/wizard/create_facturae.py """ - Configuration = Pool().get('account.configuration') + pool = Pool() + Configuration = pool.get('account.configuration') if not certificate: certificate = Configuration(1).facturae_certificate @@ -483,34 +483,14 @@ class Invoice(metaclass=PoolMeta): 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') - with NamedTemporaryFile(suffix='.xml', delete=False) as unsigned_file: - unsigned_file.write(xml_string) - - 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 - 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(gettext( - 'account_invoice_facturae.msg_certificate_error', - error=str(e))) + def _sign_file(cert, request): + key = cert.load_pem_key() + pem = cert.load_pem_certificate() # DER is an ASN.1 encoding type - crt = certificate.public_bytes(serialization.Encoding.DER) + crt = pem.public_bytes(ENCODING_DER) # Set variables values rand_min = 1 @@ -574,10 +554,9 @@ class Invoice(metaclass=PoolMeta): # Set the certificate values ctx = xmlsig.SignatureContext() - ctx.private_key = private_key - ctx.x509 = certificate - ctx.ca_certificates = additional_certificates - ctx.public_key = certificate.public_key() + ctx.private_key = key + ctx.x509 = pem + ctx.public_key = pem.public_key() # Set the footer validation object_node = etree.SubElement( @@ -632,11 +611,11 @@ class Invoice(metaclass=PoolMeta): etree.SubElement( issuer_serial, etree.QName(xmlsig.constants.DSigNs, "X509IssuerName") - ).text = xmlsig.utils.get_rdns_name(certificate.issuer.rdns) + ).text = xmlsig.utils.get_rdns_name(pem.issuer.rdns) etree.SubElement( issuer_serial, etree.QName(xmlsig.constants.DSigNs, "X509SerialNumber") - ).text = str(certificate.serial_number) + ).text = str(pem.serial_number) signature_policy_identifier = etree.SubElement( signed_signature_properties, @@ -710,11 +689,7 @@ class Invoice(metaclass=PoolMeta): return etree.tostring(root, xml_declaration=True, encoding="UTF-8") - signed_file_content = _sign_file( - certificate_facturae, - certificate_password.encode(), - xml_string, - ) + signed_file_content = _sign_file(certificate, xml_string) logger.info("Factura-e for invoice %s (%s) generated and signed", self.rec_name, self.id) @@ -750,8 +725,8 @@ class InvoiceLine(metaclass=PoolMeta): @property def facturae_item_description(self): return ( - (self.description and self.description[:2500]) - or (self.product and self.product.rec_name[:2500]) + (self.description and self.description) + or (self.product and self.product.rec_name) or '#'+str(self.id) ) @@ -760,6 +735,34 @@ class InvoiceLine(metaclass=PoolMeta): # TODO Issuer/ReceiverTransactionDate (sale, contract...) return '' + @property + def facturae_start_date(self): + try: + pool = Pool() + Consumption = pool.get('contract.consumption') + except: + Consumption = None + + if (self.origin and Consumption + and isinstance(self.origin, Consumption)): + return self.origin.start_date + return None + + @property + def facturae_end_date(self): + try: + pool = Pool() + Consumption = pool.get('contract.consumption') + except: + Consumption = None + + if (self.origin and Consumption + and isinstance(self.origin, Consumption)): + return self.origin.end_date + elif self.facturae_start_date: + return self.facturae_start_date + return None + @property def taxes_outputs(self): """Return list of 'impuestos repecutidos'""" @@ -813,17 +816,10 @@ class GenerateFacturaeStart(ModelView): 'Generate Factura-e file - Start' __name__ = 'account.invoice.generate_facturae.start' service = fields.Selection([ - (None, ''), - ], 'Service') - certificate_facturae = fields.Many2One('certificate', - 'Certificate Factura-e', - states={ - 'invisible': ~Bool(Eval('service')), - }, depends=['service']) - - @staticmethod - def default_service(): - return None + (None, 'Only generate facturae'), + ], 'Factura-e Service') + certificate = fields.Many2One('certificate', + 'Factura-e Certificate', depends=['service']) class GenerateFacturae(Wizard): @@ -836,11 +832,26 @@ class GenerateFacturae(Wizard): ]) generate = StateTransition() + def default_start(self, fields): + pool = Pool() + Configuration = pool.get('account.configuration') + + default = { + 'service': None, + 'certificate': None, + } + + config = Configuration(1) + if config.facturae_certificate: + default['certificate'] = config.facturae_certificate.id + + return default + def transition_generate(self): Invoice = Pool().get('account.invoice') invoices = Invoice.browse(Transaction().context['active_ids']) for invoice in invoices: - invoice.generate_facturae(certificate=self.start.certificate_facturae, + invoice.generate_facturae(certificate=self.start.certificate, service=self.start.service) return 'end' diff --git a/locale/ca.po b/locale/ca.po index 8423479..a941c7d 100644 --- a/locale/ca.po +++ b/locale/ca.po @@ -38,17 +38,25 @@ 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,service:" +msgid "Factura-e Service" +msgstr "Servei Factura-e" + +msgctxt "field:account.invoice.generate_facturae.start,certificate:" +msgid "Factura-e Certificate" +msgstr "Certificat Factura-e" msgctxt "field:account.invoice.generate_facturae.start,certificate_password:" msgid "Certificate Password" msgstr "Contrasenya certificat" -msgctxt "field:account.invoice.generate_facturae.start,service:" -msgid "Service" -msgstr "Servei" +msgctxt "field:account.configuration,facturae_certificate:" +msgid "Factura-e Certificate" +msgstr "Certificat Factura-e" + +msgctxt "field:account.configuration,facturae_service:" +msgid "Factura-e Service" +msgstr "Servei Factura-e" msgctxt "field:account.payment.type,facturae_type:" msgid "Factura-e Type" @@ -58,31 +66,55 @@ msgctxt "field:account.tax,report_type:" msgid "Report Type" msgstr "Tipus informe" +msgctxt "field:company.company,facturae_person_type:" +msgid "Person Type" +msgstr "Tipus persona" + +msgctxt "field:company.company,facturae_residence_type:" +msgid "Residence Type" +msgstr "Tipus residència" + +msgctxt "field:company.company,oficina_contable:" +msgid "Oficina contable" +msgstr "Oficina contable" + +msgctxt "field:company.company,organo_gestor:" +msgid "Organo gestor" +msgstr "Organo gestor" + +msgctxt "field:company.company,organo_proponente:" +msgid "Organo proponente" +msgstr "Organo proponente" + +msgctxt "field:company.company,unidad_tramitadora:" +msgid "Unidad tramitadora" +msgstr "Unidad tramitadora" + msgctxt "field:account.tax.template,report_type:" msgid "Report Type" msgstr "Tipus informe" -msgctxt "field:party.party,facturae_person_type:" +msgctxt "field:party.address,facturae_person_type:" msgid "Person Type" msgstr "Tipus persona" -msgctxt "field:party.party,facturae_residence_type:" +msgctxt "field:party.address,facturae_residence_type:" msgid "Residence Type" msgstr "Tipus residència" -msgctxt "field:party.party,oficina_contable:" +msgctxt "field:party.address,oficina_contable:" msgid "Oficina contable" msgstr "Oficina contable" -msgctxt "field:party.party,organo_gestor:" +msgctxt "field:party.address,organo_gestor:" msgid "Organo gestor" msgstr "Organo gestor" -msgctxt "field:party.party,organo_proponente:" +msgctxt "field:party.address,organo_proponente:" msgid "Organo proponente" msgstr "Organo proponente" -msgctxt "field:party.party,unidad_tramitadora:" +msgctxt "field:party.address,unidad_tramitadora:" msgid "Unidad tramitadora" msgstr "Unidad tramitadora" @@ -753,31 +785,43 @@ msgctxt "selection:account.tax.template,report_type:" msgid "Value-Added Tax" msgstr "" -msgctxt "selection:party.party,facturae_person_type:" -msgid "" -msgstr " " - -msgctxt "selection:party.party,facturae_person_type:" +msgctxt "selection:company.company,facturae_person_type:" msgid "Individual" msgstr "Persona física" -msgctxt "selection:party.party,facturae_person_type:" +msgctxt "selection:company.company,facturae_person_type:" msgid "Legal Entity" msgstr "Persona jurídica" -msgctxt "selection:party.party,facturae_residence_type:" -msgid "" -msgstr " " - -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" msgid "Foreigner" msgstr "Estranger (Fora Unió Europea)" -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" msgid "Resident in Spain" msgstr "Resident (a Espanya)" -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" +msgid "Resident in other EU country" +msgstr "Resident a la Unió Europea (excepte Espanya)" + +msgctxt "selection:party.address,facturae_person_type:" +msgid "Individual" +msgstr "Persona física" + +msgctxt "selection:party.address,facturae_person_type:" +msgid "Legal Entity" +msgstr "Persona jurídica" + +msgctxt "selection:party.address,facturae_residence_type:" +msgid "Foreigner" +msgstr "Estranger (Fora Unió Europea)" + +msgctxt "selection:party.address,facturae_residence_type:" +msgid "Resident in Spain" +msgstr "Resident (a Espanya)" + +msgctxt "selection:party.address,facturae_residence_type:" msgid "Resident in other EU country" msgstr "Resident a la Unió Europea (excepte Espanya)" @@ -807,7 +851,11 @@ msgctxt "view:account.tax:" msgid "Factura-e" msgstr "Factura-e" -msgctxt "view:party.party:" +msgctxt "view:company.company:" +msgid "Factura-e" +msgstr "Factura-e" + +msgctxt "view:party.address:" msgid "Factura-e" msgstr "Factura-e" @@ -818,3 +866,11 @@ msgstr "Cancel·la" msgctxt "wizard_button:account.invoice.generate_facturae,start,generate:" msgid "Generate" msgstr "Genera" + +msgctxt "selection:account.configuration.facturae,facturae_service:" +msgid "Only Generate Facturae" +msgstr "Només genera la Factura-e" + +msgctxt "selection:account.invoice.generate_facturae.start,service:" +msgid "Only Generate Facturae" +msgstr "Només genera la Factura-e" diff --git a/locale/es.po b/locale/es.po index 29f743b..6f70587 100644 --- a/locale/es.po +++ b/locale/es.po @@ -38,17 +38,25 @@ 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" msgctxt "field:account.invoice.generate_facturae.start,service:" -msgid "Service" -msgstr "Servicio" +msgid "Factura-e Service" +msgstr "Servicio Factura-e" + +msgctxt "field:account.invoice.generate_facturae.start,certificate:" +msgid "Factura-e Certificate" +msgstr "Certificado Factura-e" + +msgctxt "field:account.configuration,facturae_certificate:" +msgid "Factura-e Certificate" +msgstr "Certificado Factura-e" + +msgctxt "field:account.configuration,facturae_service:" +msgid "Factura-e Service" +msgstr "Servicio Factura-e" msgctxt "field:account.payment.type,facturae_type:" msgid "Factura-e Type" @@ -62,27 +70,51 @@ msgctxt "field:account.tax.template,report_type:" msgid "Report Type" msgstr "Tipo informe" -msgctxt "field:party.party,facturae_person_type:" +msgctxt "field:company.company,facturae_person_type:" msgid "Person Type" msgstr "Tipo persona" -msgctxt "field:party.party,facturae_residence_type:" +msgctxt "field:company.company,facturae_residence_type:" msgid "Residence Type" msgstr "Tipo residencia" -msgctxt "field:party.party,oficina_contable:" +msgctxt "field:company.company,oficina_contable:" msgid "Oficina contable" msgstr "Oficina contable" -msgctxt "field:party.party,organo_gestor:" +msgctxt "field:company.company,organo_gestor:" msgid "Organo gestor" msgstr "Organo gestor" -msgctxt "field:party.party,organo_proponente:" +msgctxt "field:company.company,organo_proponente:" msgid "Organo proponente" msgstr "Organo proponente" -msgctxt "field:party.party,unidad_tramitadora:" +msgctxt "field:company.company,unidad_tramitadora:" +msgid "Unidad tramitadora" +msgstr "Unidad tramitadora" + +msgctxt "field:party.address,facturae_person_type:" +msgid "Person Type" +msgstr "Tipo persona" + +msgctxt "field:party.address,facturae_residence_type:" +msgid "Residence Type" +msgstr "Tipo residencia" + +msgctxt "field:party.address,oficina_contable:" +msgid "Oficina contable" +msgstr "Oficina contable" + +msgctxt "field:party.address,organo_gestor:" +msgid "Organo gestor" +msgstr "Organo gestor" + +msgctxt "field:party.address,organo_proponente:" +msgid "Organo proponente" +msgstr "Organo proponente" + +msgctxt "field:party.address,unidad_tramitadora:" msgid "Unidad tramitadora" msgstr "Unidad tramitadora" @@ -751,23 +783,43 @@ msgctxt "selection:account.tax.template,report_type:" msgid "Value-Added Tax" msgstr "" -msgctxt "selection:party.party,facturae_person_type:" +msgctxt "selection:company.company,facturae_person_type:" msgid "Individual" msgstr "Persona física" -msgctxt "selection:party.party,facturae_person_type:" +msgctxt "selection:company.company,facturae_person_type:" msgid "Legal Entity" msgstr "Persona jurídica" -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" msgid "Foreigner" msgstr "Extranjero (Fuera Unión Europea)" -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" msgid "Resident in Spain" msgstr "Residente (en España)" -msgctxt "selection:party.party,facturae_residence_type:" +msgctxt "selection:company.company,facturae_residence_type:" +msgid "Resident in other EU country" +msgstr "Residente en la Unión Europea (excepto España)" + +msgctxt "selection:party.address,facturae_person_type:" +msgid "Individual" +msgstr "Persona física" + +msgctxt "selection:party.address,facturae_person_type:" +msgid "Legal Entity" +msgstr "Persona jurídica" + +msgctxt "selection:party.address,facturae_residence_type:" +msgid "Foreigner" +msgstr "Extranjero (Fuera Unión Europea)" + +msgctxt "selection:party.address,facturae_residence_type:" +msgid "Resident in Spain" +msgstr "Residente (en España)" + +msgctxt "selection:party.address,facturae_residence_type:" msgid "Resident in other EU country" msgstr "Residente en la Unión Europea (excepto España)" @@ -793,7 +845,11 @@ msgctxt "view:account.tax:" msgid "Factura-e" msgstr "Factura-e" -msgctxt "view:party.party:" +msgctxt "view:company.company:" +msgid "Factura-e" +msgstr "Factura-e" + +msgctxt "view:party.address:" msgid "Factura-e" msgstr "Factura-e" @@ -804,3 +860,11 @@ msgstr "Cancelar" msgctxt "wizard_button:account.invoice.generate_facturae,start,generate:" msgid "Generate" msgstr "Generar" + +msgctxt "selection:account.configuration.facturae,service:" +msgid "Only Generate Facturae" +msgstr "Solo genera la Factura-e" + +msgctxt "selection:account.invoice.generate_facturae.start,service:" +msgid "Only Generate Facturae" +msgstr "Solo genera la Factura-e" diff --git a/party.py b/party.py index 40a974a..3c9d024 100644 --- a/party.py +++ b/party.py @@ -1,13 +1,13 @@ # 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__ = ['Party'] +from trytond.pool import Pool, PoolMeta +from trytond.transaction import Transaction -class Party(metaclass=PoolMeta): - __name__ = 'party.party' +class Address(metaclass=PoolMeta): + __name__ = 'party.address' + facturae_person_type = fields.Selection([ (None, ''), ('J', 'Legal Entity'), @@ -23,3 +23,80 @@ class Party(metaclass=PoolMeta): organo_gestor = fields.Char('Organo gestor') unidad_tramitadora = fields.Char('Unidad tramitadora') organo_proponente = fields.Char('Organo proponente') + + @classmethod + def __register__(cls, module_name): + pool = Pool() + Party = pool.get('party.party') + address_table = cls.__table__() + party_table = Party.__table__() + party = Party.__table_handler__() + cursor = Transaction().connection.cursor() + + super().__register__(module_name) + + # Migrat facturae fields from party to invoice addresses or in case of + # missing the fist address. + if party.column_exist('facturae_person_type'): + cursor.execute(*party_table.select( + party_table.id, + where=party_table.facturae_person_type != None)) + addresses_update = [] + for party_id in cursor.fetchall(): + cursor.execute(*address_table.select( + address_table.id, address_table.invoice, + where=address_table.party.in_(party_id))) + addresses = cursor.fetchall() + addresses = {a[0]: a[1] for a in addresses} + if not addresses: + continue + if len(addresses) == 1: + addresses_update.append(next(iter(addresses))) + else: + for address, invoice in addresses.items(): + if invoice: + addresses_update.append(address) + if not any(addresses.values()): + addresses_update.extend(addresses) + + for address_update in addresses_update: + cursor.execute(*address_table.select( + address_table.party, + where=address_table.id == address_update)) + party_id = cursor.fetchone() + + cursor.execute(*party_table.select( + party_table.facturae_person_type, + party_table.facturae_residence_type, + party_table.oficina_contable, + party_table.organo_gestor, + party_table.unidad_tramitadora, + party_table.organo_proponente, + where=party_table.id == party_id)) + party_values = cursor.fetchone() + + cursor.execute(*address_table.update( + columns=[ + address_table.facturae_person_type, + address_table.facturae_residence_type, + address_table.oficina_contable, + address_table.organo_gestor, + address_table.unidad_tramitadora, + address_table.organo_proponente + ], + values=[ + party_values[0], + party_values[1], + party_values[2], + party_values[3], + party_values[4], + party_values[5], + ], + where=address_table.id == address_update)) + + party.drop_column('facturae_person_type') + party.drop_column('facturae_residence_type') + party.drop_column('oficina_contable') + party.drop_column('organo_gestor') + party.drop_column('unidad_tramitadora') + party.drop_column('organo_proponente') diff --git a/party.xml b/party.xml index ddc1806..05dbf51 100644 --- a/party.xml +++ b/party.xml @@ -3,10 +3,10 @@ copyright notices and license terms. --> - - party.party - - party_form + + party.address + + address_form diff --git a/template_facturae_3.2.2.xml b/template_facturae_3.2.2.xml index 2570764..1b24574 100644 --- a/template_facturae_3.2.2.xml +++ b/template_facturae_3.2.2.xml @@ -38,20 +38,20 @@ - {{ invoice.company.party.facturae_person_type }} - {{ invoice.company.party.facturae_residence_type }} + {{ invoice.company.facturae_person_type }} + {{ invoice.company.facturae_residence_type }} {{ invoice.company.party.tax_identifier.code[:30] }} {# Optional. It could be the ID or the code #} {% if invoice.company.party.code and invoice.company.party.code | length < 10 %} {{ invoice.company.party.code|int or invoice.company.party.id }} {% endif %} - {% if invoice.company.party.oficina_contable or invoice.company.party.organo_gestor or invoice.company.party.unidad_tramitadora or invoice.company.party.organo_proponente %} + {% if invoice.company.oficina_contable or invoice.company.organo_gestor or invoice.company.unidad_tramitadora or invoice.company.organo_proponente %} - {% if invoice.company.party.oficina_contable %}{{ administrative_center(invoice.company.party.oficina_contable, '01', invoice.company.party) }}{% endif %} - {% if invoice.company.party.organo_gestor %}{{ administrative_center(invoice.company.party.organo_gestor, '02', invoice.company.party) }}{% endif %} - {% if invoice.company.party.unidad_tramitadora %}{{ administrative_center(invoice.company.party.unidad_tramitadora, '03', invoice.company.party) }}{% endif %} - {% if invoice.company.party.organo_proponente %}{{ administrative_center(invoice.company.party.organo_proponente, '04', invoice.company.party) }}{% endif %} + {% if invoice.company.oficina_contable %}{{ administrative_center(invoice.company.oficina_contable, '01', invoice.company.facturae_person_type, invoice.company.party.address_get('invoice')) }}{% endif %} + {% if invoice.company.organo_gestor %}{{ administrative_center(invoice.company.organo_gestor, '02', invoice.company.facturae_person_type, invoice.company.party.address_get('invoice')) }}{% endif %} + {% if invoice.company.unidad_tramitadora %}{{ administrative_center(invoice.company.unidad_tramitadora, '03', invoice.company.facturae_person_type, invoice.company.party.address_get('invoice')) }}{% endif %} + {% if invoice.company.organo_proponente %}{{ administrative_center(invoice.company.organo_proponente, '04', invoice.company.facturae_person_type, invoice.company.party.address_get('invoice')) }}{% endif %} {% endif %} @@ -73,23 +73,23 @@ - {{ invoice.party.facturae_person_type }} - {{ invoice.party.facturae_residence_type }} + {{ invoice.invoice_address.facturae_person_type }} + {{ invoice.invoice_address.facturae_residence_type }} {{ invoice.party.tax_identifier.code[:30] }} {# Optional. It could be the ID or the code #} {% if invoice.party.code and invoice.party.code | length < 10 %} {{ invoice.party.code|int or invoice.party.id }} {% endif %} - {% if invoice.party.oficina_contable or invoice.party.organo_gestor or invoice.party.unidad_tramitadora or invoice.party.organo_proponente %} + {% if invoice.invoice_address.oficina_contable or invoice.invoice_address.organo_gestor or invoice.invoice_address.unidad_tramitadora or invoice.invoice_address.organo_proponente %} - {% if invoice.party.oficina_contable %}{{ administrative_center(invoice.party.oficina_contable, '01', invoice.party) }}{% endif %} - {% if invoice.party.organo_gestor %}{{ administrative_center(invoice.party.organo_gestor, '02', invoice.party) }}{% endif %} - {% if invoice.party.unidad_tramitadora %}{{ administrative_center(invoice.party.unidad_tramitadora, '03', invoice.party) }}{% endif %} - {% if invoice.party.organo_proponente %}{{ administrative_center(invoice.party.organo_proponente, '04', invoice.party) }}{% endif %} + {% if invoice.invoice_address.oficina_contable %}{{ administrative_center(invoice.invoice_address.oficina_contable, '01', invoice.invoice_address.facturae_person_type, invoice.invoice_address) }}{% endif %} + {% if invoice.invoice_address.organo_gestor %}{{ administrative_center(invoice.invoice_address.organo_gestor, '02', invoice.invoice_address.facturae_person_type, invoice.invoice_address) }}{% endif %} + {% if invoice.invoice_address.unidad_tramitadora %}{{ administrative_center(invoice.invoice_address.unidad_tramitadora, '03', invoice.invoice_address.facturae_person_type, invoice.invoice_address) }}{% endif %} + {% if invoice.invoice_address.organo_proponente %}{{ administrative_center(invoice.invoice_address.organo_proponente, '04', invoice.invoice_address.facturae_person_type, invoice.invoice_address) }}{% endif %} {% endif %} - {% if invoice.party.facturae_person_type == 'J' %} + {% if invoice.invoice_address.facturae_person_type == 'J' %} {{ invoice.party.name and invoice.party.name[:80] or invoice.party.code[:80] }} {% if invoice.party.trade_name %} @@ -162,7 +162,7 @@ {% if invoice.currency != euro %} {{ Invoice.double_up_to_eight(exchange_rate) }} - {{ exchange_rate_date }} + {% if exchange_rate_date %}{{ exchange_rate_date.isoformat() }}{% endif %} {% endif %} EUR @@ -255,7 +255,7 @@ - Extensions #} {{ line.facturae_receiver_transaction_reference }} - {{ line.facturae_item_description }} + {{ line.facturae_item_description[:2500] }} {{ line.quantity }} {{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }} {{ Invoice.double_up_to_eight(line.unit_price) }} @@ -321,10 +321,10 @@ {% endfor %} - {% if line.origin and line.origin.contract %} + {% if line.facturae_start_date %} - {{ line.origin.start_date }} - {{ line.origin.end_date }} + {{ line.facturae_start_date.isoformat() }} + {{ line.facturae_end_date.isoformat() }} {% endif %} {% if line.taxes_additional_line_item_information or line.note %} diff --git a/template_facturae_macros.xml b/template_facturae_macros.xml index fa9964c..341d557 100644 --- a/template_facturae_macros.xml +++ b/template_facturae_macros.xml @@ -1,19 +1,19 @@ -{% macro administrative_center(centre_code, role_type_code, party) %} +{% macro administrative_center(centre_code, role_type_code, person_type, addrs) %} {{ centre_code }} {{ role_type_code }} - {% if party.facturae_person_type == 'J' %}{{ party.name and party.name[:40] }}{% else %}{{ party.name and party.name.split(' ', 2)[0][:40] or party.code[:40] }}{% endif %} - {% if party.facturae_person_type == 'F' %} - {{ party.name and party.name.split(' ', 2)[1][:40] }} + {% if person_type == 'J' %}{{ addrs.party.name and addrs.party.name[:40] }}{% else %}{{ addrs.party.name and addrs.party.name.split(' ', 2)[0][:40] or addrs.party.code[:40] }}{% endif %} + {% if person_type == 'F' %} + {{ addrs.party.name and addrs.party.name.split(' ', 2)[1][:40] }} {% endif %} - {% if party.facturae_person_type == 'F' and party.name.split(' ') | length > 2 %} - {{ party.name and party.name.split(' ', 2)[2][:40] }} + {% if person_type == 'F' and addrs.party.name.split(' ') | length > 2 %} + {{ addrs.party.name and addrs.party.name.split(' ', 2)[2][:40] }} {% endif %} - {% if party.addresses %}{{ address(party.addresses[0]) }}{% endif %} - {% if party.contact_mechanisms %}{{ contact(party) }}{% endif %} + {% if addrs %}{{ address(addrs) }}{% endif %} + {% if addrs.party.contact_mechanisms %}{{ contact(addrs.party) }}{% endif %} - {{ party.name and party.name.split(' ', 2)[0][:40] or party.code[:40] }} + {{ addrs.party.name and addrs.party.name.split(' ', 2)[0][:40] or addrs.party.code[:40] }} {% endmacro %} diff --git a/tests/test_module.py b/tests/test_module.py index fd85cf7..54f530a 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -58,8 +58,8 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase): company.header = 'Report Header' company.party.name = 'Seller' company.party.identifiers = [tax_identifier] - company.party.facturae_person_type = 'J' - company.party.facturae_residence_type = 'R' + company.facturae_person_type = 'J' + company.facturae_residence_type = 'R' company.party.save() company.save() @@ -108,8 +108,6 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase): company_address.country = country company_address.save() party = Party(name='Buyer') - party.facturae_person_type = 'J' - party.facturae_residence_type = 'R' tax_identifier = PartyIdentifier() tax_identifier.type = 'eu_vat' tax_identifier.code = 'BE0897290877' @@ -123,6 +121,8 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase): 'postal_code': '08201', 'subdivision': subdivision.id, 'country': country.id, + 'address.facturae_person_type': 'J', + 'address.facturae_residence_type': 'R', } address, = Address.create([address_dict]) diff --git a/tryton.cfg b/tryton.cfg index c229770..fca33e2 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -8,10 +8,12 @@ depends: extras_depend: account_bank account_es + contract xml: account.xml + account_es.xml + company.xml invoice.xml party.xml payment_type.xml - account_es.xml message.xml diff --git a/view/party_form.xml b/view/address_form.xml similarity index 91% rename from view/party_form.xml rename to view/address_form.xml index 05ac85b..0f2fab7 100644 --- a/view/party_form.xml +++ b/view/address_form.xml @@ -2,7 +2,7 @@ - +