line: import taxes, notes

This commit is contained in:
Frederik Jaeckel 2025-01-15 16:26:01 +01:00
parent 18103d8e80
commit 174887f87e
4 changed files with 103 additions and 25 deletions

View file

@ -6,15 +6,18 @@
# https://portal3.gefeg.com/projectdata/invoice/deliverables/installed/publishingproject/xrechnung%20%28german%20cius%29/xrechnung_crossindustryinvoice%3B%202017-10-18.scm/html/021.htm?https://portal3.gefeg.com/projectdata/invoice/deliverables/installed/publishingproject/xrechnung%20%28german%20cius%29/xrechnung_crossindustryinvoice%3B%202017-10-18.scm/html/025.htm
import os.path
import json
from lxml import etree
from datetime import datetime, date
from decimal import Decimal
from trytond.pool import PoolMeta, Pool
from trytond.report import Report
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.model import fields
from trytond.pyson import Eval
from trytond.protocols.jsonrpc import JSONEncoder
xml_types = [
@ -410,21 +413,18 @@ class Incoming(metaclass=PoolMeta):
# get number of invoice-lines
num_lines = len(xmltree.xpath(
self._readxml_xpath(xpath_line_item), namespaces=xmltree.nsmap))
print('\n## num_lines:', num_lines)
lines_data = []
for x in range(1, num_lines + 1):
lines_data.append(
self._readxml_invoice_line(xmltree, xpath_line_item, x))
print('\n## lines_data:', lines_data)
lines = [
self._readxml_getinvoiceline(invoice, x)
for x in lines_data]
for x in range(len(lines)):
lines[x].sequence = x + 1
invoice.lines = lines
for pos in range(len(lines)):
invoice.lines[pos].on_change_account()
invoice.on_change_lines()
raise ValueError('stop')
invoice.on_change_lines()
return invoice
def _readxml_getinvoiceline(self, invoice, line_data):
@ -437,10 +437,11 @@ class Incoming(metaclass=PoolMeta):
pool = Pool()
Line = pool.get('account.invoice.line')
Tax = pool.get('account.tax')
Uom = pool.get('product.uom')
Configuration = pool.get('document.incoming.configuration')
ModelData = pool.get('ir.model.data')
cfg1 = Configuration.get_singleton()
print('\n## line_data-vor:', line_data)
def get_tax_by_percent(percent):
""" search for tax by percent of supplier tax
@ -453,7 +454,7 @@ class Incoming(metaclass=PoolMeta):
no matching tax was found
Returns:
record: model 'account.tax
tuple: (model 'account.tax, model 'product.category')
"""
if not (cfg1 and cfg1.product_category):
raise UserError(gettext(
@ -466,23 +467,34 @@ class Incoming(metaclass=PoolMeta):
current_taxes = Tax.compute(
[s_tax], Decimal('1'), 1.0, invoice.invoice_date)
# deny result of multiple or none taxes
# skip result of multiple or none taxes
if len(current_taxes) != 1:
continue
if (current_taxes[0]['tax'].rate == percent) and (
current_taxes[0]['tax'].type == 'percentage'):
# found it
return s_tax
return (s_tax, pcat)
# no product category found
raise UserError(gettext(
'document_incoming_invoice_xml.msg_no_prodcat_found',
percent=percent * Decimal('100.0')))
# uom
xml_uom = Uom(ModelData.get_id('product', 'uom_unit'))
for x_attr in line_data.get('attributes', []):
x_uom = x_attr.get('uom', None)
if not x_uom:
continue
units = Uom.search([('symbol', '=', x_uom)])
if units:
xml_uom = units[0]
line = Line(
invoice=invoice,
type='line',
unit=xml_uom,
quantity=line_data.get('quantity', {}).pop('billed', None),
unit_price=line_data.get('unit_net_price', {}).pop('amount', None))
line_no = line_data.pop('line_no', None)
@ -494,40 +506,73 @@ class Incoming(metaclass=PoolMeta):
line_data.pop('description', None)]
line.description = '; '.join([x for x in descr if x])
# taxes
taxes = []
line_taxes = line_data.get('taxes', [])
# get taxes and product-category from settings
tax_and_category = []
line_taxes = line_data.pop('taxes', [])
for x in line_taxes:
percent = x.get('percent', None)
if (x.get('type', '') == 'VAT') and (percent is not None):
percent = percent / Decimal('100')
taxes.append(get_tax_by_percent(percent))
# remove not-found-taxes
taxes = [x for x in taxes if x]
tax_and_category.append(get_tax_by_percent(percent))
# check result
if len(line_taxes) != len(taxes):
if len(line_taxes) != len(tax_and_category):
raise UserError(gettext(
'document_incoming_invoice_xml.msg_numtaxes_invalid',
descr=line.description,
taxin='|'.join([
str(x.get('percent', '-')) for x in line_taxes]),
taxout='|'.join([
str(x.rate * Decimal('100.0')) for x in taxes])))
line.taxes = taxes
str(x[0].rate * Decimal('100.0'))
for x in tax_and_category])))
#line.account =
# set taxes and account for product
line.taxes = [x[0] for x in tax_and_category]
expense_accounts = [
x[1].account_expense
for x in tax_and_category if x[1].account_expense]
if expense_accounts:
line.account = expense_accounts[0]
# check if calculated 'amount' matches with amount from xml-data
xml_amount = line_data.get('total', {}).pop('amount', None)
if xml_amount is not None:
if xml_amount != line.get_amount(None):
raise UserError(gettext(
'document_incoming_invoice_xml.msg_line_amount_invalid',
linetxt=line.description,
calcsum=Report.format_currency(
line.get_amount(None), None, invoice.currency),
xmlsum=Report.format_currency(
xml_amount, None, invoice.currency)))
# cleanup used values
for x in ['quantity', 'unit_net_price']:
for x in ['quantity', 'unit_net_price', 'total']:
if not line_data.get(x, {}):
del line_data[x]
# note of line
notes = [line_data.pop('line_note', None)]
# skipped xml-data
convert_notes = line_data.pop('convert_note', None)
if convert_notes:
notes.extend(
[' '] +
[gettext('document_incoming_invoice_xml.msg_convert_note')] +
convert_notes
if isinstance(convert_notes, list) else [str(convert_notes)])
# values not used to create invoice-line
if line_data:
notes.extend([
' ',
gettext(
'document_incoming_invoice_xml.msg_unused_linevalues'),
json.dumps(line_data, cls=JSONEncoder, indent=3)])
line.note = '\n'.join(x for x in notes if x)
line.on_change_invoice()
print('-- line_data-nach:', line_data, (line,))
return line
def _readxml_invoice_line(self, xmldata, xpth, pos):
""" read invoice-line from xml
@ -712,7 +757,7 @@ class Incoming(metaclass=PoolMeta):
('ram:TaxTotalAmount', 'tax', Decimal),
('ram:GrandTotalAmount', 'grand', Decimal),
('ram:TotalAllowanceChargeAmount', 'feecharge', Decimal),
])
])[0]
# notice ignored fields
for x in [

View file

@ -34,6 +34,18 @@ msgctxt "model:ir.message,text:msg_numtaxes_invalid"
msgid "Invalid number of taxes in line %(descr)s: wanted='%(taxin)s', found='%(taxout)s'."
msgstr "Ungültige Anzahl Steuern in Zeile %(descr)s: gesucht='%(taxin)s', gefunden='%(taxout)s'."
msgctxt "model:ir.message,text:msg_line_amount_invalid"
msgid "The calculated sum '%(calcsum)s' of the line '%(linetxt)s' differs from the value '%(xmlsum)s' from the XML data."
msgstr "Die berechnete Summe '%(calcsum)s' der Zeile '%(linetxt)s' weicht vom Wert '%(xmlsum)s' aus den XML-Daten ab."
msgctxt "model:ir.message,text:msg_convert_note"
msgid "The following XML data has not been imported:"
msgstr "Folgende XML-Daten wurden nicht importiert:"
msgctxt "model:ir.message,text:msg_unused_linevalues"
msgid "The following data was not used to generate the invoice line:"
msgstr "Die folgenden Daten wurden für die Erzeugung der Rechnungszeile nicht verwendet:"
###################################
# document.incoming.configuration #

View file

@ -30,6 +30,18 @@ msgctxt "model:ir.message,text:msg_numtaxes_invalid"
msgid "Invalid number of taxes in line %(descr)s: wanted='%(taxin)s', found='%(taxout)s'."
msgstr "Invalid number of taxes in line %(descr)s: wanted='%(taxin)s', found='%(taxout)s'."
msgctxt "model:ir.message,text:msg_line_amount_invalid"
msgid "The calculated sum '%(calcsum)s' of the line '%(linetxt)s' differs from the value '%(xmlsum)s' from the XML data."
msgstr "The calculated sum '%(calcsum)s' of the line '%(linetxt)s' differs from the value '%(xmlsum)s' from the XML data."
msgctxt "model:ir.message,text:msg_convert_note"
msgid "The following XML data has not been imported:"
msgstr "The following XML data has not been imported:"
msgctxt "model:ir.message,text:msg_unused_linevalues"
msgid "The following data was not used to generate the invoice line:"
msgstr "The following data was not used to generate the invoice line:"
msgctxt "field:document.incoming.configuration,create_supplier:"
msgid "Create Supplier Party"
msgstr "Create Supplier Party"

View file

@ -26,6 +26,15 @@
<record model="ir.message" id="msg_numtaxes_invalid">
<field name="text">Invalid number of taxes in line %(descr)s: wanted='%(taxin)s', found='%(taxout)s'.</field>
</record>
<record model="ir.message" id="msg_line_amount_invalid">
<field name="text">The calculated sum '%(calcsum)s' of the line '%(linetxt)s' differs from the value '%(xmlsum)s' from the XML data.</field>
</record>
<record model="ir.message" id="msg_convert_note">
<field name="text">The following XML data was not used:</field>
</record>
<record model="ir.message" id="msg_unused_linevalues">
<field name="text">The following data was not used to generate the invoice line:</field>
</record>
</data>
</tryton>