[FIX] account_facturx: make factur-x xml compliant
The xml we generate for factur-x is no longer compliant with all
the latest standards. With these changes, we are providing the tools
to make it work once again and make sure it is validated by the
factur-x and zugferd validators in all aspect (PDFA/3, XMP, XML)
It should also be valid to be sent to Chorus pro if applicable.
This adds a mapping to the UNECE unit of measure codes for uom, that
are required for factur-x.
It will also calculate a category for each taxes as such:
- If the tax is an export from EU to outside EU, G.
- If the supplier and customer are both in EU but different country,
K
- If both are in EU, in the same country but the tax amount is 0, E
- Otherwise, it will be S.
See https://unece.org/fileadmin/DAM/trade/untdid/d16b/tred/tred5305.htm
opw-2714544
closes odoo/odoo#90105
X-original-commit: e1bfb363bccd7b84f660d1e6c5b1e4d4fe02d398
Signed-off-by: Laurent Smet <las@odoo.com>
This commit is contained in:
@@ -8,3 +8,4 @@ from . import account_payment
|
||||
from . import ir_actions_report
|
||||
from . import mail_template
|
||||
from . import ir_attachment
|
||||
from . import uom
|
||||
|
||||
@@ -159,6 +159,7 @@ class AccountMove(models.Model):
|
||||
tax_details['base_amount_currency'] += tax_values['base_amount_currency']
|
||||
tax_details['tax_amount'] += tax_values['tax_amount']
|
||||
tax_details['tax_amount_currency'] += tax_values['tax_amount_currency']
|
||||
tax_details['exemption_reason'] = tax_values['tax_id'].name
|
||||
tax_details['group_tax_details'].append(tax_values)
|
||||
|
||||
def _prepare_edi_tax_details(self, filter_to_apply=None, filter_invl_to_apply=None, grouping_key_generator=None, compute_mode='tax_details'):
|
||||
@@ -662,7 +663,9 @@ class AccountMoveLine(models.Model):
|
||||
'price_subtotal_unit': self.currency_id.round(self.price_subtotal / self.quantity) if self.quantity else 0.0,
|
||||
'price_total_unit': self.currency_id.round(self.price_total / self.quantity) if self.quantity else 0.0,
|
||||
'price_discount': gross_price_subtotal - self.price_subtotal,
|
||||
'price_discount_unit': (gross_price_subtotal - self.price_subtotal) / self.quantity if self.quantity else 0.0,
|
||||
'gross_price_total_unit': self.currency_id.round(gross_price_subtotal / self.quantity) if self.quantity else 0.0,
|
||||
'unece_uom_code': self.product_id.product_tmpl_id.uom_id._get_unece_code(),
|
||||
}
|
||||
return res
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class UoM(models.Model):
|
||||
_inherit = 'uom.uom'
|
||||
|
||||
def _get_unece_code(self):
|
||||
""" Returns the UNECE code used for international trading for corresponding to the UoM as per
|
||||
https://unece.org/fileadmin/DAM/cefact/recommendations/bkup_htm/add2d.htm.
|
||||
"""
|
||||
if len(self) != 1:
|
||||
return 'C62'
|
||||
|
||||
xml_id = self.env['ir.model.data'].sudo().search([
|
||||
('model', '=', 'uom.uom'),
|
||||
('res_id', '=', self.id),
|
||||
]).name
|
||||
mapping = {
|
||||
'product_uom_unit': 'C62',
|
||||
'product_uom_dozen': 'DZN',
|
||||
'product_uom_kgm': 'KGM',
|
||||
'product_uom_gram': 'GRM',
|
||||
'product_uom_day': 'DAY',
|
||||
'product_uom_hour': 'HUR',
|
||||
'product_uom_ton': 'TNE',
|
||||
'product_uom_meter': 'MTR',
|
||||
'product_uom_km': 'KTM',
|
||||
'product_uom_cm': 'CMT',
|
||||
'product_uom_litre': 'LTR',
|
||||
'product_uom_lb': 'LBR',
|
||||
'product_uom_oz': 'ONZ',
|
||||
'product_uom_inch': 'INH',
|
||||
'product_uom_foot': 'FOT',
|
||||
'product_uom_mile': 'SMI',
|
||||
'product_uom_floz': 'OZA',
|
||||
'product_uom_qt': 'QT',
|
||||
'product_uom_gal': 'GLL',
|
||||
'product_uom_cubic_meter': 'MTQ',
|
||||
'product_uom_cubic_inch': 'INQ',
|
||||
'product_uom_cubic_foot': 'FTQ',
|
||||
}
|
||||
return mapping.get(xml_id, 'C62')
|
||||
@@ -8,4 +8,10 @@
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
<data noupdate="1">
|
||||
<record model="ir.config_parameter" id="ir_config_parameter_use_pdfa">
|
||||
<field name="key">edi.use_pdfa</field>
|
||||
<field name="value">false</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -29,50 +29,40 @@
|
||||
<!-- Line information, with discount and unit price separate -->
|
||||
<ram:GrossPriceProductTradePrice>
|
||||
<ram:ChargeAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(line.price_unit, record.currency_id)"/>
|
||||
|
||||
<!-- Discount. -->
|
||||
<ram:AppliedTradeAllowanceCharge t-if="line.discount">
|
||||
<ram:ChargeIndicator>
|
||||
<udt:Indicator t-esc="'false' if line.discount == 100 else 'true'"/>
|
||||
<udt:Indicator>false</udt:Indicator>
|
||||
</ram:ChargeIndicator>
|
||||
<ram:CalculationPercent t-esc="line.discount"/>
|
||||
<ram:ActualAmount t-esc="format_monetary(line_vals['price_discount_unit'], record.currency_id)"/>
|
||||
</ram:AppliedTradeAllowanceCharge>
|
||||
</ram:GrossPriceProductTradePrice>
|
||||
<!-- Line unit price, with discount applied -->
|
||||
<ram:NetPriceProductTradePrice>
|
||||
<t t-if="line.quantity > 0">
|
||||
<ram:ChargeAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(line.price_subtotal/line.quantity, record.currency_id)"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<ram:ChargeAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(0.0, record.currency_id)"/>
|
||||
</t>
|
||||
<ram:ChargeAmount
|
||||
t-esc="format_monetary(line_vals['price_subtotal_unit'], record.currency_id)"/>
|
||||
</ram:NetPriceProductTradePrice>
|
||||
</ram:SpecifiedLineTradeAgreement>
|
||||
|
||||
<!-- Quantity. -->
|
||||
<ram:SpecifiedLineTradeDelivery>
|
||||
<ram:BilledQuantity
|
||||
t-esc="line.quantity"/>
|
||||
<ram:BilledQuantity t-att-unitCode="line_vals['unece_uom_code']" t-esc="line.quantity"/>
|
||||
</ram:SpecifiedLineTradeDelivery>
|
||||
|
||||
<ram:SpecifiedLineTradeSettlement>
|
||||
<t t-foreach="tax_details['invoice_line_tax_details'][line]['tax_details'].values()"
|
||||
t-as="tax_detail_vals">
|
||||
<t t-set="tax" t-value="tax_detail_vals['tax']"/>
|
||||
<ram:ApplicableTradeTax t-if="tax.amount_type == 'percent'">
|
||||
<ram:RateApplicablePercent t-esc="tax.amount"/>
|
||||
<ram:ApplicableTradeTax t-if="tax_detail_vals['amount_type'] == 'percent'">
|
||||
<ram:TypeCode>VAT</ram:TypeCode>
|
||||
<ram:CategoryCode t-esc="tax_detail_vals['unece_tax_category_code']"/>
|
||||
<ram:RateApplicablePercent t-esc="tax_detail_vals['amount']"/>
|
||||
</ram:ApplicableTradeTax>
|
||||
</t>
|
||||
<!-- Subtotal. -->
|
||||
<ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<ram:LineTotalAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(line.price_subtotal, record.currency_id)"/>
|
||||
</ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</ram:SpecifiedLineTradeSettlement>
|
||||
@@ -87,15 +77,10 @@
|
||||
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
|
||||
<!-- Contact. -->
|
||||
<ram:Name t-if="partner.name" t-esc="partner.name"/>
|
||||
<ram:DefinedTradeContact>
|
||||
<ram:PersonName t-esc="partner.name"/>
|
||||
<ram:TelephoneUniversalCommunication t-if="partner.phone or partner.mobile">
|
||||
<ram:CompleteNumber t-esc="partner.phone or partner.mobile"/>
|
||||
</ram:TelephoneUniversalCommunication>
|
||||
<ram:EmailURIUniversalCommunication t-if="partner.email">
|
||||
<ram:URIID schemeID='SMTP' t-esc="partner.email"/>
|
||||
</ram:EmailURIUniversalCommunication>
|
||||
</ram:DefinedTradeContact>
|
||||
|
||||
<ram:SpecifiedLegalOrganization t-if="SpecifiedLegalOrganization">
|
||||
<ram:ID t-esc="SpecifiedLegalOrganization"/>
|
||||
</ram:SpecifiedLegalOrganization>
|
||||
|
||||
<!-- Address. -->
|
||||
<ram:PostalTradeAddress>
|
||||
@@ -149,6 +134,7 @@
|
||||
<!-- Address. -->
|
||||
<t t-call="account_edi_facturx.account_invoice_partner_facturx_export">
|
||||
<t t-set="partner" t-value="record.company_id.partner_id"/>
|
||||
<t t-set="SpecifiedLegalOrganization" t-value="seller_specified_legal_organization"/>
|
||||
</t>
|
||||
|
||||
<!-- VAT. -->
|
||||
@@ -162,6 +148,7 @@
|
||||
<!-- Address. -->
|
||||
<t t-call="account_edi_facturx.account_invoice_partner_facturx_export">
|
||||
<t t-set="partner" t-value="record.commercial_partner_id"/>
|
||||
<t t-set="SpecifiedLegalOrganization" t-value="buyer_specified_legal_organization"/>
|
||||
</t>
|
||||
|
||||
<!-- VAT. -->
|
||||
@@ -172,25 +159,27 @@
|
||||
|
||||
<!-- Reference. -->
|
||||
<ram:BuyerOrderReferencedDocument>
|
||||
<ram:IssuerAssignedID t-esc="'%s: %s' % (record.name, record.payment_reference) if record.payment_reference else record.name"/>
|
||||
<ram:IssuerAssignedID t-esc="record.payment_reference if record.payment_reference else record.name"/>
|
||||
</ram:BuyerOrderReferencedDocument>
|
||||
</ram:ApplicableHeaderTradeAgreement>
|
||||
|
||||
<!-- Delivery. Don't make a dependency with sale only for one field. -->
|
||||
<ram:ApplicableHeaderTradeDelivery>
|
||||
<ram:ShipToTradeParty
|
||||
t-if="record.partner_shipping_id">
|
||||
<ram:ShipToTradeParty>
|
||||
<t t-call="account_edi_facturx.account_invoice_partner_facturx_export">
|
||||
<t t-set="partner" t-value="record.partner_shipping_id"/>
|
||||
<t t-if="record.partner_shipping_id" t-set="partner" t-value="record.partner_shipping_id"/>
|
||||
<t t-else="" t-set="partner" t-value="record.commercial_partner_id"/>
|
||||
</t>
|
||||
</ram:ShipToTradeParty>
|
||||
</ram:ApplicableHeaderTradeDelivery>
|
||||
|
||||
<!-- Taxes. -->
|
||||
<ram:ApplicableHeaderTradeSettlement>
|
||||
<ram:InvoiceCurrencyCode t-esc="record.currency_id.name"/>
|
||||
|
||||
<!-- Bank account. -->
|
||||
<ram:SpecifiedTradeSettlementPaymentMeans t-if="record.partner_bank_id.acc_type == 'iban'">
|
||||
<ram:TypeCode>42</ram:TypeCode>
|
||||
<ram:PayeePartyCreditorFinancialAccount>
|
||||
<ram:IBANID t-esc="record.partner_bank_id.sanitized_acc_number"/>
|
||||
</ram:PayeePartyCreditorFinancialAccount>
|
||||
@@ -198,20 +187,28 @@
|
||||
|
||||
<!-- Tax Summary. -->
|
||||
<t t-foreach="tax_details['tax_details'].values()" t-as="tax_detail_vals">
|
||||
<t t-set="tax" t-value="tax_detail_vals['tax']"/>
|
||||
<ram:ApplicableTradeTax>
|
||||
<ram:CalculatedAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(balance_multiplicator * tax_detail_vals['tax_amount_currency'], record.currency_id)"/>
|
||||
<ram:TypeCode>VAT</ram:TypeCode>
|
||||
<ram:ExemptionReason t-if="tax_detail_vals['unece_tax_category_code'] == 'E'" t-esc="tax_detail_vals['exemption_reason']"/>
|
||||
<ram:ExemptionReason t-if="tax_detail_vals['unece_tax_category_code'] == 'G'" t-esc="'Export outside the EU'"/>
|
||||
<ram:ExemptionReason t-if="tax_detail_vals['unece_tax_category_code'] == 'K'" t-esc="'Intra-community supply'"/>
|
||||
<ram:BasisAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(balance_multiplicator * tax_detail_vals['base_amount_currency'], record.currency_id)"/>
|
||||
<ram:CategoryCode t-esc="tax_detail_vals['unece_tax_category_code']"/>
|
||||
<ram:RateApplicablePercent
|
||||
t-if="tax.amount_type == 'percent'"
|
||||
t-esc="tax.amount"/>
|
||||
t-if="tax_detail_vals['amount_type'] == 'percent'"
|
||||
t-esc="tax_detail_vals['amount']"/>
|
||||
</ram:ApplicableTradeTax>
|
||||
</t>
|
||||
|
||||
<ram:BillingSpecifiedPeriod>
|
||||
<ram:StartDateTime>
|
||||
<udt:DateTimeString format="102" t-esc="format_date(record.invoice_date)"/>
|
||||
</ram:StartDateTime>
|
||||
</ram:BillingSpecifiedPeriod>
|
||||
|
||||
<!-- Payment Term. -->
|
||||
<ram:SpecifiedTradePaymentTerms>
|
||||
<ram:Description t-if="record.invoice_payment_term_id" t-esc="record.invoice_payment_term_id.name"/>
|
||||
@@ -223,22 +220,17 @@
|
||||
<!-- Summary. -->
|
||||
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<ram:LineTotalAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, record.currency_id)"/>
|
||||
<ram:TaxBasisTotalAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, record.currency_id)"/>
|
||||
<ram:TaxTotalAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_tax, record.currency_id)"/>
|
||||
<ram:GrandTotalAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_total, record.currency_id)"/>
|
||||
<ram:TotalPrepaidAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_total - record.amount_residual, record.currency_id)"/>
|
||||
<ram:DuePayableAmount
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_residual, record.currency_id)"/>
|
||||
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ram:ApplicableHeaderTradeSettlement>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from . import account_edi_format
|
||||
from . import account_tax
|
||||
from . import mail_template
|
||||
|
||||
@@ -36,7 +36,7 @@ class AccountEdiFormat(models.Model):
|
||||
if not edi_document.attachment_id:
|
||||
return
|
||||
|
||||
pdf_writer.embed_odoo_attachment(edi_document.attachment_id, subtype='application/xml')
|
||||
pdf_writer.embed_odoo_attachment(edi_document.attachment_id, subtype='text/xml')
|
||||
if not pdf_writer.is_pdfa and str2bool(self.env['ir.config_parameter'].sudo().get_param('edi.use_pdfa', 'False')):
|
||||
try:
|
||||
pdf_writer.convert_to_pdfa()
|
||||
@@ -59,16 +59,29 @@ class AccountEdiFormat(models.Model):
|
||||
|
||||
def format_monetary(number, currency):
|
||||
# Format the monetary values to avoid trailing decimals (e.g. 90.85000000000001).
|
||||
if currency.is_zero(number): # Ensure that we never return -0.0
|
||||
number = 0.0
|
||||
return float_repr(number, currency.decimal_places)
|
||||
|
||||
self.ensure_one()
|
||||
# Create file content.
|
||||
seller_siret = 'siret' in invoice.company_id._fields and invoice.company_id.siret or invoice.company_id.company_registry
|
||||
buyer_siret = 'siret' in invoice.commercial_partner_id._fields and invoice.commercial_partner_id.siret
|
||||
tax_detail_vals = invoice._prepare_edi_tax_details(
|
||||
grouping_key_generator=lambda tax_values: {
|
||||
'unece_tax_category_code': tax_values['tax_id']._get_unece_category_code(invoice.commercial_partner_id, invoice.company_id),
|
||||
'amount': tax_values['tax_id'].amount,
|
||||
'amount_type': tax_values['tax_id'].amount_type, # We need to check the type
|
||||
}
|
||||
)
|
||||
template_values = {
|
||||
**invoice._prepare_edi_vals_to_export(),
|
||||
'tax_details': invoice._prepare_edi_tax_details(),
|
||||
'tax_details': tax_detail_vals,
|
||||
'format_date': format_date,
|
||||
'format_monetary': format_monetary,
|
||||
'is_html_empty': is_html_empty,
|
||||
'seller_specified_legal_organization': seller_siret,
|
||||
'buyer_specified_legal_organization': buyer_siret,
|
||||
}
|
||||
|
||||
xml_content = markupsafe.Markup("<?xml version='1.0' encoding='UTF-8'?>")
|
||||
@@ -172,17 +185,22 @@ class AccountEdiFormat(models.Model):
|
||||
if elements:
|
||||
invoice_form.narration = elements[0].text
|
||||
|
||||
# Total amount.
|
||||
elements = tree.xpath('//ram:GrandTotalAmount', namespaces=tree.nsmap)
|
||||
# Get currency string for new invoices, or invoices coming from outside
|
||||
elements = tree.xpath('//ram:InvoiceCurrencyCode', namespaces=tree.nsmap)
|
||||
if elements:
|
||||
currency_str = elements[0].text
|
||||
# Fallback for old invoices from odoo where the InvoiceCurrencyCode was not present
|
||||
else:
|
||||
elements = tree.xpath('//ram:TaxTotalAmount', namespaces=tree.nsmap)
|
||||
if elements:
|
||||
currency_str = elements[0].attrib.get('currencyID', None)
|
||||
|
||||
# Currency.
|
||||
currency_str = elements[0].attrib.get('currencyID', None)
|
||||
if currency_str:
|
||||
invoice_form.currency_id = self._retrieve_currency(currency_str)
|
||||
# Currency.
|
||||
if currency_str:
|
||||
invoice_form.currency_id = self._retrieve_currency(currency_str)
|
||||
|
||||
# Store xml total amount.
|
||||
amount_total_import = total_amount * refund_sign
|
||||
# Store xml total amount.
|
||||
amount_total_import = total_amount * refund_sign
|
||||
|
||||
# Date.
|
||||
elements = tree.xpath('//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString', namespaces=tree.nsmap)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_inherit = 'account.tax'
|
||||
|
||||
def _get_unece_category_code(self, customer, supplier):
|
||||
""" By default, this method will try to compute the tax category (used by EDI for example) based on the amount
|
||||
and the tax repartition lines. This is hack-ish~ but a valid solution to get a default value in stable.
|
||||
See https://unece.org/fileadmin/DAM/trade/untdid/d16b/tred/tred5305.htm for the codes.
|
||||
"""
|
||||
self.ensure_one()
|
||||
# Defaulting to standard tax.
|
||||
category = 'S'
|
||||
if self.type_tax_use == 'sale':
|
||||
eu_countries = self.env.ref('base.europe').country_ids
|
||||
if supplier.country_id in eu_countries and customer.country_id not in eu_countries:
|
||||
category = 'G'
|
||||
else:
|
||||
if customer.country_id != supplier.country_id \
|
||||
and customer.country_id in eu_countries \
|
||||
and supplier.country_id in eu_countries:
|
||||
category = 'K'
|
||||
# Taxes with a Zero amount will get the E code. (Exempt)
|
||||
elif self.amount == 0:
|
||||
category = 'E'
|
||||
|
||||
return category
|
||||
@@ -82,76 +82,77 @@ class TestAccountEdiFacturx(AccountEdiTestCommon):
|
||||
</SpecifiedTradeProduct>
|
||||
<SpecifiedLineTradeAgreement>
|
||||
<GrossPriceProductTradePrice>
|
||||
<ChargeAmount currencyID="Gol">275.000</ChargeAmount>
|
||||
<ChargeAmount>275.000</ChargeAmount>
|
||||
<AppliedTradeAllowanceCharge>
|
||||
<ChargeIndicator>
|
||||
<Indicator>true</Indicator>
|
||||
<Indicator>false</Indicator>
|
||||
</ChargeIndicator>
|
||||
<CalculationPercent>20.0</CalculationPercent>
|
||||
<ActualAmount>55.000</ActualAmount>
|
||||
</AppliedTradeAllowanceCharge>
|
||||
</GrossPriceProductTradePrice>
|
||||
<NetPriceProductTradePrice>
|
||||
<ChargeAmount currencyID="Gol">220.000</ChargeAmount>
|
||||
<ChargeAmount>220.000</ChargeAmount>
|
||||
</NetPriceProductTradePrice>
|
||||
</SpecifiedLineTradeAgreement>
|
||||
<SpecifiedLineTradeDelivery>
|
||||
<BilledQuantity>5.0</BilledQuantity>
|
||||
<BilledQuantity unitCode="C62">5.0</BilledQuantity>
|
||||
</SpecifiedLineTradeDelivery>
|
||||
<SpecifiedLineTradeSettlement>
|
||||
<ApplicableTradeTax>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<LineTotalAmount currencyID="Gol">1100.000</LineTotalAmount>
|
||||
<LineTotalAmount>1100.000</LineTotalAmount>
|
||||
</SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</SpecifiedLineTradeSettlement>
|
||||
</IncludedSupplyChainTradeLineItem>
|
||||
<ApplicableHeaderTradeAgreement>
|
||||
<SellerTradeParty>
|
||||
<Name>company_1_data</Name>
|
||||
<DefinedTradeContact>
|
||||
<PersonName>company_1_data</PersonName>
|
||||
</DefinedTradeContact>
|
||||
<PostalTradeAddress/>
|
||||
</SellerTradeParty>
|
||||
<BuyerTradeParty>
|
||||
<Name>partner_b</Name>
|
||||
<DefinedTradeContact>
|
||||
<PersonName>partner_b</PersonName>
|
||||
</DefinedTradeContact>
|
||||
<PostalTradeAddress/>
|
||||
</BuyerTradeParty>
|
||||
<BuyerOrderReferencedDocument>
|
||||
<IssuerAssignedID>INV/2017/00001: INV/2017/00001</IssuerAssignedID>
|
||||
<IssuerAssignedID>INV/2017/00001</IssuerAssignedID>
|
||||
</BuyerOrderReferencedDocument>
|
||||
</ApplicableHeaderTradeAgreement>
|
||||
<ApplicableHeaderTradeDelivery>
|
||||
<ShipToTradeParty>
|
||||
<Name>partner_b</Name>
|
||||
<DefinedTradeContact>
|
||||
<PersonName>partner_b</PersonName>
|
||||
</DefinedTradeContact>
|
||||
<PostalTradeAddress/>
|
||||
</ShipToTradeParty>
|
||||
</ApplicableHeaderTradeDelivery>
|
||||
<ApplicableHeaderTradeSettlement>
|
||||
<InvoiceCurrencyCode>Gol</InvoiceCurrencyCode>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount currencyID="Gol">220.000</CalculatedAmount>
|
||||
<BasisAmount currencyID="Gol">1100.000</BasisAmount>
|
||||
<CalculatedAmount>220.000</CalculatedAmount>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<BasisAmount>1100.000</BasisAmount>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<BillingSpecifiedPeriod>
|
||||
<StartDateTime>
|
||||
<DateTimeString format="102">20170101</DateTimeString>
|
||||
</StartDateTime>
|
||||
</BillingSpecifiedPeriod>
|
||||
<SpecifiedTradePaymentTerms>
|
||||
<DueDateDateTime>
|
||||
<DateTimeString format="102">20170101</DateTimeString>
|
||||
</DueDateDateTime>
|
||||
</SpecifiedTradePaymentTerms>
|
||||
<SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<LineTotalAmount currencyID="Gol">1100.000</LineTotalAmount>
|
||||
<TaxBasisTotalAmount currencyID="Gol">1100.000</TaxBasisTotalAmount>
|
||||
<LineTotalAmount>1100.000</LineTotalAmount>
|
||||
<TaxBasisTotalAmount>1100.000</TaxBasisTotalAmount>
|
||||
<TaxTotalAmount currencyID="Gol">220.000</TaxTotalAmount>
|
||||
<GrandTotalAmount currencyID="Gol">1320.000</GrandTotalAmount>
|
||||
<TotalPrepaidAmount currencyID="Gol">0.000</TotalPrepaidAmount>
|
||||
<DuePayableAmount currencyID="Gol">1320.000</DuePayableAmount>
|
||||
<GrandTotalAmount>1320.000</GrandTotalAmount>
|
||||
<TotalPrepaidAmount>0.000</TotalPrepaidAmount>
|
||||
<DuePayableAmount>1320.000</DuePayableAmount>
|
||||
</SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ApplicableHeaderTradeSettlement>
|
||||
</SupplyChainTradeTransaction>
|
||||
@@ -174,49 +175,66 @@ class TestAccountEdiFacturx(AccountEdiTestCommon):
|
||||
})
|
||||
|
||||
applied_xpath = '''
|
||||
<xpath expr="//NetPriceProductTradePrice/ChargeAmount" position="replace">
|
||||
<ChargeAmount currencyID="Gol">200.000</ChargeAmount>
|
||||
</xpath>
|
||||
<xpath expr="//SpecifiedLineTradeSettlement" position="replace">
|
||||
<SpecifiedLineTradeSettlement>
|
||||
<ApplicableTradeTax>
|
||||
<RateApplicablePercent>10.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<ApplicableTradeTax>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<LineTotalAmount currencyID="Gol">1000.000</LineTotalAmount>
|
||||
</SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</SpecifiedLineTradeSettlement>
|
||||
</xpath>
|
||||
<xpath expr="//ApplicableHeaderTradeSettlement" position="replace">
|
||||
<ApplicableHeaderTradeSettlement>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount currencyID="Gol">100.000</CalculatedAmount>
|
||||
<BasisAmount currencyID="Gol">1000.000</BasisAmount>
|
||||
<RateApplicablePercent>10.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount currencyID="Gol">220.000</CalculatedAmount>
|
||||
<BasisAmount currencyID="Gol">1100.000</BasisAmount>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<SpecifiedTradePaymentTerms>
|
||||
<DueDateDateTime>
|
||||
<DateTimeString format="102">20170101</DateTimeString>
|
||||
</DueDateDateTime>
|
||||
</SpecifiedTradePaymentTerms>
|
||||
<SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<LineTotalAmount currencyID="Gol">1000.000</LineTotalAmount>
|
||||
<TaxBasisTotalAmount currencyID="Gol">1000.000</TaxBasisTotalAmount>
|
||||
<TaxTotalAmount currencyID="Gol">320.000</TaxTotalAmount>
|
||||
<GrandTotalAmount currencyID="Gol">1320.000</GrandTotalAmount>
|
||||
<TotalPrepaidAmount currencyID="Gol">0.000</TotalPrepaidAmount>
|
||||
<DuePayableAmount currencyID="Gol">1320.000</DuePayableAmount>
|
||||
</SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ApplicableHeaderTradeSettlement>
|
||||
</xpath>
|
||||
<xpath expr="//AppliedTradeAllowanceCharge/ActualAmount" position="replace">
|
||||
<ActualAmount>50.000</ActualAmount>
|
||||
</xpath>
|
||||
<xpath expr="//NetPriceProductTradePrice/ChargeAmount" position="replace">
|
||||
<ChargeAmount>200.000</ChargeAmount>
|
||||
</xpath>
|
||||
<xpath expr="//SpecifiedLineTradeSettlement" position="replace">
|
||||
<SpecifiedLineTradeSettlement>
|
||||
<ApplicableTradeTax>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>10.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<ApplicableTradeTax>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<LineTotalAmount>1000.000</LineTotalAmount>
|
||||
</SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</SpecifiedLineTradeSettlement>
|
||||
</xpath>
|
||||
<xpath expr="//ApplicableHeaderTradeSettlement" position="replace">
|
||||
<ApplicableHeaderTradeSettlement>
|
||||
<InvoiceCurrencyCode>Gol</InvoiceCurrencyCode>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount>100.000</CalculatedAmount>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<BasisAmount>1000.000</BasisAmount>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>10.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount>220.000</CalculatedAmount>
|
||||
<TypeCode>VAT</TypeCode>
|
||||
<BasisAmount>1100.000</BasisAmount>
|
||||
<CategoryCode>S</CategoryCode>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<BillingSpecifiedPeriod>
|
||||
<StartDateTime>
|
||||
<DateTimeString format="102">20170101</DateTimeString>
|
||||
</StartDateTime>
|
||||
</BillingSpecifiedPeriod>
|
||||
<SpecifiedTradePaymentTerms>
|
||||
<DueDateDateTime>
|
||||
<DateTimeString format="102">20170101</DateTimeString>
|
||||
</DueDateDateTime>
|
||||
</SpecifiedTradePaymentTerms>
|
||||
<SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<LineTotalAmount>1000.000</LineTotalAmount>
|
||||
<TaxBasisTotalAmount>1000.000</TaxBasisTotalAmount>
|
||||
<TaxTotalAmount currencyID="Gol">320.000</TaxTotalAmount>
|
||||
<GrandTotalAmount>1320.000</GrandTotalAmount>
|
||||
<TotalPrepaidAmount>0.000</TotalPrepaidAmount>
|
||||
<DuePayableAmount>1320.000</DuePayableAmount>
|
||||
</SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ApplicableHeaderTradeSettlement>
|
||||
</xpath>
|
||||
'''
|
||||
|
||||
self.assert_generated_file_equal(self.invoice, self.expected_invoice_facturx_values, applied_xpath)
|
||||
|
||||
Reference in New Issue
Block a user