[IMP] account_edi[_facturx]: Add generic values to export invoice EDI
This commit is contained in:
committed by
Josse Colpaert
parent
13bf64b376
commit
9b993f4af7
@@ -135,6 +135,66 @@ class AccountMove(models.Model):
|
||||
# Export Electronic Document
|
||||
####################################################
|
||||
|
||||
def _prepare_edi_vals_to_export(self):
|
||||
''' The purpose of this helper is to prepare values in order to export an invoice through the EDI system.
|
||||
This includes the computation of the tax details for each invoice line that could be very difficult to
|
||||
handle regarding the computation of the base amount.
|
||||
|
||||
:return: A python dict containing default pre-processed values.
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
res = {
|
||||
'record': self,
|
||||
'invoice_line_vals_list': [],
|
||||
}
|
||||
|
||||
# Invoice lines details.
|
||||
tax_detail_per_tax = {}
|
||||
added_base_amount_keys = set()
|
||||
for index, line in enumerate(self.invoice_line_ids.filtered(lambda line: not line.display_type), start=1):
|
||||
line_vals = line._prepare_edi_vals_to_export()
|
||||
line_vals['index'] = index
|
||||
res['invoice_line_vals_list'].append(line_vals)
|
||||
|
||||
# Tax details.
|
||||
for tax_vals in line_vals['tax_detail_vals_list']:
|
||||
tax_detail_per_tax.setdefault(tax_vals['tax'], {
|
||||
'tax': tax_vals['tax'],
|
||||
'orig_tax': tax_vals['orig_tax'],
|
||||
'tax_base_amount_currency': 0.0,
|
||||
'tax_amount_currency': 0.0,
|
||||
'tag_ids': set(),
|
||||
})
|
||||
vals = tax_detail_per_tax[tax_vals['tax']]
|
||||
|
||||
# Avoid adding multiple times the same base (e.g. with multiple taxes on the same line or multiple
|
||||
# repartition lines).
|
||||
base_amount_key = (line.id, tax_vals['tax']['id'])
|
||||
if base_amount_key not in added_base_amount_keys:
|
||||
vals['tax_base_amount_currency'] += tax_vals['tax_base_amount_currency']
|
||||
added_base_amount_keys.add(base_amount_key)
|
||||
|
||||
vals['tax_amount_currency'] += tax_vals['tax_amount_currency']
|
||||
for tag in tax_vals['tags']:
|
||||
vals['tag_ids'].add(tag.id)
|
||||
|
||||
# Format the aggregated tax details as a list.
|
||||
res['tax_detail_vals_list'] = []
|
||||
for tax_detail_vals in tax_detail_per_tax.values():
|
||||
res['tax_detail_vals_list'].append({
|
||||
**tax_detail_vals,
|
||||
'tags': self.env['account.account.tag'].browse(tax_detail_vals['tag_ids']),
|
||||
})
|
||||
|
||||
# Totals.
|
||||
res.update({
|
||||
'total_price_subtotal_before_discount': sum(x['price_subtotal_before_discount'] for x in res['invoice_line_vals_list']),
|
||||
'total_price_discount': sum(x['price_discount'] for x in res['invoice_line_vals_list']),
|
||||
})
|
||||
|
||||
return res
|
||||
|
||||
def _update_payments_edi_documents(self):
|
||||
''' Update the edi documents linked to the current journal entries. These journal entries must be linked to an
|
||||
account.payment of an account.bank.statement.line. This additional method is needed because the payment flow is
|
||||
@@ -307,6 +367,68 @@ class AccountMoveLine(models.Model):
|
||||
# Export Electronic Document
|
||||
####################################################
|
||||
|
||||
def _prepare_edi_vals_to_export(self):
|
||||
''' The purpose of this helper is the same as '_prepare_edi_vals_to_export' but for a single invoice line.
|
||||
This includes the computation of the tax details for each invoice line or the management of the discount.
|
||||
Indeed, in some EDI, we need to provide extra values depending the discount such as:
|
||||
- the discount as an amount instead of a percentage.
|
||||
- the price_unit but after subtraction of the discount.
|
||||
|
||||
:return: A python dict containing default pre-processed values.
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
res = {
|
||||
'line': self,
|
||||
'price_unit_after_discount': self.price_unit * (1 - (self.discount / 100.0)),
|
||||
'price_subtotal_before_discount': self.currency_id.round(self.price_unit * self.quantity),
|
||||
'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,
|
||||
}
|
||||
|
||||
res['price_discount'] = res['price_subtotal_before_discount'] - self.price_subtotal
|
||||
|
||||
# Tax details.
|
||||
tax_detail_per_tax = {}
|
||||
taxes_res = self.tax_ids.compute_all(
|
||||
res['price_unit_after_discount'],
|
||||
currency=self.currency_id,
|
||||
quantity=self.quantity,
|
||||
product=self.product_id,
|
||||
partner=self.partner_id,
|
||||
is_refund=self.move_id.move_type in ('in_refund', 'out_refund'),
|
||||
)
|
||||
taxes_added_to_base = set()
|
||||
for tax_vals in taxes_res['taxes']:
|
||||
tax_rep = self.env['account.tax.repartition.line'].browse(tax_vals['tax_repartition_line_id'])
|
||||
tax = tax_rep.tax_id
|
||||
tax_detail_per_tax.setdefault(tax, {
|
||||
'tax': tax,
|
||||
'orig_tax': tax_vals['group'].id if tax_vals['group'] else tax.id,
|
||||
'tax_base_amount_currency': 0.0,
|
||||
'tax_amount_currency': 0.0,
|
||||
'tag_ids': set(),
|
||||
})
|
||||
vals = tax_detail_per_tax[tax]
|
||||
|
||||
# Avoid adding multiple times the same base (e.g. with multiple repartition lines).
|
||||
if tax.id not in taxes_added_to_base:
|
||||
vals['tax_base_amount_currency'] = tax_vals['base']
|
||||
taxes_added_to_base.add(tax.id)
|
||||
|
||||
vals['tax_amount_currency'] += tax_vals['amount']
|
||||
for tag_id in tax_rep.tag_ids:
|
||||
vals['tag_ids'].add(tag_id)
|
||||
|
||||
res['tax_detail_vals_list'] = []
|
||||
for tax_detail_vals in tax_detail_per_tax.values():
|
||||
res['tax_detail_vals_list'].append({
|
||||
**tax_detail_vals,
|
||||
'tags': self.env['account.account.tag'].browse(tax_detail_vals['tag_ids']),
|
||||
})
|
||||
|
||||
return res
|
||||
|
||||
def reconcile(self):
|
||||
# OVERRIDE
|
||||
# In some countries, the payments must be sent to the government under some condition. One of them could be
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="account_invoice_line_facturx_export">
|
||||
<t t-set="line" t-value="line_values['line']"/>
|
||||
<t t-set="line" t-value="line_vals['line']"/>
|
||||
<t xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
|
||||
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
||||
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
|
||||
<ram:IncludedSupplyChainTradeLineItem>
|
||||
<!-- Line number. -->
|
||||
<ram:AssociatedDocumentLineDocument>
|
||||
<ram:LineID t-esc="line_values['index']"/>
|
||||
<ram:LineID t-esc="line_vals['index']"/>
|
||||
</ram:AssociatedDocumentLineDocument>
|
||||
|
||||
<!-- Product. -->
|
||||
@@ -28,8 +28,8 @@
|
||||
<ram:SpecifiedLineTradeAgreement>
|
||||
<ram:GrossPriceProductTradePrice>
|
||||
<ram:ChargeAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(line.price_unit, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(line.price_unit, record.currency_id)"/>
|
||||
|
||||
<!-- Discount. -->
|
||||
<ram:AppliedTradeAllowanceCharge t-if="line.discount">
|
||||
@@ -48,8 +48,8 @@
|
||||
</ram:SpecifiedLineTradeDelivery>
|
||||
|
||||
<ram:SpecifiedLineTradeSettlement>
|
||||
<t t-foreach="line_values['tax_details']" t-as="tax_vals">
|
||||
<t t-set="tax" t-value="tax_vals['tax']"/>
|
||||
<t t-foreach="line_vals['tax_detail_vals_list']" 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>
|
||||
@@ -57,8 +57,8 @@
|
||||
<!-- Subtotal. -->
|
||||
<ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<ram:LineTotalAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(line.price_subtotal, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(line.price_subtotal, record.currency_id)"/>
|
||||
</ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</ram:SpecifiedLineTradeSettlement>
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
</template>
|
||||
|
||||
<template id="account_invoice_facturx_export">
|
||||
<t t-set="currency" t-value="record.currency_id or record.company_currency_id"/>
|
||||
<rsm:CrossIndustryInvoice
|
||||
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
|
||||
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
||||
@@ -124,7 +123,7 @@
|
||||
|
||||
<rsm:SupplyChainTradeTransaction>
|
||||
<!-- Invoice lines. -->
|
||||
<t t-foreach="invoice_line_values" t-as="line_values">
|
||||
<t t-foreach="invoice_line_vals_list" t-as="line_vals">
|
||||
<t t-call="account_edi_facturx.account_invoice_line_facturx_export"/>
|
||||
</t>
|
||||
|
||||
@@ -183,18 +182,18 @@
|
||||
</ram:SpecifiedTradeSettlementPaymentMeans>
|
||||
|
||||
<!-- Tax Summary. -->
|
||||
<t t-foreach="tax_details" t-as="tax_vals">
|
||||
<t t-set="tax_line" t-value="tax_vals['line']"/>
|
||||
<t t-foreach="tax_detail_vals_list" t-as="tax_detail_vals">
|
||||
<t t-set="tax" t-value="tax_detail_vals['tax']"/>
|
||||
<ram:ApplicableTradeTax>
|
||||
<ram:CalculatedAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(tax_vals['tax_amount'], currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(tax_detail_vals['tax_amount_currency'], record.currency_id)"/>
|
||||
<ram:BasisAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(tax_vals['tax_base_amount'], currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(tax_detail_vals['tax_base_amount_currency'], record.currency_id)"/>
|
||||
<ram:RateApplicablePercent
|
||||
t-if="tax_line.tax_line_id.amount_type == 'percent'"
|
||||
t-esc="tax_line.tax_line_id.amount"/>
|
||||
t-if="tax.amount_type == 'percent'"
|
||||
t-esc="tax.amount"/>
|
||||
</ram:ApplicableTradeTax>
|
||||
</t>
|
||||
|
||||
@@ -209,23 +208,23 @@
|
||||
<!-- Summary. -->
|
||||
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<ram:LineTotalAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, record.currency_id)"/>
|
||||
<ram:TaxBasisTotalAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_untaxed, record.currency_id)"/>
|
||||
<ram:TaxTotalAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(record.amount_tax, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_tax, record.currency_id)"/>
|
||||
<ram:GrandTotalAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(record.amount_total, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_total, record.currency_id)"/>
|
||||
<ram:TotalPrepaidAmount
|
||||
t-att-currencyID="currency.name"
|
||||
t-esc="format_monetary(record.amount_total - record.amount_residual, currency)"/>
|
||||
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="currency.name"
|
||||
t-esc="format_monetary(record.amount_residual, currency)"/>
|
||||
t-att-currencyID="record.currency_id.name"
|
||||
t-esc="format_monetary(record.amount_residual, record.currency_id)"/>
|
||||
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ram:ApplicableHeaderTradeSettlement>
|
||||
</rsm:SupplyChainTradeTransaction>
|
||||
|
||||
@@ -52,53 +52,11 @@ class AccountEdiFormat(models.Model):
|
||||
self.ensure_one()
|
||||
# Create file content.
|
||||
template_values = {
|
||||
'record': invoice,
|
||||
**invoice._prepare_edi_vals_to_export(),
|
||||
'format_date': format_date,
|
||||
'format_monetary': format_monetary,
|
||||
'invoice_line_values': [],
|
||||
}
|
||||
|
||||
# Tax lines.
|
||||
aggregated_taxes_details = {line.tax_line_id.id: {
|
||||
'line': line,
|
||||
'tax_amount': -line.amount_currency if line.currency_id else -line.balance,
|
||||
'tax_base_amount': 0.0,
|
||||
} for line in invoice.line_ids.filtered('tax_line_id')}
|
||||
|
||||
# Invoice lines.
|
||||
for i, line in enumerate(invoice.invoice_line_ids.filtered(lambda l: not l.display_type)):
|
||||
price_unit_with_discount = line.price_unit * (1 - (line.discount / 100.0))
|
||||
taxes_res = line.tax_ids.with_context(force_sign=line.move_id._get_tax_force_sign()).compute_all(
|
||||
price_unit_with_discount,
|
||||
currency=line.currency_id,
|
||||
quantity=line.quantity,
|
||||
product=line.product_id,
|
||||
partner=invoice.partner_id,
|
||||
is_refund=line.move_id.move_type in ('in_refund', 'out_refund'),
|
||||
)
|
||||
|
||||
line_template_values = {
|
||||
'line': line,
|
||||
'index': i + 1,
|
||||
'tax_details': [],
|
||||
'net_price_subtotal': taxes_res['total_excluded'],
|
||||
}
|
||||
|
||||
for tax_res in taxes_res['taxes']:
|
||||
tax = self.env['account.tax'].browse(tax_res['id'])
|
||||
line_template_values['tax_details'].append({
|
||||
'tax': tax,
|
||||
'tax_amount': tax_res['amount'],
|
||||
'tax_base_amount': tax_res['base'],
|
||||
})
|
||||
|
||||
if tax.id in aggregated_taxes_details:
|
||||
aggregated_taxes_details[tax.id]['tax_base_amount'] += tax_res['base']
|
||||
|
||||
template_values['invoice_line_values'].append(line_template_values)
|
||||
|
||||
template_values['tax_details'] = list(aggregated_taxes_details.values())
|
||||
|
||||
xml_content = b"<?xml version='1.0' encoding='UTF-8'?>"
|
||||
xml_content += self.env.ref('account_edi_facturx.account_invoice_facturx_export')._render(template_values)
|
||||
xml_name = '%s_facturx.xml' % (invoice.name.replace('/', '_'))
|
||||
|
||||
@@ -180,16 +180,16 @@ class TestAccountEdiFacturx(AccountEdiTestCommon):
|
||||
</xpath>
|
||||
<xpath expr="//ApplicableHeaderTradeSettlement" position="replace">
|
||||
<ApplicableHeaderTradeSettlement>
|
||||
<ApplicableTradeTax>
|
||||
<CalculatedAmount currencyID="Gol">220.000</CalculatedAmount>
|
||||
<BasisAmount currencyID="Gol">1100.000</BasisAmount>
|
||||
<RateApplicablePercent>20.0</RateApplicablePercent>
|
||||
</ApplicableTradeTax>
|
||||
<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>20170101</DateTimeString>
|
||||
|
||||
Reference in New Issue
Block a user