diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..154f62a
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,6 @@
+syntax: glob
+build/*
+dist/*
+mds_edocument_xrechnung.egg-info/*
+__pycache__/*
+locale/convert_de2en.py
diff --git a/__init__.py b/__init__.py
index dda7be0..84ce866 100644
--- a/__init__.py
+++ b/__init__.py
@@ -4,7 +4,13 @@
# full copyright notices and license terms.
from trytond.pool import Pool
+from .edocument import Invoice
+from .party import PartyConfiguration, Party
+
def register():
Pool.register(
+ Invoice,
+ Party,
+ PartyConfiguration,
module='edocument_xrechnung', type_='model')
diff --git a/edocument.py b/edocument.py
new file mode 100644
index 0000000..57c77c6
--- /dev/null
+++ b/edocument.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# This file is part of the edcoment-module for Tryton.
+# The COPYRIGHT file at the top level of this repository contains the
+# full copyright notices and license terms.
+
+import genshi.template
+import os
+from trytond.modules.edocument_uncefact.edocument import Invoice
+
+
+class Invoice(Invoice):
+ 'EDocument XRechnung'
+ __name__ = 'edocument.xrechnung.invoice'
+
+ def _get_template(self, version):
+ """ load our own template if 'version' is ours
+ """
+ if version == 'XRechnung-2.2':
+ loader = genshi.template.TemplateLoader(
+ os.path.join(os.path.dirname(__file__), 'template'),
+ auto_reload=True)
+ return loader.load(os.path.join(version, 'XRechnung.xml'))
+ else :
+ return super(Invoice, self)._get_template(version)
+
+# end Invoice
diff --git a/locale/de.po b/locale/de.po
new file mode 100644
index 0000000..c06b9e6
--- /dev/null
+++ b/locale/de.po
@@ -0,0 +1,20 @@
+#
+msgid ""
+msgstr "Content-Type: text/plain; charset=utf-8\n"
+
+
+##############
+# ir.message #
+##############
+msgctxt "model:ir.message,text:msg_missing_xrechnung_route_id"
+msgid "No XRechnung routing ID is stored for the party '%(partyname)s'."
+msgstr "Für die Partei '%(partyname)s' ist keine XRechnung-Leitweg-ID hinterlegt."
+
+
+#######################
+# party.configuration #
+#######################
+msgctxt "selection:party.configuration,identifier_types:"
+msgid "X-Rechnung Route-ID"
+msgstr "X-Rechnung Leitweg-ID"
+
diff --git a/locale/en.po b/locale/en.po
new file mode 100644
index 0000000..e074035
--- /dev/null
+++ b/locale/en.po
@@ -0,0 +1,8 @@
+#
+msgid ""
+msgstr "Content-Type: text/plain; charset=utf-8\n"
+
+msgctxt "selection:party.configuration,identifier_types:"
+msgid "X-Rechnung Route-ID"
+msgstr "X-Rechnung Route-ID"
+
diff --git a/message.xml b/message.xml
new file mode 100644
index 0000000..7d38804
--- /dev/null
+++ b/message.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ No XRechnung routing ID is stored for the party '%(partyname)s'.
+
+
+
diff --git a/party.py b/party.py
new file mode 100644
index 0000000..7079640
--- /dev/null
+++ b/party.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# This file is part of the edcoment-module for Tryton.
+# The COPYRIGHT file at the top level of this repository contains the
+# full copyright notices and license terms.
+
+from trytond.pool import PoolMeta
+from trytond.exceptions import UserError
+from trytond.i18n import gettext
+
+
+class Party(metaclass=PoolMeta):
+ __name__ = 'party.party'
+
+ def get_xrechnung_route_id(self):
+ """ search for route-id at party, fire-exception if missing
+ """
+ for ident in self.identifiers:
+ if ident.type == 'edoc_route_id':
+ return ident.code
+ raise UserError(gettext(
+ 'edocument_xrechnung.msg_missing_xrechnung_route_id',
+ partyname = self.rec_name,
+ ))
+# end Party
+
+
+class PartyConfiguration(metaclass=PoolMeta):
+ __name__ = 'party.configuration'
+
+ @classmethod
+ def __setup__(cls):
+ super(PartyConfiguration, cls).__setup__()
+ cls.identifier_types.selection.append(
+ ('edoc_route_id', 'X-Rechnung Route-ID')
+ )
+
+# end Configuration
diff --git a/setup.py b/setup.py
index a986302..fa1f0fa 100644
--- a/setup.py
+++ b/setup.py
@@ -97,7 +97,7 @@ setup(name='%s_%s' % (PREFIX, MODULE),
package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
- 'template/*.xml','versiondep.txt', 'README.rst']),
+ 'template/*/*.xml','versiondep.txt', 'README.rst']),
},
install_requires=requires,
diff --git a/template/XRechnung-2.2/XRechnung.xml b/template/XRechnung-2.2/XRechnung.xml
new file mode 100644
index 0000000..d43aa45
--- /dev/null
+++ b/template/XRechnung-2.2/XRechnung.xml
@@ -0,0 +1,176 @@
+
+
+
+
+ ${', '.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)}
+
+
+
+ ${getattr(value, 'code', None)}
+
+ VAT
+
+
+
+ ${value.name}
+ ${value.phone}
+ ${value.email}
+
+
+ ${value.name}
+ ${getattr(value.tax_identifier, 'code', None)}
+
+
+ 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.type_code}
+ ${this.invoice.currency.code}
+ ${this.invoice.party.get_xrechnung_route_id()}
+
+ bestell-nr
+ auftrags-nr
+
+
+ vertrags-nr
+
+
+ proj-referenz
+
+
+
+
+ ${PostalAddress(this.seller_trade_address)}
+
+
+ ${PartyTaxScheme(this.seller_trade_tax_identifier)}
+
+
+ ${PartyLegalEntity(this.invoice.company.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}
+
+ DE00 1234 5678 0000 1234 56
+ ${this.invoice.company.party.name}
+
+
+
+ ${this.invoice.payment_term.rec_name}
+
+
+ false
+ Neukundenrabatt
+ 5.00
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ ${this.invoice.total_amount}
+
+ 35.25
+ 6.70
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+
+ 40.25
+ 35.25
+ 41.95
+ 5.00
+ 0.00
+ 0.00
+ 41.95
+
+
+ 1
+ 0.5
+ 35.00
+
+ Reparaturdienstleistung in Stunden
+
+ REP-012
+
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ 70.00
+
+
+
+ 2
+ 3
+ 5.25
+
+ Material
+
+ MAT-987
+
+
+ S
+ 19.00
+
+ VAT
+
+
+
+
+ 1.75
+
+
+
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..173e725
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,23 @@
+# This file is part of Tryton. The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+import trytond.tests.test_tryton
+import unittest
+
+from trytond.modules.edocument_xrechnung.tests.test_edocument import EdocTestCase
+
+__all__ = ['suite']
+
+
+class EDocumentTestCase(\
+ EdocTestCase,\
+ ):
+ 'Test edocument module'
+ module = 'edocument_xrechnung'
+
+# end EDocumentTestCase
+
+def suite():
+ suite = trytond.tests.test_tryton.suite()
+ suite.addTests(unittest.TestLoader().loadTestsFromTestCase(EDocumentTestCase))
+ return suite
diff --git a/tests/test_edocument.py b/tests/test_edocument.py
new file mode 100644
index 0000000..fcae41f
--- /dev/null
+++ b/tests/test_edocument.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# This file is part of the cashbook-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, with_transaction
+from trytond.pool import Pool
+from trytond.modules.edocument_uncefact.tests.test_edocument_uncefact import get_invoice
+from unittest.mock import Mock, MagicMock
+
+
+class EdocTestCase(ModuleTestCase):
+ 'Test e-rechnung module'
+ module = 'edocument_xrechnung'
+
+ @with_transaction()
+ def test_xrechn_export_xml(self):
+ """ run export
+ """
+ pool = Pool()
+ Template = pool.get('edocument.xrechnung.invoice')
+ Address = pool.get('party.address')
+ Identifier = pool.get('party.identifier')
+
+ invoice = get_invoice()
+ invoice.party.get_xrechnung_route_id = Mock(return_value='xrechn-route-id-123')
+ invoice.identifiers = [
+ Mock(spec=Identifier,
+ type='edoc_route_id',
+ code='xrechn-route-id-123')
+ ]
+
+ template = Template(invoice)
+ invoice_string = template.render('XRechnung-2.2')
+ with open('xrechnung-test.xml', 'wt') as fhdl:
+ fhdl.write(invoice_string.decode('utf8'))
+
+# end EdocTestCase
diff --git a/tryton.cfg b/tryton.cfg
index 913582b..fec7d8f 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -2,4 +2,8 @@
version=6.0.0
depends:
edocument_uncefact
+ party
+#extras_depend:
+ account_invoice
xml:
+ message.xml