diff --git a/__init__.py b/__init__.py
index cb9e647..d20da9a 100644
--- a/__init__.py
+++ b/__init__.py
@@ -4,13 +4,13 @@
# full copyright notices and license terms.
from trytond.pool import Pool
-from .edocument import Invoice, FacturX
+from .edocument import XRechnung, FacturX
from .party import PartyConfiguration, Party
def register():
Pool.register(
- Invoice,
+ XRechnung,
FacturX,
Party,
PartyConfiguration,
diff --git a/edocument.py b/edocument.py
index b5e5a4c..d196140 100644
--- a/edocument.py
+++ b/edocument.py
@@ -5,50 +5,12 @@
import genshi.template
import os
-import html
-from trytond.exceptions import UserError
-from trytond.i18n import gettext
-from trytond.modules.edocument_uncefact.edocument import Invoice
from decimal import Decimal
+from trytond.modules.edocument_uncefact.edocument import Invoice
+from .mixin import EdocumentMixin
-class FacturX(Invoice):
- 'Factur-X'
- __name__ = 'edocument.facturxext.invoice'
-
- def get_list_of_comments(self):
- """ comment, to export in
-
- Returns:
- _type_: _description_
- """
- result = []
- if self.invoice.comment:
- result.append({
- 'content': self.invoice.comment,
- 'subject_code': '',
- 'content_code': ''})
- return result
-
- def _get_template(self, version):
- """ load our own template if 'version' is ours
- """
- loader = genshi.template.TemplateLoader(
- os.path.join(os.path.dirname(__file__), 'template'),
- auto_reload=True)
-
- if version == 'Factur-X-1.07.2-extended':
- if self.type_code in ['380', '389', '381', '261']:
- return loader.load(os.path.join(version, 'invoice.xml'))
- else:
- raise ValueError('invalid type-code "%s"' % self.type_code)
- else:
- return super(Invoice, self)._get_template(version)
-
-# end FacturX
-
-
-class Invoice(Invoice):
+class XRechnung(EdocumentMixin, Invoice):
'EDocument XRechnung'
__name__ = 'edocument.xrechnung.invoice'
@@ -88,74 +50,6 @@ class Invoice(Invoice):
if notes:
return '; '.join(notes)
- def invoice_line_tax(self, line):
- """ get tax of invoice-line,
- fire exception if no/multiple taxes exists
- """
- if len(line.invoice_taxes) != 1:
- raise UserError(gettext(
- 'edocument_xrechnung.msg_linetax_invalid_number',
- linename=line.rec_name,
- numtax=len(line.invoice_taxes)))
-
- allowed_cat = ['AE', 'L', 'M', 'E', 'S', 'Z', 'G', 'O', 'K', 'B']
- unece_category_code = self.get_category_code(line.invoice_taxes[0].tax)
- if unece_category_code not in allowed_cat:
- raise UserError(gettext(
- 'edocument_xrechnung.msg_linetax_invalid_catcode',
- taxname=line.invoice_taxes[0].tax.rec_name,
- allowed=', '.join(allowed_cat)))
-
- return line.invoice_taxes[0].tax
-
- def taxident_data(self, tax_identifier):
- """ get tax-scheme-id and codes
- """
- result = {'code': None, 'id': None}
-
- if tax_identifier:
- if tax_identifier.type == 'de_vat':
- result['code'] = 'DE%s' % tax_identifier.code
- result['id'] = 'VAT'
- return result
-
- def tax_rate(self, tax):
- """ get tax-rate in procent
- """
- return (tax.rate * Decimal('100.0')).quantize(Decimal('0.01'))
-
- def uom_unece_code(self, line):
- """ 'line': invoice.line
- """
- if len(line.unit.unece_code or '') == 0:
- raise UserError(gettext(
- 'edocument_xrechnung.msg_uom_code_missing',
- uomname=line.unit.rec_name))
- return line.unit.unece_code
-
- def get_category_code(self, tax):
- while tax:
- if tax.unece_category_code:
- return tax.unece_category_code
- break
- tax = tax.parent
-
- def tax_category_code(self, tax):
- """ read tax-category, fire exception if missing
- """
- unece_category_code = self.get_category_code(tax)
- if not unece_category_code:
- raise UserError(gettext(
- 'edocument_xrechnung.mds_tax_category_missing',
- taxname=tax.rec_name))
- return unece_category_code
-
- def quote_text(self, text):
- """ replace critical chars
- """
- if text:
- return html.quote(text)
-
def _get_template(self, version):
""" load our own template if 'version' is ours
"""
@@ -176,6 +70,28 @@ class Invoice(Invoice):
else:
raise ValueError('invalid type-code "%s"' % self.type_code)
else:
- return super(Invoice, self)._get_template(version)
+ return super(XRechnung, self)._get_template(version)
-# end Invoice
+# end XRechnung
+
+
+class FacturX(EdocumentMixin, Invoice):
+ 'Factur-X'
+ __name__ = 'edocument.facturxext.invoice'
+
+ def _get_template(self, version):
+ """ load our own template if 'version' is ours
+ """
+ loader = genshi.template.TemplateLoader(
+ os.path.join(os.path.dirname(__file__), 'template'),
+ auto_reload=True)
+
+ if version == 'Factur-X-1.07.2-extended':
+ if self.type_code in ['380', '389', '381', '261']:
+ return loader.load(os.path.join(version, 'invoice.xml'))
+ else:
+ raise ValueError('invalid type-code "%s"' % self.type_code)
+ else:
+ return super(FacturX, self)._get_template(version)
+
+# end FacturX
diff --git a/locale/de.po b/locale/de.po
index c8098e7..ecc828d 100644
--- a/locale/de.po
+++ b/locale/de.po
@@ -14,6 +14,10 @@ msgctxt "model:ir.message,text:mds_tax_category_missing"
msgid "The UNECE tax category is not configured for tax '%(taxname)s'."
msgstr "Für die Steuer '%(taxname)s' ist die UNECE-Steuerkategorie nicht konfiguriert."
+msgctxt "model:ir.message,text:msg_tax_code_missing"
+msgid "The UNECE tax code is not configured for tax '%(taxname)s'."
+msgstr "Für die Steuer '%(taxname)s' ist der UNECE-Einheitencode nicht konfiguriert."
+
msgctxt "model:ir.message,text:msg_uom_code_missing"
msgid "The UNECE uom code is not configured for unit '%(uomname)s'."
msgstr "Für die Einheit '%(uomname)s' ist der UNECE-Einheitencode nicht konfiguriert."
diff --git a/locale/en.po b/locale/en.po
index ab8715b..465895a 100644
--- a/locale/en.po
+++ b/locale/en.po
@@ -10,6 +10,10 @@ msgctxt "model:ir.message,text:mds_tax_category_missing"
msgid "The UNECE tax category is not configured for tax '%(taxname)s'."
msgstr "The UNECE tax category is not configured for tax '%(taxname)s'."
+msgctxt "model:ir.message,text:msg_tax_code_missing"
+msgid "The UNECE tax code is not configured for tax '%(taxname)s'."
+msgstr "The UNECE tax code is not configured for tax '%(taxname)s'."
+
msgctxt "model:ir.message,text:msg_uom_code_missing"
msgid "The UNECE uom code is not configured for unit '%(uomname)s'."
msgstr "The UNECE uom code is not configured for unit '%(uomname)s'."
diff --git a/message.xml b/message.xml
index f7a77ac..f2c47db 100644
--- a/message.xml
+++ b/message.xml
@@ -11,6 +11,9 @@ full copyright notices and license terms. -->
The UNECE tax category is not configured for tax '%(taxname)s'.
+
+ The UNECE tax code is not configured for tax '%(taxname)s'.
+
The UNECE uom code is not configured for unit '%(uomname)s'.
diff --git a/mixin.py b/mixin.py
new file mode 100644
index 0000000..adcb478
--- /dev/null
+++ b/mixin.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# This file is part of the edocument-module for Tryton from m-ds.de.
+# The COPYRIGHT file at the top level of this repository contains the
+# full copyright notices and license terms.
+
+
+from decimal import Decimal
+import html
+from trytond.exceptions import UserError
+from trytond.i18n import gettext
+
+
+class EdocumentMixin(object):
+ """ functions to get field values for xml
+ """
+ __slots__ = ()
+
+ def get_list_of_comments(self):
+ """ comment, to export in
+
+ Returns:
+ _type_: _description_
+ """
+ result = []
+ if self.invoice.comment:
+ result.append({
+ 'content': self.invoice.comment,
+ 'subject_code': '',
+ 'content_code': ''})
+ return result
+
+ def invoice_line_tax(self, line):
+ """ get tax of invoice-line,
+ fire exception if no/multiple taxes exists
+ """
+ if len(line.invoice_taxes) != 1:
+ raise UserError(gettext(
+ 'edocument_xrechnung.msg_linetax_invalid_number',
+ linename=line.rec_name,
+ numtax=len(line.invoice_taxes)))
+
+ allowed_cat = ['AE', 'L', 'M', 'E', 'S', 'Z', 'G', 'O', 'K', 'B']
+ unece_category_code = self.get_category_code(line.invoice_taxes[0].tax)
+ if unece_category_code not in allowed_cat:
+ raise UserError(gettext(
+ 'edocument_xrechnung.msg_linetax_invalid_catcode',
+ taxname=line.invoice_taxes[0].tax.rec_name,
+ allowed=', '.join(allowed_cat)))
+
+ return line.invoice_taxes[0].tax
+
+ def taxident_data(self, tax_identifier):
+ """ get tax-scheme-id and codes
+ """
+ result = {'code': None, 'id': None}
+
+ if tax_identifier:
+ if tax_identifier.type == 'de_vat':
+ result['code'] = 'DE%s' % tax_identifier.code
+ result['id'] = 'VAT'
+ return result
+
+ def tax_rate(self, tax):
+ """ get tax-rate in procent
+ """
+ return (tax.rate * Decimal('100.0')).quantize(Decimal('0.01'))
+
+ def uom_unece_code(self, line):
+ """ 'line': invoice.line
+ """
+ if len(line.unit.unece_code or '') == 0:
+ raise UserError(gettext(
+ 'edocument_xrechnung.msg_uom_code_missing',
+ uomname=line.unit.rec_name))
+ return line.unit.unece_code
+
+ def tax_unece_code(self, tax):
+ """ 'tax': invoice.line
+ """
+ if not (tax.unece_code or ''):
+ raise UserError(gettext(
+ 'edocument_xrechnung.msg_tax_code_missing',
+ taxname=tax.rec_name))
+ return tax.unece_code
+
+ def get_category_code(self, tax):
+ while tax:
+ if tax.unece_category_code:
+ return tax.unece_category_code
+ break
+ tax = tax.parent
+
+ def tax_category_code(self, tax):
+ """ read tax-category, fire exception if missing
+ """
+ unece_category_code = self.get_category_code(tax)
+ if not unece_category_code:
+ raise UserError(gettext(
+ 'edocument_xrechnung.mds_tax_category_missing',
+ taxname=tax.rec_name))
+ return unece_category_code
+
+ def quote_text(self, text):
+ """ replace critical chars
+ """
+ if text:
+ return html.quote(text)
+
+# end EdocumentMixin
diff --git a/template/Factur-X-1.07.2-extended/invoice.xml b/template/Factur-X-1.07.2-extended/invoice.xml
index c10307f..ff32e24 100644
--- a/template/Factur-X-1.07.2-extended/invoice.xml
+++ b/template/Factur-X-1.07.2-extended/invoice.xml
@@ -37,10 +37,10 @@ this repository contains the full copyright notices and license terms. -->
${amount * this.type_sign}
- ${tax.unece_code}
+ ${this.tax_unece_code(tax)}
${tax.legal_notice}
${base * this.type_sign}
- ${tax.unece_category_code}
+ ${this.tax_category_code(tax)}
${tax.rate * 100}
@@ -69,9 +69,9 @@ this repository contains the full copyright notices and license terms. -->
${line_id}
-
- ${line.product.code}
- ${line.product.name}
+
+ ${line.product.code}
+ ${line.product.name if line.product else ''}
${line.description}