read invoice-line, taxes
This commit is contained in:
parent
5d68a2c2b4
commit
18103d8e80
4 changed files with 189 additions and 6 deletions
162
document.py
162
document.py
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from datetime import datetime
|
from datetime import datetime, date
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from trytond.pool import PoolMeta, Pool
|
from trytond.pool import PoolMeta, Pool
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
|
@ -110,7 +110,9 @@ class Incoming(metaclass=PoolMeta):
|
||||||
if nodes:
|
if nodes:
|
||||||
for node1 in nodes:
|
for node1 in nodes:
|
||||||
result.append(
|
result.append(
|
||||||
node1.text if vtype is None else vtype(node1.text))
|
node1.text if vtype is None
|
||||||
|
else self._readxml_convertdate(node1.text) if vtype == date
|
||||||
|
else vtype(node1.text))
|
||||||
|
|
||||||
if not allow_list:
|
if not allow_list:
|
||||||
break
|
break
|
||||||
|
@ -414,10 +416,118 @@ class Incoming(metaclass=PoolMeta):
|
||||||
lines_data.append(
|
lines_data.append(
|
||||||
self._readxml_invoice_line(xmltree, xpath_line_item, x))
|
self._readxml_invoice_line(xmltree, xpath_line_item, x))
|
||||||
print('\n## lines_data:', lines_data)
|
print('\n## lines_data:', lines_data)
|
||||||
|
lines = [
|
||||||
|
self._readxml_getinvoiceline(invoice, x)
|
||||||
|
for x in lines_data]
|
||||||
|
invoice.lines = lines
|
||||||
|
for pos in range(len(lines)):
|
||||||
|
invoice.lines[pos].on_change_account()
|
||||||
|
invoice.on_change_lines()
|
||||||
|
|
||||||
raise ValueError('stop')
|
raise ValueError('stop')
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
|
def _readxml_getinvoiceline(self, invoice, line_data):
|
||||||
|
""" create invoice line in memory
|
||||||
|
|
||||||
|
Args:
|
||||||
|
line_data (dict): values from xml to create
|
||||||
|
invoice line
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Line = pool.get('account.invoice.line')
|
||||||
|
Tax = pool.get('account.tax')
|
||||||
|
Configuration = pool.get('document.incoming.configuration')
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Args:
|
||||||
|
percent (Decimal): tax percent, 7% --> 0.07
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UserError: if no product category is configured or
|
||||||
|
no matching tax was found
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
record: model 'account.tax
|
||||||
|
"""
|
||||||
|
if not (cfg1 and cfg1.product_category):
|
||||||
|
raise UserError(gettext(
|
||||||
|
'document_incoming_invoice_xml.msg_no_prodcat_configured'))
|
||||||
|
|
||||||
|
for pcat in cfg1.product_category:
|
||||||
|
for s_tax in pcat.supplier_taxes:
|
||||||
|
# get current taxes of supplier-tax at
|
||||||
|
# invoice-date
|
||||||
|
current_taxes = Tax.compute(
|
||||||
|
[s_tax], Decimal('1'), 1.0, invoice.invoice_date)
|
||||||
|
|
||||||
|
# deny 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
|
||||||
|
|
||||||
|
# no product category found
|
||||||
|
raise UserError(gettext(
|
||||||
|
'document_incoming_invoice_xml.msg_no_prodcat_found',
|
||||||
|
percent=percent * Decimal('100.0')))
|
||||||
|
|
||||||
|
line = Line(
|
||||||
|
invoice=invoice,
|
||||||
|
type='line',
|
||||||
|
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)
|
||||||
|
|
||||||
|
# description
|
||||||
|
descr = [
|
||||||
|
'[%s]' % line_no if line_no else None,
|
||||||
|
line_data.pop('name', None),
|
||||||
|
line_data.pop('description', None)]
|
||||||
|
line.description = '; '.join([x for x in descr if x])
|
||||||
|
|
||||||
|
# taxes
|
||||||
|
taxes = []
|
||||||
|
line_taxes = line_data.get('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]
|
||||||
|
# check result
|
||||||
|
if len(line_taxes) != len(taxes):
|
||||||
|
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
|
||||||
|
|
||||||
|
#line.account =
|
||||||
|
|
||||||
|
# cleanup used values
|
||||||
|
for x in ['quantity', 'unit_net_price']:
|
||||||
|
if not line_data.get(x, {}):
|
||||||
|
del line_data[x]
|
||||||
|
|
||||||
|
line.on_change_invoice()
|
||||||
|
|
||||||
|
print('-- line_data-nach:', line_data, (line,))
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
||||||
def _readxml_invoice_line(self, xmldata, xpth, pos):
|
def _readxml_invoice_line(self, xmldata, xpth, pos):
|
||||||
""" read invoice-line from xml
|
""" read invoice-line from xml
|
||||||
|
|
||||||
|
@ -529,7 +639,7 @@ class Incoming(metaclass=PoolMeta):
|
||||||
'ram:SpecifiedLineTradeAgreement',
|
'ram:SpecifiedLineTradeAgreement',
|
||||||
'ram:NetPriceProductTradePrice'], [
|
'ram:NetPriceProductTradePrice'], [
|
||||||
('ram:ChargeAmount', 'amount', Decimal),
|
('ram:ChargeAmount', 'amount', Decimal),
|
||||||
('ram:BasisQuantity', 'basequantity', Decimal)])
|
('ram:BasisQuantity', 'basequantity', Decimal)])[0]
|
||||||
# notice ignored field
|
# notice ignored field
|
||||||
if self._readxml_getvalue(xmldata, xpath_netprice + [
|
if self._readxml_getvalue(xmldata, xpath_netprice + [
|
||||||
'ram:AppliedTradeAllowanceCharge']):
|
'ram:AppliedTradeAllowanceCharge']):
|
||||||
|
@ -546,13 +656,14 @@ class Incoming(metaclass=PoolMeta):
|
||||||
'ram:SpecifiedLineTradeAgreement',
|
'ram:SpecifiedLineTradeAgreement',
|
||||||
'ram:GrossPriceProductTradePrice'], [
|
'ram:GrossPriceProductTradePrice'], [
|
||||||
('ram:ChargeAmount', 'amount', Decimal),
|
('ram:ChargeAmount', 'amount', Decimal),
|
||||||
('ram:BasisQuantity', 'basequantity', Decimal)])
|
('ram:BasisQuantity', 'basequantity', Decimal)])[0]
|
||||||
# notice ignored field
|
# notice ignored field
|
||||||
if self._readxml_getvalue(xmldata, xpath_grossprice + [
|
if self._readxml_getvalue(xmldata, xpath_grossprice + [
|
||||||
'ram:AppliedTradeAllowanceCharge']):
|
'ram:AppliedTradeAllowanceCharge']):
|
||||||
result['convert_note'].append(
|
result['convert_note'].append(
|
||||||
'skip: ' + self._readxml_xpath(
|
'skip: ' + self._readxml_xpath(
|
||||||
xpath_grossprice + ['ram:AppliedTradeAllowanceCharge']))
|
xpath_grossprice +
|
||||||
|
['ram:AppliedTradeAllowanceCharge']))
|
||||||
|
|
||||||
# quantity
|
# quantity
|
||||||
xpath_quantity = xpth + [pos] + ['ram:SpecifiedLineTradeDelivery']
|
xpath_quantity = xpth + [pos] + ['ram:SpecifiedLineTradeDelivery']
|
||||||
|
@ -561,7 +672,7 @@ class Incoming(metaclass=PoolMeta):
|
||||||
('ram:BilledQuantity', 'billed', Decimal),
|
('ram:BilledQuantity', 'billed', Decimal),
|
||||||
('ram:ChargeFreeQuantity', 'chargefree', Decimal),
|
('ram:ChargeFreeQuantity', 'chargefree', Decimal),
|
||||||
('ram:PackageQuantity', 'package', Decimal),
|
('ram:PackageQuantity', 'package', Decimal),
|
||||||
])
|
])[0]
|
||||||
# notice ignored fields
|
# notice ignored fields
|
||||||
for x in [
|
for x in [
|
||||||
'ShipToTradeParty', 'UltimateShipToTradeParty',
|
'ShipToTradeParty', 'UltimateShipToTradeParty',
|
||||||
|
@ -575,6 +686,45 @@ class Incoming(metaclass=PoolMeta):
|
||||||
'skip: ' + self._readxml_xpath(xp_to_check))
|
'skip: ' + self._readxml_xpath(xp_to_check))
|
||||||
|
|
||||||
# taxes
|
# taxes
|
||||||
|
xpath_trade = xpth + [pos] + ['ram:SpecifiedLineTradeSettlement']
|
||||||
|
result['taxes'] = read_listdata([
|
||||||
|
'ram:SpecifiedLineTradeSettlement',
|
||||||
|
'ram:ApplicableTradeTax'], [
|
||||||
|
('ram:CalculatedAmount', 'amount', Decimal),
|
||||||
|
('ram:TypeCode', 'type'),
|
||||||
|
('ram:ExemptionReason', 'reason'),
|
||||||
|
('ram:BasisAmount', 'base', Decimal),
|
||||||
|
('ram:LineTotalBasisAmount', 'basetotal', Decimal),
|
||||||
|
('ram:AllowanceChargeBasisAmount', 'feebase', Decimal),
|
||||||
|
('ram:CategoryCode', 'category_code'),
|
||||||
|
('ram:ExemptionReasonCode', 'reason_code'),
|
||||||
|
('ram:TaxPointDate', 'taxdate', date),
|
||||||
|
('ram:DueDateTypeCode', 'duecode'),
|
||||||
|
('ram:RateApplicablePercent', 'percent', Decimal)])
|
||||||
|
|
||||||
|
# total amounts
|
||||||
|
result['total'] = read_listdata([
|
||||||
|
'ram:SpecifiedLineTradeSettlement',
|
||||||
|
'ram:SpecifiedTradeSettlementLineMonetarySummation'], [
|
||||||
|
('ram:LineTotalAmount', 'amount', Decimal),
|
||||||
|
('ram:ChargeTotalAmount', 'charge', Decimal),
|
||||||
|
('ram:AllowanceTotalAmount', 'fee', Decimal),
|
||||||
|
('ram:TaxTotalAmount', 'tax', Decimal),
|
||||||
|
('ram:GrandTotalAmount', 'grand', Decimal),
|
||||||
|
('ram:TotalAllowanceChargeAmount', 'feecharge', Decimal),
|
||||||
|
])
|
||||||
|
|
||||||
|
# notice ignored fields
|
||||||
|
for x in [
|
||||||
|
'BillingSpecifiedPeriod', 'SpecifiedTradeAllowanceCharge',
|
||||||
|
'ActualDeliverySupplyChainEvent',
|
||||||
|
'InvoiceReferencedDocument',
|
||||||
|
'AdditionalReferencedDocument',
|
||||||
|
'ReceivableSpecifiedTradeAccountingAccount']:
|
||||||
|
xp_to_check = xpath_trade + ['ram:' + x]
|
||||||
|
if self._readxml_getvalue(xmldata, xp_to_check):
|
||||||
|
result['convert_note'].append(
|
||||||
|
'skip: ' + self._readxml_xpath(xp_to_check))
|
||||||
|
|
||||||
# skip None values
|
# skip None values
|
||||||
return {x: result[x] for x in result.keys() if result[x]}
|
return {x: result[x] for x in result.keys() if result[x]}
|
||||||
|
|
12
locale/de.po
12
locale/de.po
|
@ -22,6 +22,18 @@ msgctxt "model:ir.message,text:msg_not_our_company"
|
||||||
msgid "The buyer party '%(partytxt)s' differs from the corporate party."
|
msgid "The buyer party '%(partytxt)s' differs from the corporate party."
|
||||||
msgstr "Die Käuferpartei '%(partytxt)s' weicht von der Unternehmenspartei ab."
|
msgstr "Die Käuferpartei '%(partytxt)s' weicht von der Unternehmenspartei ab."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_no_prodcat_configured"
|
||||||
|
msgid "There is no product category configured for XML import in Document-Incoming."
|
||||||
|
msgstr "Es ist in Document-Incoming keine Produktkategorie für den XML-Import kofiguriert."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_no_prodcat_found"
|
||||||
|
msgid "No product category with a %(percent)s %% tax found."
|
||||||
|
msgstr "Keine Produktkategorie mit einer Steuer von %(percent)s %% gefunden."
|
||||||
|
|
||||||
|
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'."
|
||||||
|
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# document.incoming.configuration #
|
# document.incoming.configuration #
|
||||||
|
|
12
locale/en.po
12
locale/en.po
|
@ -18,6 +18,18 @@ msgctxt "model:ir.message,text:msg_not_our_company"
|
||||||
msgid "The buyer party '%(partytxt)s' differs from the corporate party."
|
msgid "The buyer party '%(partytxt)s' differs from the corporate party."
|
||||||
msgstr "The buyer party '%(partytxt)s' differs from the corporate party."
|
msgstr "The buyer party '%(partytxt)s' differs from the corporate party."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_no_prodcat_configured"
|
||||||
|
msgid "There is no product category configured for XML import in Document-Incoming."
|
||||||
|
msgstr "There is no product category configured for XML import in Document-Incoming."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_no_prodcat_found"
|
||||||
|
msgid "No product category with a %(percent)s %% tax found."
|
||||||
|
msgstr "No product category with a %(percent)s %% tax found."
|
||||||
|
|
||||||
|
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 "field:document.incoming.configuration,create_supplier:"
|
msgctxt "field:document.incoming.configuration,create_supplier:"
|
||||||
msgid "Create Supplier Party"
|
msgid "Create Supplier Party"
|
||||||
msgstr "Create Supplier Party"
|
msgstr "Create Supplier Party"
|
||||||
|
|
|
@ -17,6 +17,15 @@
|
||||||
<record model="ir.message" id="msg_not_our_company">
|
<record model="ir.message" id="msg_not_our_company">
|
||||||
<field name="text">The buyer party '%(partytxt)s' differs from the corporate party.</field>
|
<field name="text">The buyer party '%(partytxt)s' differs from the corporate party.</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="ir.message" id="msg_no_prodcat_configured">
|
||||||
|
<field name="text">There is no product category configured for XML import in Document-Incoming.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_no_prodcat_found">
|
||||||
|
<field name="text">No product category with a %(percent)s %% tax found.</field>
|
||||||
|
</record>
|
||||||
|
<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>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|
Loading…
Reference in a new issue