diff --git a/document.py b/document.py index 28431db..d10214b 100644 --- a/document.py +++ b/document.py @@ -7,6 +7,7 @@ import os.path import json +import copy from lxml import etree from datetime import datetime, date from decimal import Decimal @@ -633,6 +634,8 @@ class Incoming(metaclass=PoolMeta): line.account = expense_accounts[0] # check if calculated 'amount' matches with amount from xml-data + assert invoice.currency is not None + xml_amount = line_data.get('total', {}).pop('amount', None) if xml_amount is not None: if xml_amount != line.get_amount(None): @@ -667,10 +670,43 @@ class Incoming(metaclass=PoolMeta): '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() return line + def _readxml_read_listdata(self, xmldata, xpth, xtag, key_list): + """ read list of values from xml + + Args: + xmldata (xtree): xml-tree + xpth (list): list of xml-tags until index-counter + pos (int): position in list + xtag (str): xml-tag to read as list + key_list (list): [('', '')] + + Returns: + list: list of dict + """ + if isinstance(xtag, str): + xtag = [xtag] + xpath_list = xpth + xtag + num_listitem = len(xmldata.xpath( + self._readxml_xpath(xpath_list), namespaces=xmldata.nsmap)) + listdata = [] + for x in range(1, num_listitem + 1): + values = {} + for list_item in key_list: + (xkey, key, xtype) = ( + list_item if len(list_item) == 3 + else tuple(list(list_item) + [None])) + + value = self._readxml_getvalue( + xmldata, xpath_list + [x, xkey], vtype=xtype) + if value is not None: + values[key] = value + if values: + listdata.append(values) + return listdata + def _readxml_invoice_line(self, xmldata, xpth, pos): """ read invoice-line from xml @@ -682,43 +718,13 @@ class Incoming(metaclass=PoolMeta): Returns: dict: invoice line data """ - def read_listdata(xtag, key_list): - """ read list of values from xml - - Args: - xtag (str): xml-tag to read as list - key_list (list): [('', '')] - - Returns: - list: list of dict - """ - if isinstance(xtag, str): - xtag = [xtag] - xpath_list = xpth + [pos] + xtag - num_listitem = len(xmldata.xpath( - self._readxml_xpath(xpath_list), namespaces=xmldata.nsmap)) - listdata = [] - for x in range(1, num_listitem + 1): - values = {} - for list_item in key_list: - (xkey, key, xtype) = ( - list_item if len(list_item) == 3 - else tuple(list(list_item) + [None])) - - value = self._readxml_getvalue( - xmldata, xpath_list + [x, xkey], vtype=xtype) - if value is not None: - values[key] = value - if values: - listdata.append(values) - return listdata - + xpath_line = xpth + [pos] result = {'convert_note': []} - result['line_no'] = self._readxml_getvalue(xmldata, xpth + [pos] + [ + result['line_no'] = self._readxml_getvalue(xmldata, xpath_line + [ 'ram:AssociatedDocumentLineDocument', 'ram:LineID']) result['line_note'] = '\n'.join(self._readxml_getvalue( - xmldata, xpth + [pos] + [ + xmldata, xpath_line + [ 'ram:AssociatedDocumentLineDocument', 'ram:IncludedNote', 'ram:Content'], allow_list=True)) @@ -736,34 +742,38 @@ class Incoming(metaclass=PoolMeta): ('ram:ModelName', 'model_name'), ('ram:OriginTradeCountry', 'trade_country') ]: - result[key] = self._readxml_getvalue(xmldata, xpth + [pos] + [ + result[key] = self._readxml_getvalue(xmldata, xpath_line + [ 'ram:SpecifiedTradeProduct', xkey]) # attributes of product - result['attributes'] = read_listdata([ - 'ram:SpecifiedTradeProduct', - 'ram:ApplicableProductCharacteristic'], [ + result['attributes'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedTradeProduct', + 'ram:ApplicableProductCharacteristic'], [ ('ram:TypeCode', 'code'), ('ram:Description', 'description'), ('ram:ValueMeasure', 'uom'), ('ram:ValueY', 'value')]) # classification of product - result['classification'] = read_listdata([ - 'ram:SpecifiedTradeProduct', - 'ram:DesignatedProductClassification'], [ + result['classification'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedTradeProduct', + 'ram:DesignatedProductClassification'], [ ('ram:ClassCode', 'code'), ('ram:ClassName', 'name')]) # serial-numbers of product - result['serialno'] = read_listdata([ - 'ram:SpecifiedTradeProduct', - 'ram:IndividualTradeProductInstance'], [ + result['serialno'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedTradeProduct', + 'ram:IndividualTradeProductInstance'], [ ('ram:BatchID', 'lot'), ('ram:SupplierAssignedSerialID', 'serial')]) # referenced product - result['refprod'] = read_listdata( + result['refprod'] = self._readxml_read_listdata( + xmldata, xpath_line, ['ram:SpecifiedTradeProduct', 'ram:IncludedReferencedProduct'], [ ('ram:ID', 'id'), ('ram:GlobalID', 'global_id'), @@ -775,12 +785,13 @@ class Incoming(metaclass=PoolMeta): ]) # net price - xpath_netprice = xpth + [pos] + [ + xpath_netprice = xpath_line + [ 'ram:SpecifiedLineTradeAgreement', 'ram:NetPriceProductTradePrice'] if self._readxml_getvalue(xmldata, xpath_netprice): - result['unit_net_price'] = read_listdata([ - 'ram:SpecifiedLineTradeAgreement', - 'ram:NetPriceProductTradePrice'], [ + result['unit_net_price'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedLineTradeAgreement', + 'ram:NetPriceProductTradePrice'], [ ('ram:ChargeAmount', 'amount', Decimal), ('ram:BasisQuantity', 'basequantity', Decimal)])[0] # notice ignored field @@ -791,13 +802,14 @@ class Incoming(metaclass=PoolMeta): xpath_netprice + ['ram:AppliedTradeAllowanceCharge'])) # gross price - xpath_grossprice = xpth + [pos] + [ + xpath_grossprice = xpath_line + [ 'ram:SpecifiedLineTradeAgreement', 'ram:GrossPriceProductTradePrice'] if self._readxml_getvalue(xmldata, xpath_grossprice): - result['unit_gross_price'] = read_listdata([ - 'ram:SpecifiedLineTradeAgreement', - 'ram:GrossPriceProductTradePrice'], [ + result['unit_gross_price'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedLineTradeAgreement', + 'ram:GrossPriceProductTradePrice'], [ ('ram:ChargeAmount', 'amount', Decimal), ('ram:BasisQuantity', 'basequantity', Decimal)])[0] # notice ignored field @@ -809,9 +821,10 @@ class Incoming(metaclass=PoolMeta): ['ram:AppliedTradeAllowanceCharge'])) # quantity - xpath_quantity = xpth + [pos] + ['ram:SpecifiedLineTradeDelivery'] - result['quantity'] = read_listdata( - ['ram:SpecifiedLineTradeDelivery'], [ + xpath_quantity = xpath_line + ['ram:SpecifiedLineTradeDelivery'] + result['quantity'] = self._readxml_read_listdata( + xmldata, xpath_line, [ + 'ram:SpecifiedLineTradeDelivery'], [ ('ram:BilledQuantity', 'billed', Decimal), ('ram:ChargeFreeQuantity', 'chargefree', Decimal), ('ram:PackageQuantity', 'package', Decimal), @@ -829,8 +842,8 @@ class Incoming(metaclass=PoolMeta): 'skip: ' + self._readxml_xpath(xp_to_check)) # taxes - xpath_trade = xpth + [pos] + ['ram:SpecifiedLineTradeSettlement'] - result['taxes'] = read_listdata([ + xpath_trade = xpath_line + ['ram:SpecifiedLineTradeSettlement'] + result['taxes'] = self._readxml_read_listdata(xmldata, xpath_line, [ 'ram:SpecifiedLineTradeSettlement', 'ram:ApplicableTradeTax'], [ ('ram:CalculatedAmount', 'amount', Decimal), @@ -846,7 +859,7 @@ class Incoming(metaclass=PoolMeta): ('ram:RateApplicablePercent', 'percent', Decimal)]) # total amounts - result['total'] = read_listdata([ + result['total'] = self._readxml_read_listdata(xmldata, xpath_line, [ 'ram:SpecifiedLineTradeSettlement', 'ram:SpecifiedTradeSettlementLineMonetarySummation'], [ ('ram:LineTotalAmount', 'amount', Decimal),