read some fields
This commit is contained in:
parent
a5cbb23cdc
commit
0024f76192
14 changed files with 520 additions and 1 deletions
|
@ -11,6 +11,7 @@ Requires
|
|||
========
|
||||
- Tryton 7.0
|
||||
|
||||
|
||||
Changes
|
||||
=======
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
from .document import Incoming
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
Incoming,
|
||||
module='document_incoming_invoice_xml', type_='model')
|
||||
|
|
227
document.py
Normal file
227
document.py
Normal file
|
@ -0,0 +1,227 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the document-incoming-invoice-xml-module
|
||||
# from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
# 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
|
||||
from lxml import etree
|
||||
from datetime import datetime
|
||||
from trytond.pool import PoolMeta
|
||||
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
|
||||
|
||||
|
||||
xml_types = [
|
||||
(['xsd', 'Factur-X_1.07.2_EXTENDED', 'Factur-X_1.07.2_EXTENDED.xsd'],
|
||||
'Factur-X extended', 'facturx_extended'),
|
||||
(['xsd', 'Factur-X_1.07.2_EN16931', 'Factur-X_1.07.2_EN16931.xsd'],
|
||||
'Factur-X EN16931', ''),
|
||||
(['xsd', 'Factur-X_1.07.2_BASIC', 'Factur-X_1.07.2_BASIC.xsd'],
|
||||
'Factur-X basic', ''),
|
||||
(['xsd', 'Factur-X_1.07.2_BASICWL', 'Factur-X_1.07.2_BASICWL.xsd'],
|
||||
'Factur-X basic-wl', ''),
|
||||
(['xsd', '/Factur-X_1.07.2_MINIMUM', 'Factur-X_1.07.2_MINIMUM.xsd'],
|
||||
'Factur-X minimum', ''),
|
||||
(['xsd', 'CII D22B XSD', 'CrossIndustryInvoice_100pD22B.xsd'],
|
||||
'CrossIndustryInvoice D22', ''),
|
||||
(['xsd', 'os-UBL-2.1', 'xsd/maindoc', 'UBL-Invoice-2.1.xsd'],
|
||||
'XRechnung - Invoice', ''),
|
||||
(['xsd', 'os-UBL-2.1', 'xsd', 'maindoc', 'UBL-CreditNote-2.1.xsd'],
|
||||
'XRechnung - Credit Note', ''),
|
||||
(['xsd', 'os-UBL-2.1', 'xsd', 'maindoc', 'UBL-DebitNote-2.1.xsd'],
|
||||
'XRechnung - Debit Note', '')]
|
||||
|
||||
|
||||
class Incoming(metaclass=PoolMeta):
|
||||
__name__ = 'document.incoming'
|
||||
|
||||
xsd_type = fields.Char(
|
||||
string='XML Content', readonly=True,
|
||||
states={'invisible': Eval('mime_type', '') != 'application/xml'})
|
||||
|
||||
@classmethod
|
||||
def default_company(cls):
|
||||
return Transaction().context.get('company')
|
||||
|
||||
def _process_supplier_invoice(self):
|
||||
""" try to detect content of 'data', read values
|
||||
"""
|
||||
invoice = super()._process_supplier_invoice()
|
||||
|
||||
self.xsd_type = None
|
||||
if self.mime_type == 'application/xml':
|
||||
# detect xml-content
|
||||
xml_info = self._facturx_detect_content()
|
||||
if xml_info:
|
||||
(xsd_type, funcname, xmltree) = xml_info
|
||||
self.xsd_type = xsd_type
|
||||
|
||||
xml_read_func = getattr(self, '_readxml_%s' % funcname, None)
|
||||
if not xml_read_func:
|
||||
raise UserError(gettext(
|
||||
'document_incoming_invoice_xml.msg_not_implemented',
|
||||
xmltype=xsd_type))
|
||||
# read xml data
|
||||
invoice = xml_read_func(invoice, xmltree)
|
||||
return invoice
|
||||
|
||||
def _readxml_getvalue(
|
||||
self, xmltree, tags, vtype=None, allow_list=False,
|
||||
text_field=True):
|
||||
""" read 'text'-part from xml-xpath,
|
||||
convert to 'vtype'
|
||||
|
||||
Args:
|
||||
tags (list): list of tags to build xpath
|
||||
vtype (type-class, optional): to convert value. Defaults to None.
|
||||
allow_list (boolean): get result as list of values
|
||||
text_field (boolean): return content of 'text'-part of node
|
||||
|
||||
Returns:
|
||||
various: converted value or None
|
||||
"""
|
||||
result = []
|
||||
xpath = '/' + '/'.join(tags)
|
||||
nodes = xmltree.xpath(xpath, namespaces=xmltree.nsmap)
|
||||
if nodes:
|
||||
if not text_field:
|
||||
result.extend(nodes)
|
||||
else:
|
||||
for node1 in nodes:
|
||||
if node1.text:
|
||||
result.append(
|
||||
node1.text if vtype is None
|
||||
else vtype(node1.text))
|
||||
if not allow_list:
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
def _readxml_getattrib(self, xmltree, tags, attrib, vtype=None):
|
||||
""" read attribute from xml-xpath,
|
||||
convert to 'vtype'
|
||||
|
||||
Args:
|
||||
tags (list): list fo tags to build xpath
|
||||
attrib (str): name of attribute to read
|
||||
vtype (type-class, optional): to convert value. Defaults to None.
|
||||
|
||||
Returns:
|
||||
various: converted value or None
|
||||
"""
|
||||
result = None
|
||||
xpath = '/' + '/'.join(tags)
|
||||
nodes = xmltree.xpath(xpath, namespaces=xmltree.nsmap)
|
||||
if nodes:
|
||||
result = nodes[0].attrib.get(attrib, None)
|
||||
|
||||
if result and vtype:
|
||||
result = vtype(result)
|
||||
return result
|
||||
|
||||
def _readxml_facturx_extended(self, invoice, xmltree):
|
||||
""" read factur-x extended
|
||||
|
||||
Args:
|
||||
invoice (record): model account.invoice
|
||||
|
||||
Returns:
|
||||
record: model account.invoice
|
||||
"""
|
||||
# check invoice-type:
|
||||
# allowed codes 380 incoice, 381 credit note
|
||||
inv_code = self._readxml_getvalue(xmltree, [
|
||||
'rsm:CrossIndustryInvoice',
|
||||
'rsm:ExchangedDocument', 'ram:TypeCode'], int)
|
||||
if inv_code not in [380, 381]:
|
||||
raise UserError(gettext(
|
||||
'document_incoming_invoice_xml.msg_convert_error',
|
||||
msg='invalid type-code: %(code)s (expect: 380, 381)' % {
|
||||
'code': str(inv_code)}))
|
||||
|
||||
invoice.number = self._readxml_getvalue(xmltree, [
|
||||
'rsm:CrossIndustryInvoice',
|
||||
'rsm:ExchangedDocument', 'ram:ID'])
|
||||
|
||||
# invoice-date
|
||||
date_path = [
|
||||
'rsm:CrossIndustryInvoice', 'rsm:ExchangedDocument',
|
||||
'ram:IssueDateTime', 'udt:DateTimeString']
|
||||
date_format = self._readxml_getattrib(xmltree, date_path, 'format')
|
||||
if date_format != '102':
|
||||
raise UserError(gettext(
|
||||
'document_incoming_invoice_xml.msg_convert_error',
|
||||
msg='invalid date-format: %(code)s (expect: 102)' % {
|
||||
'code': str(date_format)}))
|
||||
invoice.invoice_date = self._readxml_convertdate(
|
||||
self._readxml_getvalue(xmltree, date_path))
|
||||
|
||||
# IncludedNote, 1st line --> 'description', 2nd ff. --> 'comment'
|
||||
note_list = self._readxml_getvalue(xmltree, [
|
||||
'rsm:CrossIndustryInvoice',
|
||||
'rsm:ExchangedDocument', 'ram:IncludedNote'],
|
||||
allow_list=True, text_field=False)
|
||||
print('\n## note_list:', (note_list,))
|
||||
if note_list:
|
||||
invoice.description = self._readxml_getvalue(
|
||||
note_list[0], ['ram:Content'], allow_list=False)
|
||||
if len(note_list) > 1:
|
||||
for x in note_list[1:]:
|
||||
print('- x:', x)
|
||||
cont_code = self._readxml_getvalue(
|
||||
x, ['ram:ContentCode'], allow_list=False)
|
||||
print('- cont_code:', cont_code)
|
||||
cont_str = self._readxml_getvalue(
|
||||
x, ['ram:Content'], allow_list=False)
|
||||
|
||||
# descr_list = self._readxml_getvalue(xmltree, [
|
||||
# 'rsm:CrossIndustryInvoice',
|
||||
# 'rsm:ExchangedDocument', 'ram:IncludedNote',
|
||||
# 'ram:Content'], allow_list=True)
|
||||
# if descr_list:
|
||||
# invoice.description = descr_list[0]
|
||||
# if len(descr_list) > 1:
|
||||
# invoice.comment = '\n'.join(descr_list[1:])
|
||||
return invoice
|
||||
|
||||
def _readxml_convertdate(self, date_string):
|
||||
""" convert date-string to python-date
|
||||
|
||||
Args:
|
||||
date_string (_type_): _description_
|
||||
"""
|
||||
if not date_string:
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(date_string, '%Y%m%d').date()
|
||||
except ValueError:
|
||||
raise UserError(gettext(
|
||||
'document_incoming_invoice_xml.msg_convert_error',
|
||||
msg='cannot convert %(val)s' % {
|
||||
'val': date_string}))
|
||||
|
||||
def _facturx_detect_content(self):
|
||||
""" check xml-data against xsd of XRechnung and FacturX,
|
||||
begin with extended, goto minimal, then xrechnung
|
||||
|
||||
Returns:
|
||||
tuple: ('xsd tyle', '<function to read xml>', <xml etree>)
|
||||
defaults to None if not detected
|
||||
"""
|
||||
xml_data = etree.fromstring(self.data)
|
||||
|
||||
for xsdpath, xsdtype, funcname in xml_types:
|
||||
fname = os.path.join(*[os.path.split(__file__)[0]] + xsdpath)
|
||||
schema = etree.XMLSchema(etree.parse(fname))
|
||||
try:
|
||||
schema.assertValid(xml_data)
|
||||
except etree.DocumentInvalid:
|
||||
pass
|
||||
return (xsdtype, funcname, xml_data)
|
||||
return None
|
||||
|
||||
# end Incoming
|
13
document.xml
Normal file
13
document.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the document-incoming-invoice-xml-module
|
||||
from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="document_incoming_view_form">
|
||||
<field name="model">document.incoming</field>
|
||||
<field name="inherit" ref="document_incoming.document_incoming_view_form"/>
|
||||
<field name="name">document_incoming_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
15
locale/de.po
Normal file
15
locale/de.po
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
|
||||
##############
|
||||
# ir.message #
|
||||
##############
|
||||
msgctxt "model:ir.message,text:msg_type_short_unique"
|
||||
msgid "XML import for %(xmltype)s not implemented."
|
||||
msgstr "XML import für %(xmltype)s nicht implementiert."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_convert_error"
|
||||
msgid "Conversion error: %(msg)s."
|
||||
msgstr "Konvertierfehler: %(msg)s."
|
12
locale/en.po
Normal file
12
locale/en.po
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_type_short_unique"
|
||||
msgid "XML import for %(xmltype)s not implemented."
|
||||
msgstr "XML import for %(xmltype)s not implemented."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_convert_error"
|
||||
msgid "Conversion error: %(msg)s."
|
||||
msgstr "Conversion error: %(msg)s."
|
||||
|
17
message.xml
Normal file
17
message.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the document-incoming-invoice-xml-module
|
||||
from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data grouped="1">
|
||||
|
||||
<record model="ir.message" id="msg_not_implemented">
|
||||
<field name="text">XML import for %(xmltype)s not implemented.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_convert_error">
|
||||
<field name="text">Conversion error: %(msg)s.</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</tryton>
|
3
setup.py
3
setup.py
|
@ -37,7 +37,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
|
|||
major_version = 7
|
||||
minor_version = 0
|
||||
|
||||
requires = ['pypdf', 'factur-x']
|
||||
requires = ['pypdf', 'factur-x', 'lxml']
|
||||
for dep in info.get('depends', []):
|
||||
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
|
||||
if dep in modversion.keys():
|
||||
|
@ -96,6 +96,7 @@ setup(
|
|||
'trytond.modules.%s' % MODULE: (
|
||||
info.get('xml', [])
|
||||
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
|
||||
'view/*.xml',
|
||||
'versiondep.txt', 'README.rst']),
|
||||
},
|
||||
|
||||
|
|
4
tests/__init__.py
Normal file
4
tests/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the document-incoming-invoice-xml-module
|
||||
# from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
71
tests/document.py
Normal file
71
tests/document.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the document-incoming-invoice-xml-module
|
||||
# from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import os.path
|
||||
from datetime import date
|
||||
from trytond.tests.test_tryton import with_transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.modules.company.tests import create_company, set_company
|
||||
|
||||
|
||||
class DocumentTestCase(object):
|
||||
""" check import of xml + pdf files
|
||||
"""
|
||||
def prep_incomingdoc_run_worker(self):
|
||||
""" run tasks from queue
|
||||
"""
|
||||
Queue = Pool().get('ir.queue')
|
||||
|
||||
while True:
|
||||
tasks = Queue.search([])
|
||||
if not tasks:
|
||||
break
|
||||
for task in tasks:
|
||||
task.run()
|
||||
Queue.delete(tasks)
|
||||
|
||||
@with_transaction()
|
||||
def test_xmldoc_import_facturx(self):
|
||||
""" create incoming-document, load xml, detect type
|
||||
"""
|
||||
pool = Pool()
|
||||
IncDocument = pool.get('document.incoming')
|
||||
|
||||
company = create_company('m-ds')
|
||||
with set_company(company):
|
||||
|
||||
to_create = []
|
||||
with open(os.path.join(
|
||||
os.path.split(__file__)[0],
|
||||
'facturx-extended.xml'), 'rb') as fhdl:
|
||||
to_create.append({
|
||||
'data': fhdl.read(),
|
||||
'name': 'facturx-extended.xml',
|
||||
'type': 'supplier_invoice'})
|
||||
|
||||
document, = IncDocument.create(to_create)
|
||||
self.assertEqual(document.mime_type, 'application/xml')
|
||||
self.assertEqual(document.company.id, company.id)
|
||||
self.assertTrue(document.data.startswith(
|
||||
b'<?xml version="1.0" encoding="UTF-8"?>\n' +
|
||||
b'<rsm:CrossIndustryInvoice xmlns'))
|
||||
|
||||
invoice = document._process_supplier_invoice()
|
||||
print('\n## invoice:', (invoice,))
|
||||
self.assertEqual(invoice.type, 'in')
|
||||
self.assertEqual(invoice.number, 'RE2024.01234')
|
||||
self.assertEqual(invoice.invoice_date, date(2024, 6, 17))
|
||||
self.assertEqual(invoice.currency.name, 'usd')
|
||||
self.assertEqual(invoice.company.rec_name, 'm-ds')
|
||||
invoice.save()
|
||||
|
||||
print('\n## invoice:', invoice)
|
||||
|
||||
# 'process' will queue the job to workers
|
||||
IncDocument.process([document])
|
||||
# run the usual call: process workers
|
||||
self.prep_incomingdoc_run_worker()
|
||||
|
||||
# end DocumentTestCase
|
122
tests/facturx-extended.xml
Normal file
122
tests/facturx-extended.xml
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<rsm:ExchangedDocumentContext>
|
||||
<ram:GuidelineSpecifiedDocumentContextParameter>
|
||||
<ram:ID>urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended</ram:ID>
|
||||
</ram:GuidelineSpecifiedDocumentContextParameter>
|
||||
</rsm:ExchangedDocumentContext>
|
||||
<rsm:ExchangedDocument>
|
||||
<ram:ID>RE2024.01234</ram:ID>
|
||||
<ram:TypeCode>380</ram:TypeCode>
|
||||
<ram:IssueDateTime>
|
||||
<udt:DateTimeString format="102">20240617</udt:DateTimeString>
|
||||
</ram:IssueDateTime>
|
||||
<ram:IncludedNote>
|
||||
<ram:Content>Description of invoice</ram:Content>
|
||||
</ram:IncludedNote>
|
||||
<ram:IncludedNote>
|
||||
<ram:Content>Some notes to the customer.</ram:Content>
|
||||
<ram:ContentCode>1</ram:ContentCode>
|
||||
</ram:IncludedNote>
|
||||
<ram:IncludedNote>
|
||||
<ram:Content>Goes to field comment.</ram:Content>
|
||||
<ram:ContentCode>22</ram:ContentCode>
|
||||
</ram:IncludedNote>
|
||||
</rsm:ExchangedDocument>
|
||||
<rsm:SupplyChainTradeTransaction>
|
||||
<ram:IncludedSupplyChainTradeLineItem>
|
||||
<ram:AssociatedDocumentLineDocument>
|
||||
<ram:LineID>1</ram:LineID>
|
||||
</ram:AssociatedDocumentLineDocument>
|
||||
<ram:SpecifiedTradeProduct>
|
||||
<ram:Name>Name of Product 1</ram:Name>
|
||||
<ram:Description>Description of Product 1</ram:Description>
|
||||
</ram:SpecifiedTradeProduct>
|
||||
<ram:SpecifiedLineTradeAgreement>
|
||||
<ram:NetPriceProductTradePrice>
|
||||
<ram:ChargeAmount currencyID="EUR">1350.00</ram:ChargeAmount>
|
||||
</ram:NetPriceProductTradePrice>
|
||||
</ram:SpecifiedLineTradeAgreement>
|
||||
<ram:SpecifiedLineTradeDelivery>
|
||||
<ram:BilledQuantity unitCode="C62">1.0</ram:BilledQuantity>
|
||||
</ram:SpecifiedLineTradeDelivery>
|
||||
<ram:SpecifiedLineTradeSettlement>
|
||||
<ram:ApplicableTradeTax>
|
||||
<ram:TypeCode>VAT</ram:TypeCode>
|
||||
<ram:CategoryCode>S</ram:CategoryCode>
|
||||
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
|
||||
</ram:ApplicableTradeTax>
|
||||
<ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
<ram:LineTotalAmount currencyID="EUR">1350.00</ram:LineTotalAmount>
|
||||
</ram:SpecifiedTradeSettlementLineMonetarySummation>
|
||||
</ram:SpecifiedLineTradeSettlement>
|
||||
</ram:IncludedSupplyChainTradeLineItem>
|
||||
<ram:ApplicableHeaderTradeAgreement>
|
||||
<ram:SellerTradeParty>
|
||||
<ram:Name>Name of the Comany</ram:Name>
|
||||
<ram:SpecifiedLegalOrganization>
|
||||
</ram:SpecifiedLegalOrganization>
|
||||
<ram:PostalTradeAddress>
|
||||
<ram:PostcodeCode>12345</ram:PostcodeCode>
|
||||
<ram:LineOne>Street of Company No 1</ram:LineOne>
|
||||
<ram:CityName>Berlin</ram:CityName>
|
||||
<ram:CountryID>DE</ram:CountryID>
|
||||
<ram:CountrySubDivisionName>Berlin</ram:CountrySubDivisionName>
|
||||
</ram:PostalTradeAddress>
|
||||
</ram:SellerTradeParty>
|
||||
<ram:BuyerTradeParty>
|
||||
<ram:Name>Customer Company</ram:Name>
|
||||
<ram:SpecifiedLegalOrganization>
|
||||
</ram:SpecifiedLegalOrganization>
|
||||
<ram:PostalTradeAddress>
|
||||
<ram:PostcodeCode>23456</ram:PostcodeCode>
|
||||
<ram:LineOne>Address Line 1</ram:LineOne>
|
||||
<ram:LineTwo>Address Line 2</ram:LineTwo>
|
||||
<ram:CityName>Potsdam</ram:CityName>
|
||||
<ram:CountryID>DE</ram:CountryID>
|
||||
<ram:CountrySubDivisionName>Brandenburg</ram:CountrySubDivisionName>
|
||||
</ram:PostalTradeAddress>
|
||||
</ram:BuyerTradeParty>
|
||||
<ram:BuyerOrderReferencedDocument>
|
||||
<ram:IssuerAssignedID/>
|
||||
</ram:BuyerOrderReferencedDocument>
|
||||
</ram:ApplicableHeaderTradeAgreement>
|
||||
<ram:ApplicableHeaderTradeDelivery>
|
||||
</ram:ApplicableHeaderTradeDelivery>
|
||||
<ram:ApplicableHeaderTradeSettlement>
|
||||
<ram:PaymentReference>RE2024.01234</ram:PaymentReference>
|
||||
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
|
||||
<ram:SpecifiedTradeSettlementPaymentMeans>
|
||||
<ram:TypeCode>30</ram:TypeCode>
|
||||
<ram:Information>Wire transfer</ram:Information>
|
||||
<ram:PayeePartyCreditorFinancialAccount>
|
||||
<ram:IBANID>DE02300209000106531065</ram:IBANID>
|
||||
<ram:AccountName>mbs</ram:AccountName>
|
||||
</ram:PayeePartyCreditorFinancialAccount>
|
||||
<ram:PayeeSpecifiedCreditorFinancialInstitution>
|
||||
<ram:BICID>WELADED1PMB</ram:BICID>
|
||||
</ram:PayeeSpecifiedCreditorFinancialInstitution>
|
||||
</ram:SpecifiedTradeSettlementPaymentMeans>
|
||||
<ram:ApplicableTradeTax>
|
||||
<ram:CalculatedAmount currencyID="EUR">256.5</ram:CalculatedAmount>
|
||||
<ram:TypeCode>VAT</ram:TypeCode>
|
||||
<ram:BasisAmount>1350</ram:BasisAmount>
|
||||
<ram:CategoryCode>S</ram:CategoryCode>
|
||||
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
|
||||
</ram:ApplicableTradeTax>
|
||||
<ram:SpecifiedTradePaymentTerms>
|
||||
<ram:DueDateDateTime>
|
||||
<udt:DateTimeString format="102">20240701</udt:DateTimeString>
|
||||
</ram:DueDateDateTime>
|
||||
<ram:PartialPaymentAmount currencyID="EUR">1606.50</ram:PartialPaymentAmount>
|
||||
</ram:SpecifiedTradePaymentTerms>
|
||||
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
<ram:LineTotalAmount currencyID="EUR">1350.00</ram:LineTotalAmount>
|
||||
<ram:TaxBasisTotalAmount currencyID="EUR">1350.00</ram:TaxBasisTotalAmount>
|
||||
<ram:TaxTotalAmount currencyID="EUR">256.5</ram:TaxTotalAmount>
|
||||
<ram:GrandTotalAmount currencyID="EUR">1606.50</ram:GrandTotalAmount>
|
||||
<ram:DuePayableAmount currencyID="EUR">1606.50</ram:DuePayableAmount>
|
||||
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
||||
</ram:ApplicableHeaderTradeSettlement>
|
||||
</rsm:SupplyChainTradeTransaction>
|
||||
</rsm:CrossIndustryInvoice>
|
20
tests/test_module.py
Normal file
20
tests/test_module.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the document-incoming-invoice-xml-module
|
||||
# from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
from .document import DocumentTestCase
|
||||
|
||||
|
||||
class XmlIncomingTestCase(
|
||||
DocumentTestCase,
|
||||
ModuleTestCase):
|
||||
'Test document incoming xml converter module'
|
||||
module = 'document_incoming_invoice_xml'
|
||||
|
||||
# end XmlIncomingTestCase
|
||||
|
||||
|
||||
del ModuleTestCase
|
|
@ -3,3 +3,5 @@ version=7.0.0
|
|||
depends:
|
||||
document_incoming_invoice
|
||||
xml:
|
||||
message.xml
|
||||
document.xml
|
||||
|
|
12
view/document_incoming_form.xml
Normal file
12
view/document_incoming_form.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the document-incoming-invoice-xml-module
|
||||
from m-ds for Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
|
||||
<xpath expr="/form/field[@name='type']" position="after">
|
||||
<label name="xsd_type"/>
|
||||
<field name="xsd_type"/>
|
||||
</xpath>
|
||||
|
||||
</data>
|
Loading…
Reference in a new issue