trytond-account_invoice_fac.../template_facturae_3.2.2.xml
Bernat Brunet 16f74f6507 allow to have more than one Facturae deffinition per party. Moving the
fields from party model to address. For the company move from party to
company model.

Update the way that the certificate to sign xml is used, based with the way
that the certificats are all load the same way.
2023-08-04 17:26:27 +02:00

396 lines
26 KiB
XML

{% from "template_facturae_macros.xml" import administrative_center, address, contact, registration_data %}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<namespace:Facturae xmlns:namespace="http://www.facturae.gob.es/formato/Versiones/Facturaev3_2_2.xml" xmlns:namespace2="http://uri.etsi.org/01903/v1.2.2#" xmlns:namespace3="http://www.w3.org/2000/09/xmldsig#">
<FileHeader>
<SchemaVersion>3.2.2</SchemaVersion>
<Modality>I</Modality>
{# The invoice is signed by Issuer (the company, only supported out invoices) #}
<InvoiceIssuerType>EM</InvoiceIssuerType>
{# As InvoiceIssuerType != TE, ThirdParty element is not generated #}
<Batch>
<BatchIdentifier>{{ ('%s%s' % (invoice.company.party.tax_identifier.code, invoice.number))[:70] }}</BatchIdentifier>
<InvoicesCount>1</InvoicesCount>
<TotalInvoicesAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</EquivalentInEuros>
{% endif %}
</TotalInvoicesAmount>
<TotalOutstandingAmount>
{# TODO: it must to get amount_to_pay? #}
<TotalAmount>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</EquivalentInEuros>
{% endif %}
</TotalOutstandingAmount>
<TotalExecutableAmount>
{# TODO: it must to get amount_to_pay? #}
<TotalAmount>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</EquivalentInEuros>
{% endif %}
</TotalExecutableAmount>
<InvoiceCurrencyCode>{{ invoice.currency.code.upper() }}</InvoiceCurrencyCode>
</Batch>
{# FactoryAssignmentData optional: not supported (factoring not supported) #}
</FileHeader>
<Parties>
<SellerParty>
<TaxIdentification>
<PersonTypeCode>{{ invoice.company.facturae_person_type }}</PersonTypeCode>
<ResidenceTypeCode>{{ invoice.company.facturae_residence_type }}</ResidenceTypeCode>
<TaxIdentificationNumber>{{ invoice.company.party.tax_identifier.code[:30] }}</TaxIdentificationNumber>
</TaxIdentification>
{# Optional. It could be the ID or the code #}
{% if invoice.company.party.code and invoice.company.party.code | length < 10 %}
<PartyIdentification>{{ invoice.company.party.code|int or invoice.company.party.id }}</PartyIdentification>
{% endif %}
{% if invoice.company.oficina_contable or invoice.company.organo_gestor or invoice.company.unidad_tramitadora or invoice.company.organo_proponente %}
<AdministrativeCentres>
{% 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 %}
</AdministrativeCentres>
{% endif %}
<LegalEntity>
<CorporateName>{{ invoice.company.party.name[:80] }}</CorporateName>
{% if invoice.company.party.trade_name %}
<TradeName>{{ invoice.company.party.trade_name[:40] }}</TradeName>
{% endif %}
{% if invoice.company %}
{{ registration_data(invoice.company) }}
{% endif %}
{% if invoice.company.party.addresses %}
{{ address(invoice.company.party.addresses[0]) }}
{% endif %}
{% if invoice.company.party.contact_mechanisms %}
{{ contact(invoice.company.party) }}
{% endif %}
</LegalEntity>
</SellerParty>
<BuyerParty>
<TaxIdentification>
<PersonTypeCode>{{ invoice.invoice_address.facturae_person_type }}</PersonTypeCode>
<ResidenceTypeCode>{{ invoice.invoice_address.facturae_residence_type }}</ResidenceTypeCode>
<TaxIdentificationNumber>{{ invoice.party.tax_identifier.code[:30] }}</TaxIdentificationNumber>
</TaxIdentification>
{# Optional. It could be the ID or the code #}
{% if invoice.party.code and invoice.party.code | length < 10 %}
<PartyIdentification>{{ invoice.party.code|int or invoice.party.id }}</PartyIdentification>
{% endif %}
{% if invoice.invoice_address.oficina_contable or invoice.invoice_address.organo_gestor or invoice.invoice_address.unidad_tramitadora or invoice.invoice_address.organo_proponente %}
<AdministrativeCentres>
{% 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 %}
</AdministrativeCentres>
{% endif %}
{% if invoice.invoice_address.facturae_person_type == 'J' %}
<LegalEntity>
<CorporateName>{{ invoice.party.name and invoice.party.name[:80] or invoice.party.code[:80] }}</CorporateName>
{% if invoice.party.trade_name %}
<TradeName>{{ invoice.party.trade_name[:40] }}</TradeName>
{% endif %}
{{ address(invoice.invoice_address) }}
{% if invoice.party.contact_mechanisms %}
{{ contact(invoice.party) }}
{% endif %}
</LegalEntity>
{% else %}
<Individual>
<Name>{{ invoice.party.name and invoice.party.name.split(' ', 2)[0][:40] or invoice.party.code[:40] }}</Name>
<FirstSurname>{{ invoice.party.name and invoice.party.name.split(' ', 2)[1][:40] }}</FirstSurname>
{% if invoice.party.name.split(' ') | length > 2 %}
<SecondSurname>{{ invoice.party.name and invoice.party.name.split(' ', 2)[2][:40] }}</SecondSurname>
{% endif %}
{{ address(invoice.invoice_address) }}
{% if invoice.party.contact_mechanisms %}
{{ contact(invoice.party) }}
{% endif %}
</Individual>
{% endif %}
</BuyerParty>
</Parties>
<Invoices>
<Invoice>
<InvoiceHeader>
<InvoiceNumber>{{ invoice.number[:20] }}</InvoiceNumber>
{# InvoiceSeriesCode optional: not supported #}
{# TODO: FA (Factura Simplificada) not supported #}
<InvoiceDocumentType>FC</InvoiceDocumentType>
{# TODO: Types not supported:
- OC (O. Recapitulativa)
- CO (Duplicado Original)
- CR, (D. Rectificativa)
- CC (D. Recapitulativa)
#}
<InvoiceClass>{{ 'OO' if not invoice.credited_invoices else 'OR' }}</InvoiceClass>
{% if invoice.credited_invoices %}
<Corrective>
<InvoiceNumber>{{ invoice.credited_invoices and invoice.credited_invoices[0].number[:20] }}</InvoiceNumber>
{# InvoiceSeriesCode Optional: not supported #}
<ReasonCode>{{ invoice.rectificative_reason_code }}</ReasonCode>
<ReasonDescription>{{ invoice.rectificative_reason_spanish_description }}</ReasonDescription>
<TaxPeriod>
{# TODO: it is the period or the term (VAT report period?) #}
<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:
- 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)
#}
<CorrectionMethod>01</CorrectionMethod>
<CorrectionMethodDescription>Rectificación íntegra</CorrectionMethodDescription>
{# AdditionalReasonDescription optional: not supported #}
{# TODO: InvoiceIssueDate: Nuevo campo para definir la fecha de expedición de la factura rectificada. Valor obligatorio en el supuesto de que la etiqueta CorrectionMethod tome los valores “01” o “02” #}
</Corrective>
{% endif %}
</InvoiceHeader>
<InvoiceIssueData>
<IssueDate>{{ invoice.invoice_date.isoformat() }}</IssueDate>
{# OperationDate required only if is different to IssueDate, but we consider OperatinDate==invoice_date: not supported #}
{# PlaceOfIssue optional: not supported #}
{# InvoicingPeriod required only for Recapitulativas or temporary service: not supported #}
<InvoiceCurrencyCode>{{ invoice.currency.code.upper() }}</InvoiceCurrencyCode>
{% if invoice.currency != euro %}
<ExchangeRateDetails>
<ExchangeRate>{{ Invoice.double_up_to_eight(exchange_rate) }}</ExchangeRate>
<ExchangeRateDate>{% if exchange_rate_date %}{{ exchange_rate_date.isoformat() }}{% endif %}</ExchangeRateDate>
</ExchangeRateDetails>
{% 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 %}
<Tax>
<TaxTypeCode>{{ invoice_tax.tax.report_type }}</TaxTypeCode>
<TaxRate>{{ Invoice.double_up_to_eight(invoice_tax.tax.rate * 100) }}</TaxRate>
<TaxableBase>
<TotalAmount>{{ Invoice.double_up_to_eight(invoice_tax.base) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Invoice.double_up_to_eight(invoice_tax.base) }}</EquivalentInEuros>
{% endif %}
</TaxableBase>
<TaxAmount>
<TotalAmount>{{ Invoice.double_up_to_eight(invoice_tax.amount) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }}</EquivalentInEuros>
{% endif %}
</TaxAmount>
{# TODO: special taxes not supported
- SpecialTaxableBase
- SpecialTaxAmount
#}
{% if invoice_tax.tax.recargo_equivalencia %}
{# TODO: EquivalenceSurchace must to have its own Tax entry or it must to go to the IVA line? TaxRate == EquivalenceSurcharge and TaxAmount == EquivalenceSurchargeAmount? #}
<EquivalenceSurcharge>{{ (invoice_tax.tax.rate * 100).quantize(Decimal('0.01')) }}</EquivalenceSurcharge>
<EquivalenceSurchargeAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }}</EquivalentInEuros>
{% endif %}
</EquivalenceSurchargeAmount>
{% endif %}
</Tax>
{% endfor %}
</TaxesOutputs>
{% if invoice.taxes_withheld %}
<TaxesWithheld>
{% for invoice_tax in invoice.taxes_withheld %}
<Tax>
<TaxTypeCode>{{ invoice_tax.tax.report_type }}</TaxTypeCode>
<TaxRate>{{ Invoice.double_up_to_eight(invoice_tax.tax.rate * 100) }}</TaxRate>
<TaxableBase>
<TotalAmount>{{ Currency.compute(invoice.currency, invoice_tax.base, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice_tax.base, euro) }}</EquivalentInEuros>
{% endif %}
</TaxableBase>
<TaxAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }}</EquivalentInEuros>
{% endif %}
</TaxAmount>
</Tax>
{% endfor %}
</TaxesWithheld>
{% endif %}
<InvoiceTotals>
<TotalGrossAmount>{{ Invoice.double_up_to_eight(invoice.untaxed_amount) }}</TotalGrossAmount>
{# TODO: GeneralDiscounts and TotalGeneralDiscounts (account_invoice_discount_global) not supported #}
{# TODO: GeneralSurcharges and TotalGeneralSurcharges not supported #}
<TotalGrossAmountBeforeTaxes>{{ Invoice.double_up_to_eight(invoice.untaxed_amount) }}</TotalGrossAmountBeforeTaxes>
<TotalTaxOutputs>{{ Invoice.double_up_to_eight(invoice.taxes_outputs | sum(attribute='amount', start=Decimal(0))) }}</TotalTaxOutputs>
<TotalTaxesWithheld>{{ Invoice.double_up_to_eight(invoice.taxes_withheld | sum(attribute='amount', start=Decimal(0))) }}</TotalTaxesWithheld>
<InvoiceTotal>{{ Invoice.double_up_to_eight(invoice.total_amount) }}</InvoiceTotal>
{# TODO: optional, not supported
- Subsidies
- PaymentsOnAccount, TotalPaymentsOnAccount
- ReimbursableExpenses, TotalReimbursableExpenses (suplidos?)
- TotalFinancialExpenses (account_payment_type_cost?)
- AmountsWithheld
#}
<TotalOutstandingAmount>{{ Invoice.double_up_to_eight(invoice.total_amount) }}</TotalOutstandingAmount>
<TotalExecutableAmount>{{ Invoice.double_up_to_eight(invoice.total_amount) }}</TotalExecutableAmount>
</InvoiceTotals>
<Items>
{% for line in invoice.lines if line.type == 'line' %}
<InvoiceLine>
{# TODO: optional, not supported
- Issuer/ReceiverContractReference, Issuer/ReceiverContractDate (contract)
- FileReference, FileDate
- SequenceNumber
- DeliveryNotesReferences (account_invoice_stock)
- TransactionDate
- Extensions
#}
<ReceiverTransactionReference>{{ line.facturae_receiver_transaction_reference }}</ReceiverTransactionReference>
<ItemDescription>{{ line.facturae_item_description[:2500] }}</ItemDescription>
<Quantity>{{ line.quantity }}</Quantity>
<UnitOfMeasure>{{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }}</UnitOfMeasure>
<UnitPriceWithoutTax>{{ Invoice.double_up_to_eight(line.unit_price) }}</UnitPriceWithoutTax>
<TotalCost>{{ Invoice.double_up_to_eight(line.amount) }}</TotalCost>
{# TODO: optional, not supported
- DiscountsAndRebates (account_invoice_discount)
- Charges
#}
<GrossAmount>{{ Invoice.double_up_to_eight(line.amount) }}</GrossAmount>
{% if line.taxes_withheld %}
<TaxesWithheld>
{% for line_tax in invoice.taxes_withheld %}
<Tax>
<TaxTypeCode>{{ line_tax.tax.report_type }}</TaxTypeCode>
<TaxRate>{{ Invoice.double_up_to_eight(line_tax.tax.rate * 100) }}</TaxRate>
<TaxableBase>
<TotalAmount>{{ Currency.compute(invoice.currency, line.amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, line.amount, euro) }}</EquivalentInEuros>
{% endif %}
</TaxableBase>
<TaxAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</EquivalentInEuros>
{% endif %}
</TaxAmount>
</Tax>
{% endfor %}
</TaxesWithheld>
{% endif %}
<TaxesOutputs>
{% for line_tax in line.taxes_outputs %}
<Tax>
<TaxTypeCode>{{ line_tax.tax.report_type }}</TaxTypeCode>
<TaxRate>{{ Currency.compute(invoice.currency, line_tax.tax.rate * 100, euro) }}</TaxRate>
<TaxableBase>
<TotalAmount>{{ Currency.compute(invoice.currency, line.amount, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, line.amount, euro) }}</EquivalentInEuros>
{% endif %}
</TaxableBase>
<TaxAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</EquivalentInEuros>
{% endif %}
</TaxAmount>
{# TODO: special taxes not supported
- SpecialTaxableBase
- SpecialTaxAmount
#}
{% if line_tax.tax.recargo_equivalencia %}
{# TODO: EquivalenceSurchace must to have its own Tax entry or it must to go to the IVA line? TaxRate == EquivalenceSurcharge and TaxAmount == EquivalenceSurchargeAmount? #}
<EquivalenceSurcharge>{{ (line_tax.tax.rate * 100).quantize(Decimal('0.01')) }}</EquivalenceSurcharge>
<EquivalenceSurchargeAmount>
<TotalAmount>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</TotalAmount>
{% if invoice.currency != euro %}
<EquivalentInEuros>{{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }}</EquivalentInEuros>
{% endif %}
</EquivalenceSurchargeAmount>
{% endif %}
</Tax>
{% endfor %}
</TaxesOutputs>
{% if line.facturae_start_date %}
<LineItemPeriod>
<StartDate>{{ line.facturae_start_date.isoformat() }}</StartDate>
<EndDate>{{ line.facturae_end_date.isoformat() }}</EndDate>
</LineItemPeriod>
{% endif %}
{% if line.taxes_additional_line_item_information or line.note %}
<AdditionalLineItemInformation>
{% if line.taxes_additional_line_item_information %}
{% for key, description in line.taxes_additional_line_item_information.items() %}
{{ key }} = {{ description }}
{% endfor %}
{% elif line.note %}
{{ line.note }}
{% endif %}
</AdditionalLineItemInformation>
{% endif %}
{# TODO: SpecialTaxableEvent not supported #}
{% if line.product and line.product.code %}
<ArticleCode>{{ line.facturae_article_code[:20] }}</ArticleCode>
{% endif %}
</InvoiceLine>
{% endfor %}
</Items>
{% if invoice.payment_details %}
<PaymentDetails>
{% for move_line in invoice.payment_details %}
<Installment>
<InstallmentDueDate>{{ move_line.maturity_date.isoformat() }}</InstallmentDueDate>
<InstallmentAmount>{{ Currency.compute(invoice.currency, ((move_line.debit - move_line.credit) | abs).quantize(Decimal('0.01')), euro) }}</InstallmentAmount>
<PaymentMeans>{{ move_line.payment_type.facturae_type }}</PaymentMeans>
{% if move_line.payment_type.facturae_type == '04' %}
<AccountToBeCredited>
<IBAN>{% for number in (move_line.bank_account.numbers|selectattr('type', 'equalto', 'iban')) %}{% if loop.first %}{{ number.number_compact }}{% endif %}{% endfor %}</IBAN>
{# Unnecessary if IBAN is supplied: AccountNumber, BankCode, BranchCode, BranchInSpainAddress, OverseasBranchAddress, BIC #}
</AccountToBeCredited>
{% elif move_line.payment_type.facturae_type == '02' %}
<AccountToBeDebited>
<IBAN>{% for number in (move_line.bank_account.numbers|selectattr('type', 'equalto', 'iban')) %}{% if loop.first %}{{ number.number_compact }}{% endif %}{% endfor %}</IBAN>
{# Unnecessary if IBAN is supplied: AccountNumber, BankCode, BranchCode, BranchInSpainAddress, OverseasBranchAddress, BIC #}
</AccountToBeDebited>
{# optional, not supported:
- PaymentReconciliationReference[:60]
- CollectionAdditionalInformation[:2500]
- RegulatoryReportingData (for international operations)
- DebitReconciliationReference[:60]
#}
{% endif %}
</Installment>
{% endfor %}
</PaymentDetails>
{% endif %}
<LegalLiterals>
{% for inv_tax in invoice.taxes %}
{% if inv_tax.tax and inv_tax.tax.report_description %}
<LegalReference>{{ inv_tax.tax.report_description[:250] }}</LegalReference>
{% endif %}
{% endfor %}
</LegalLiterals>
<AdditionalData>
{# optional, not supported:
- RelatedInvoice[:40]
- RelatedDocuments
- Extensions
#}
<InvoiceAdditionalInformation> # [:2500]
Factura generada con Tryton (https://www.tryton.org)
</InvoiceAdditionalInformation>
</AdditionalData>
</Invoice>
</Invoices>
{# Extensions optional, not supported #}
</namespace:Facturae>