diff --git a/edocument.py b/edocument.py
index 2dcfd1d..436d40e 100644
--- a/edocument.py
+++ b/edocument.py
@@ -123,7 +123,7 @@ class Invoice(Invoice):
def _get_template(self, version):
""" load our own template if 'version' is ours
"""
- if version == 'XRechnung-2.2':
+ if version in ['XRechnung-2.2', 'XRechnung-2.3', 'XRechnung-3.0']:
loader = genshi.template.TemplateLoader(
os.path.join(os.path.dirname(__file__), 'template'),
auto_reload=True)
diff --git a/template/XRechnung-2.2/XRechnung_invoice.xml b/template/XRechnung-2.2/XRechnung_invoice.xml
index a1de5bf..396cf24 100644
--- a/template/XRechnung-2.2/XRechnung_invoice.xml
+++ b/template/XRechnung-2.2/XRechnung_invoice.xml
@@ -90,7 +90,7 @@
urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.1
${this.invoice.number}
${this.invoice.invoice_date.isoformat()}
- ${(getattr(this.invoice, 'payment_term_date', None) or this.invoice.invoice_date).isoformat()}
+ ${(this.invoice.payment_term_date or this.invoice.invoice_date).isoformat()}
${this.type_code}
${this.invoice_note()}
${this.invoice.currency.code}
diff --git a/template/XRechnung-2.3/XRechnung_credit.xml b/template/XRechnung-2.3/XRechnung_credit.xml
new file mode 100644
index 0000000..b487804
--- /dev/null
+++ b/template/XRechnung-2.3/XRechnung_credit.xml
@@ -0,0 +1,182 @@
+
+
+
+
+ ${', '.join((getattr(value, 'street', None) or '').split('\n'))}
+ ${getattr(value, 'city', None) or ''}
+ ${getattr(value, 'postal_code', None) or ''}
+
+ ${getattr(getattr(value, 'country', None), 'code', None)}
+
+
+
+
+ ${this.taxident_data(value)['code']}
+
+ ${this.taxident_data(value)['id']}
+
+
+
+
+ ${value.name}
+
+ ${value.phone}
+
+
+ ${value.email}
+
+
+
+
+ ${value.name}
+ ${this.taxident_data(this.seller_trade_tax_identifier)['code']}
+
+
+
+
+ ${value.bank_accounts[0].numbers[0].number_compact}
+ ${value.name}
+
+
+
+
+ ${this.tax_category_code(value)}
+ ${this.tax_rate(value)}
+ ${value.legal_notice or '-'}
+
+ VAT
+
+
+
+
+
+ ${this.negate_amount(value.base)}
+ ${this.negate_amount(value.amount)}
+
+ ${TaxCategory(value.tax, True)}
+
+
+
+
+
+
+ ${value.id}
+ ${this.negate_amount(value.quantity)}
+ ${this.negate_amount(value.amount)}
+
+ ${value.description or getattr(value.product, 'name', None)}
+
+ ${value.product.code}
+
+
+ ${TaxCategory(this.invoice_line_tax(value))}
+
+
+
+ ${value.unit_price}
+
+
+
+
+ urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3
+ ${this.invoice.number}
+ ${this.invoice.invoice_date.isoformat()}
+ ${this.type_code}
+ ${this.invoice_note()}
+ ${this.invoice.currency.code}
+ ${this.invoice.party.get_xrechnung_route_id()}
+
+ ${this.invoice.reference}
+ ${this.sales_order_nums()}
+
+
+ vertrags-nr
+
+
+ proj-referenz
+
+
+
+
+ ${PostalAddress(this.seller_trade_address)}
+
+
+ ${PartyTaxScheme(this.seller_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.seller_trade_party)}
+
+
+ ${Contact(this.seller_trade_party)}
+
+
+
+
+
+
+ ${this.buyer_trade_party.code}
+
+
+ ${PostalAddress(this.buyer_trade_address)}
+
+
+ ${PartyTaxScheme(this.buyer_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.buyer_trade_party, False)}
+
+
+ ${Contact(this.buyer_trade_party)}
+
+
+
+
+ 30
+ ${this.payment_reference}
+
+ ${PayeeFinancialAccount(this.seller_trade_party)}
+
+
+
+ ${this.invoice.payment_term.rec_name}
+
+
+ false
+ Neukundenrabatt
+ 5.00
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ ${this.negate_amount(this.invoice.tax_amount)}
+
+ ${TaxSubTotal(taxline)}
+
+
+
+ ${this.negate_amount(this.invoice.untaxed_amount)}
+ ${this.negate_amount(this.invoice.untaxed_amount)}
+ ${this.negate_amount(this.invoice.total_amount)}
+ 0.00
+ 0.00
+ ${this.negate_amount(this.prepaid_amount(this.invoice))}
+ ${this.negate_amount(this.invoice.amount_to_pay)}
+
+
+ ${CreditNoteLine(line)}
+
+
diff --git a/template/XRechnung-2.3/XRechnung_invoice.xml b/template/XRechnung-2.3/XRechnung_invoice.xml
new file mode 100644
index 0000000..f3599a1
--- /dev/null
+++ b/template/XRechnung-2.3/XRechnung_invoice.xml
@@ -0,0 +1,183 @@
+
+
+
+
+ ${', '.join((getattr(value, 'street', None) or '').split('\n'))}
+ ${getattr(value, 'city', None) or ''}
+ ${getattr(value, 'postal_code', None) or ''}
+
+ ${getattr(getattr(value, 'country', None), 'code', None)}
+
+
+
+
+ ${this.taxident_data(value)['code']}
+
+ ${this.taxident_data(value)['id']}
+
+
+
+
+ ${value.name}
+
+ ${value.phone}
+
+
+ ${value.email}
+
+
+
+
+ ${value.name}
+ ${this.taxident_data(this.seller_trade_tax_identifier)['code']}
+
+
+
+
+ ${value.bank_accounts[0].numbers[0].number_compact}
+ ${value.name}
+
+
+
+
+ ${this.tax_category_code(value)}
+ ${this.tax_rate(value)}
+ ${value.legal_notice or '-'}
+
+ VAT
+
+
+
+
+
+ ${value.base}
+ ${value.amount}
+
+ ${TaxCategory(value.tax, True)}
+
+
+
+
+
+
+ ${value.id}
+ ${value.quantity}
+ ${value.amount}
+
+ ${value.description or getattr(value.product, 'name', None)}
+
+ ${value.product.code}
+
+
+ ${TaxCategory(this.invoice_line_tax(value))}
+
+
+
+ ${value.unit_price}
+
+
+
+
+ urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3
+ ${this.invoice.number}
+ ${this.invoice.invoice_date.isoformat()}
+ ${(this.invoice.payment_term_date or this.invoice.invoice_date).isoformat()}
+ ${this.type_code}
+ ${this.invoice_note()}
+ ${this.invoice.currency.code}
+ ${this.invoice.party.get_xrechnung_route_id()}
+
+ ${this.invoice.reference}
+ ${this.sales_order_nums()}
+
+
+ vertrags-nr
+
+
+ proj-referenz
+
+
+
+
+ ${PostalAddress(this.seller_trade_address)}
+
+
+ ${PartyTaxScheme(this.seller_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.seller_trade_party)}
+
+
+ ${Contact(this.seller_trade_party)}
+
+
+
+
+
+
+ ${this.buyer_trade_party.code}
+
+
+ ${PostalAddress(this.buyer_trade_address)}
+
+
+ ${PartyTaxScheme(this.buyer_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.buyer_trade_party, False)}
+
+
+ ${Contact(this.buyer_trade_party)}
+
+
+
+
+ 30
+ ${this.payment_reference}
+
+ ${PayeeFinancialAccount(this.seller_trade_party)}
+
+
+
+ ${this.invoice.payment_term.rec_name}
+
+
+ false
+ Neukundenrabatt
+ 5.00
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ ${this.invoice.tax_amount}
+
+ ${TaxSubTotal(taxline)}
+
+
+
+ ${this.invoice.untaxed_amount}
+ ${this.invoice.untaxed_amount}
+ ${this.invoice.total_amount}
+ 0.00
+ 0.00
+ ${this.prepaid_amount(this.invoice)}
+ ${this.invoice.amount_to_pay}
+
+
+ ${InvoiceLine(line)}
+
+
diff --git a/template/XRechnung-3.0/XRechnung_credit.xml b/template/XRechnung-3.0/XRechnung_credit.xml
new file mode 100644
index 0000000..6c3db35
--- /dev/null
+++ b/template/XRechnung-3.0/XRechnung_credit.xml
@@ -0,0 +1,182 @@
+
+
+
+
+ ${', '.join((getattr(value, 'street', None) or '').split('\n'))}
+ ${getattr(value, 'city', None) or ''}
+ ${getattr(value, 'postal_code', None) or ''}
+
+ ${getattr(getattr(value, 'country', None), 'code', None)}
+
+
+
+
+ ${this.taxident_data(value)['code']}
+
+ ${this.taxident_data(value)['id']}
+
+
+
+
+ ${value.name}
+
+ ${value.phone}
+
+
+ ${value.email}
+
+
+
+
+ ${value.name}
+ ${this.taxident_data(this.seller_trade_tax_identifier)['code']}
+
+
+
+
+ ${value.bank_accounts[0].numbers[0].number_compact}
+ ${value.name}
+
+
+
+
+ ${this.tax_category_code(value)}
+ ${this.tax_rate(value)}
+ ${value.legal_notice or '-'}
+
+ VAT
+
+
+
+
+
+ ${this.negate_amount(value.base)}
+ ${this.negate_amount(value.amount)}
+
+ ${TaxCategory(value.tax, True)}
+
+
+
+
+
+
+ ${value.id}
+ ${this.negate_amount(value.quantity)}
+ ${this.negate_amount(value.amount)}
+
+ ${value.description or getattr(value.product, 'name', None)}
+
+ ${value.product.code}
+
+
+ ${TaxCategory(this.invoice_line_tax(value))}
+
+
+
+ ${value.unit_price}
+
+
+
+
+ urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0
+ ${this.invoice.number}
+ ${this.invoice.invoice_date.isoformat()}
+ ${this.type_code}
+ ${this.invoice_note()}
+ ${this.invoice.currency.code}
+ ${this.invoice.party.get_xrechnung_route_id()}
+
+ ${this.invoice.reference}
+ ${this.sales_order_nums()}
+
+
+ vertrags-nr
+
+
+ proj-referenz
+
+
+
+
+ ${PostalAddress(this.seller_trade_address)}
+
+
+ ${PartyTaxScheme(this.seller_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.seller_trade_party)}
+
+
+ ${Contact(this.seller_trade_party)}
+
+
+
+
+
+
+ ${this.buyer_trade_party.code}
+
+
+ ${PostalAddress(this.buyer_trade_address)}
+
+
+ ${PartyTaxScheme(this.buyer_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.buyer_trade_party, False)}
+
+
+ ${Contact(this.buyer_trade_party)}
+
+
+
+
+ 30
+ ${this.payment_reference}
+
+ ${PayeeFinancialAccount(this.seller_trade_party)}
+
+
+
+ ${this.invoice.payment_term.rec_name}
+
+
+ false
+ Neukundenrabatt
+ 5.00
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ ${this.negate_amount(this.invoice.tax_amount)}
+
+ ${TaxSubTotal(taxline)}
+
+
+
+ ${this.negate_amount(this.invoice.untaxed_amount)}
+ ${this.negate_amount(this.invoice.untaxed_amount)}
+ ${this.negate_amount(this.invoice.total_amount)}
+ 0.00
+ 0.00
+ ${this.negate_amount(this.prepaid_amount(this.invoice))}
+ ${this.negate_amount(this.invoice.amount_to_pay)}
+
+
+ ${CreditNoteLine(line)}
+
+
diff --git a/template/XRechnung-3.0/XRechnung_invoice.xml b/template/XRechnung-3.0/XRechnung_invoice.xml
new file mode 100644
index 0000000..c25d170
--- /dev/null
+++ b/template/XRechnung-3.0/XRechnung_invoice.xml
@@ -0,0 +1,183 @@
+
+
+
+
+ ${', '.join((getattr(value, 'street', None) or '').split('\n'))}
+ ${getattr(value, 'city', None) or ''}
+ ${getattr(value, 'postal_code', None) or ''}
+
+ ${getattr(getattr(value, 'country', None), 'code', None)}
+
+
+
+
+ ${this.taxident_data(value)['code']}
+
+ ${this.taxident_data(value)['id']}
+
+
+
+
+ ${value.name}
+
+ ${value.phone}
+
+
+ ${value.email}
+
+
+
+
+ ${value.name}
+ ${this.taxident_data(this.seller_trade_tax_identifier)['code']}
+
+
+
+
+ ${value.bank_accounts[0].numbers[0].number_compact}
+ ${value.name}
+
+
+
+
+ ${this.tax_category_code(value)}
+ ${this.tax_rate(value)}
+ ${value.legal_notice or '-'}
+
+ VAT
+
+
+
+
+
+ ${value.base}
+ ${value.amount}
+
+ ${TaxCategory(value.tax, True)}
+
+
+
+
+
+
+ ${value.id}
+ ${value.quantity}
+ ${value.amount}
+
+ ${value.description or getattr(value.product, 'name', None)}
+
+ ${value.product.code}
+
+
+ ${TaxCategory(this.invoice_line_tax(value))}
+
+
+
+ ${value.unit_price}
+
+
+
+
+ urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0
+ ${this.invoice.number}
+ ${this.invoice.invoice_date.isoformat()}
+ ${(this.invoice.payment_term_date or this.invoice.invoice_date).isoformat()}
+ ${this.type_code}
+ ${this.invoice_note()}
+ ${this.invoice.currency.code}
+ ${this.invoice.party.get_xrechnung_route_id()}
+
+ ${this.invoice.reference}
+ ${this.sales_order_nums()}
+
+
+ vertrags-nr
+
+
+ proj-referenz
+
+
+
+
+ ${PostalAddress(this.seller_trade_address)}
+
+
+ ${PartyTaxScheme(this.seller_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.seller_trade_party)}
+
+
+ ${Contact(this.seller_trade_party)}
+
+
+
+
+
+
+ ${this.buyer_trade_party.code}
+
+
+ ${PostalAddress(this.buyer_trade_address)}
+
+
+ ${PartyTaxScheme(this.buyer_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.buyer_trade_party, False)}
+
+
+ ${Contact(this.buyer_trade_party)}
+
+
+
+
+ 30
+ ${this.payment_reference}
+
+ ${PayeeFinancialAccount(this.seller_trade_party)}
+
+
+
+ ${this.invoice.payment_term.rec_name}
+
+
+ false
+ Neukundenrabatt
+ 5.00
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ ${this.invoice.tax_amount}
+
+ ${TaxSubTotal(taxline)}
+
+
+
+ ${this.invoice.untaxed_amount}
+ ${this.invoice.untaxed_amount}
+ ${this.invoice.total_amount}
+ 0.00
+ 0.00
+ ${this.prepaid_amount(this.invoice)}
+ ${this.invoice.amount_to_pay}
+
+
+ ${InvoiceLine(line)}
+
+
diff --git a/tests/test_edocument.py b/tests/test_edocument.py
index 2b223e4..02648aa 100644
--- a/tests/test_edocument.py
+++ b/tests/test_edocument.py
@@ -3,11 +3,14 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
+from lxml import etree
+import os
+from unittest.mock import Mock
+from decimal import Decimal
+from datetime import date
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.modules.edocument_uncefact.tests.test_module import get_invoice
-from unittest.mock import Mock
-from decimal import Decimal
class EdocTestCase(ModuleTestCase):
@@ -27,6 +30,7 @@ class EdocTestCase(ModuleTestCase):
BankNumber = pool.get('bank.account.number')
invoice = get_invoice()
+ invoice.payment_term_date = date.today()
invoice.party.get_xrechnung_route_id = Mock(
return_value='xrechn-route-id-123')
invoice.company.party.bank_accounts = [
@@ -48,9 +52,16 @@ class EdocTestCase(ModuleTestCase):
]
template = Template(invoice)
- invoice_string = template.render('XRechnung-2.2')
- with open('xrechnung-test-invoice.xml', 'wt') as fhdl:
- fhdl.write(invoice_string.decode('utf8'))
+
+ schema_file = os.path.join(
+ os.path.dirname(__file__), 'os-UBL-2.1',
+ 'xsd', 'maindoc', 'UBL-Invoice-2.1.xsd')
+
+ for x in ['XRechnung-2.2', 'XRechnung-2.3', 'XRechnung-3.0']:
+ invoice_string = template.render(x)
+ invoice_xml = etree.fromstring(invoice_string)
+ schema = etree.XMLSchema(etree.parse(schema_file))
+ schema.assertValid(invoice_xml)
@with_transaction()
def test_xrechn_export_xml_creditnote(self):
@@ -97,9 +108,20 @@ class EdocTestCase(ModuleTestCase):
]
template = Template(invoice)
- invoice_string = template.render('XRechnung-2.2')
- with open('xrechnung-test-creditnote.xml', 'wt') as fhdl:
- fhdl.write(invoice_string.decode('utf8'))
+
+ schema_file = os.path.join(
+ os.path.dirname(__file__), 'os-UBL-2.1',
+ 'xsd', 'maindoc', 'UBL-CreditNote-2.1.xsd')
+
+ for x in ['XRechnung-2.2', 'XRechnung-2.3', 'XRechnung-3.0']:
+ invoice_string = template.render(x)
+ invoice_xml = etree.fromstring(invoice_string)
+ schema = etree.XMLSchema(etree.parse(schema_file))
+ schema.assertValid(invoice_xml)
+
+ # invoice_string = template.render('XRechnung-2.2')
+ # with open('xrechnung-test-creditnote.xml', 'wt') as fhdl:
+ # fhdl.write(invoice_string.decode('utf8'))
# end EdocTestCase