diff --git a/README.rst b/README.rst
index e2fa676..ec3c4db 100644
--- a/README.rst
+++ b/README.rst
@@ -9,11 +9,15 @@ pip install mds-edocument-xrechnung
Requires
========
-- Tryton 6.0
+- Tryton 6.6
Changes
=======
-*6.0.0 - 17.10.2022*
+*6.6.2 - 30.06.2023*
-- init
+- add: credit-note
+
+*6.6.1 - 04.04.2023*
+
+- compatibility to Tryton 6.6
diff --git a/__init__.py b/__init__.py
index 84ce866..fe1a4a0 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# This file is part of the cashbook-module from m-ds for Tryton.
+# 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.
diff --git a/edocument.py b/edocument.py
index 44b4123..c71c002 100644
--- a/edocument.py
+++ b/edocument.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
-# This file is part of the edcoment-module for Tryton.
+# 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.
import genshi.template
-import os, html
+import os
+import html
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.modules.edocument_uncefact.edocument import Invoice
@@ -21,6 +22,19 @@ class Invoice(Invoice):
if getattr(self.invoice, 'sales', None) is not None:
return ', '.join([x.number for x in self.invoice.sales])
+ def negate_amount(self, amount):
+ """ amount * -1.0
+ """
+ if amount is not None and amount:
+ if isinstance(amount, Decimal):
+ return amount.copy_negate()
+ elif isinstance(amount, float):
+ return -1.0 * amount
+ elif isinstance(amount, int):
+ return -1 * amount
+ else:
+ return amount
+
def prepaid_amount(self, invoice):
""" compute already paid amount
"""
@@ -45,17 +59,15 @@ class Invoice(Invoice):
if len(line.invoice_taxes) != 1:
raise UserError(gettext(
'edocument_xrechnung.msg_linetax_invalid_number',
- linename = line.rec_name,
- numtax = len(line.invoice_taxes),
- ))
+ linename=line.rec_name,
+ numtax=len(line.invoice_taxes)))
allowed_cat = ['AE', 'L', 'M', 'E', 'S', 'Z', 'G', 'O', 'K', 'B']
if not line.invoice_taxes[0].tax.unece_category_code in allowed_cat:
raise UserError(gettext(
'edocument_xrechnung.msg_linetax_invalid_catcode',
- taxname = line.invoice_taxes[0].tax.rec_name,
- allowed = ', '.join(allowed_cat),
- ))
+ taxname=line.invoice_taxes[0].tax.rec_name,
+ allowed=', '.join(allowed_cat)))
return line.invoice_taxes[0].tax
@@ -84,8 +96,7 @@ class Invoice(Invoice):
if len(line.unit.unece_code or '') == 0:
raise UserError(gettext(
'edocument_xrechnung.msg_uom_code_missing',
- uomname = line.unit.rec_name,
- ))
+ uomname=line.unit.rec_name))
return line.unit.unece_code
def tax_category_code(self, tax):
@@ -94,8 +105,7 @@ class Invoice(Invoice):
if len(tax.unece_category_code or '') == 0:
raise UserError(gettext(
'edocument_xrechnung.mds_tax_category_missing',
- taxname = tax.rec_name,
- ))
+ taxname=tax.rec_name))
return tax.unece_category_code
def quote_text(self, text):
@@ -111,8 +121,15 @@ class Invoice(Invoice):
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 :
+ if self.type_code in ['380', '389']:
+ return loader.load(os.path.join(
+ version, 'XRechnung_invoice.xml'))
+ elif self.type_code in ['381', '261']:
+ return loader.load(os.path.join(
+ version, 'XRechnung_credit.xml'))
+ else:
+ raise ValueError('invalid type-code "%s"' % self.type_code)
+ else:
return super(Invoice, self)._get_template(version)
# end Invoice
diff --git a/party.py b/party.py
index 38cc321..037bae9 100644
--- a/party.py
+++ b/party.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# This file is part of the edcoment-module for Tryton.
+# 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.
@@ -19,8 +19,7 @@ class Party(metaclass=PoolMeta):
return ident.code
raise UserError(gettext(
'edocument_xrechnung.msg_missing_xrechnung_route_id',
- partyname = self.rec_name,
- ))
+ partyname=self.rec_name))
# end Party
diff --git a/setup.py b/setup.py
index b0df333..6458cf7 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
"""
# Always prefer setuptools over distutils
-from setuptools import setup, find_packages
+from setuptools import setup
# To use a consistent encoding
from codecs import open
from os import path
@@ -36,7 +36,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
l2 = i.strip().split(';')
if len(l2) < 4:
continue
- modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]}
+ modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]}
# tryton-version
major_version = 6
@@ -51,19 +51,21 @@ for dep in info.get('depends', []):
prefix = modversion[dep]['prefix']
if len(modversion[dep]['max']) > 0:
- requires.append('%s_%s >= %s, <= %s' %
- (prefix, dep, modversion[dep]['min'], modversion[dep]['max']))
- else :
- requires.append('%s_%s >= %s' %
- (prefix, dep, modversion[dep]['min']))
- else :
- requires.append('%s_%s >= %s.%s, < %s.%s' %
- ('trytond', dep, major_version, minor_version,
+ requires.append('%s_%s >= %s, <= %s' % (
+ prefix, dep, modversion[dep]['min'],
+ modversion[dep]['max']))
+ else:
+ requires.append('%s_%s >= %s' % (
+ prefix, dep, modversion[dep]['min']))
+ else:
+ requires.append('%s_%s >= %s.%s, < %s.%s' % (
+ 'trytond', dep, major_version, minor_version,
major_version, minor_version + 1))
-requires.append('trytond >= %s.%s, < %s.%s' %
- (major_version, minor_version, major_version, minor_version + 1))
+requires.append('trytond >= %s.%s, < %s.%s' % (
+ major_version, minor_version, major_version, minor_version + 1))
-setup(name='%s_%s' % (PREFIX, MODULE),
+setup(
+ name='%s_%s' % (PREFIX, MODULE),
version=info.get('version', '0.0.1'),
description='Tryton module to XRechnung to edocument.',
long_description=long_description,
@@ -72,22 +74,22 @@ setup(name='%s_%s' % (PREFIX, MODULE),
author_email='service@m-ds.de',
license='GPL-3',
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Plugins',
- 'Framework :: Tryton',
- 'Intended Audience :: Developers',
- 'Intended Audience :: Customer Service',
- 'Intended Audience :: Information Technology',
- 'Intended Audience :: Financial and Insurance Industry',
- 'Topic :: Office/Business',
- 'Topic :: Office/Business :: Financial :: Accounting',
- 'Natural Language :: German',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'License :: OSI Approved :: GNU General Public License (GPL)',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Plugins',
+ 'Framework :: Tryton',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Customer Service',
+ 'Intended Audience :: Information Technology',
+ 'Intended Audience :: Financial and Insurance Industry',
+ 'Topic :: Office/Business',
+ 'Topic :: Office/Business :: Financial :: Accounting',
+ 'Natural Language :: German',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
],
keywords='tryton xrechnung edcoument',
@@ -96,9 +98,10 @@ setup(name='%s_%s' % (PREFIX, MODULE),
'trytond.modules.%s' % MODULE,
],
package_data={
- 'trytond.modules.%s' % MODULE: (info.get('xml', [])
+ '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_credit.xml b/template/XRechnung-2.2/XRechnung_credit.xml
new file mode 100644
index 0000000..e12ba91
--- /dev/null
+++ b/template/XRechnung-2.2/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.1
+ ${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.2/XRechnung.xml b/template/XRechnung-2.2/XRechnung_invoice.xml
similarity index 95%
rename from template/XRechnung-2.2/XRechnung.xml
rename to template/XRechnung-2.2/XRechnung_invoice.xml
index b07ad43..a1de5bf 100644
--- a/template/XRechnung-2.2/XRechnung.xml
+++ b/template/XRechnung-2.2/XRechnung_invoice.xml
@@ -27,8 +27,12 @@
${value.name}
- ${value.phone}
- ${value.email}
+
+ ${value.phone}
+
+
+ ${value.email}
+
diff --git a/tests/__init__.py b/tests/__init__.py
index 173e725..bd1a230 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,23 +1,18 @@
-# This file is part of Tryton. The COPYRIGHT file at the top level of
-# this repository contains the full copyright notices and license terms.
+# -*- 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.
import trytond.tests.test_tryton
import unittest
-from trytond.modules.edocument_xrechnung.tests.test_edocument import EdocTestCase
+from .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))
+ suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
+ EdocTestCase))
return suite
diff --git a/tests/test_edocument.py b/tests/test_edocument.py
index aa6c8ad..2b223e4 100644
--- a/tests/test_edocument.py
+++ b/tests/test_edocument.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
-# This file is part of the cashbook-module from m-ds for Tryton.
+# 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 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, MagicMock
+from unittest.mock import Mock
from decimal import Decimal
@@ -15,12 +15,11 @@ class EdocTestCase(ModuleTestCase):
module = 'edocument_xrechnung'
@with_transaction()
- def test_xrechn_export_xml(self):
- """ run export
+ def test_xrechn_export_xml_invoice(self):
+ """ run export - invoice
"""
pool = Pool()
Template = pool.get('edocument.xrechnung.invoice')
- Address = pool.get('party.address')
Identifier = pool.get('party.identifier')
Party = pool.get('party.party')
Bank = pool.get('bank')
@@ -28,28 +27,81 @@ class EdocTestCase(ModuleTestCase):
BankNumber = pool.get('bank.account.number')
invoice = get_invoice()
- invoice.party.get_xrechnung_route_id = Mock(return_value='xrechn-route-id-123')
+ invoice.party.get_xrechnung_route_id = Mock(
+ return_value='xrechn-route-id-123')
invoice.company.party.bank_accounts = [
- Mock(spec=BankAccount,
+ Mock(
+ spec=BankAccount,
currency=invoice.currency,
bank=Mock(spec=Bank, party=Mock(spec=Party, name='Bank')),
- owners = [invoice.company.party],
- numbers = [Mock(spec=BankNumber, type='other', number='123456')],
+ owners=[invoice.company.party],
+ numbers=[Mock(spec=BankNumber, type='other', number='123456')],
)]
invoice.description = 'description of invoice'
invoice.comment = 'note line 1\nnote line 2'
invoice.taxes[0].tax.rate = Decimal('0.1')
invoice.identifiers = [
- Mock(spec=Identifier,
+ 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:
+ with open('xrechnung-test-invoice.xml', 'wt') as fhdl:
+ fhdl.write(invoice_string.decode('utf8'))
+
+ @with_transaction()
+ def test_xrechn_export_xml_creditnote(self):
+ """ run export - creditnote
+ """
+ pool = Pool()
+ Template = pool.get('edocument.xrechnung.invoice')
+ Identifier = pool.get('party.identifier')
+ Party = pool.get('party.party')
+ Bank = pool.get('bank')
+ BankAccount = pool.get('bank.account')
+ BankNumber = pool.get('bank.account.number')
+
+ invoice = get_invoice()
+
+ # credit note
+ invoice.lines[0].quantity = -1
+ invoice.lines[0].amount = Decimal('-100.0')
+ invoice.taxes[0].base = Decimal('-100.0')
+ invoice.taxes[0].amount = Decimal('-10.0')
+ invoice.untaxed_amount = Decimal('-100.0')
+ invoice.tax_amount = Decimal('-10.0')
+ invoice.total_amount = Decimal('-110.0')
+ invoice.lines_to_pay[0].debit = Decimal('-110.0')
+
+ invoice.party.get_xrechnung_route_id = Mock(
+ return_value='xrechn-route-id-123')
+ invoice.company.party.bank_accounts = [
+ Mock(
+ spec=BankAccount,
+ currency=invoice.currency,
+ bank=Mock(spec=Bank, party=Mock(spec=Party, name='Bank')),
+ owners=[invoice.company.party],
+ numbers=[Mock(spec=BankNumber, type='other', number='123456')],
+ )]
+ invoice.description = 'description of invoice'
+ invoice.comment = 'note line 1\nnote line 2'
+ invoice.taxes[0].tax.rate = Decimal('0.1')
+ 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-creditnote.xml', 'wt') as fhdl:
fhdl.write(invoice_string.decode('utf8'))
# end EdocTestCase
+
del ModuleTestCase
diff --git a/tryton.cfg b/tryton.cfg
index 5fc2311..4a6a354 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -1,5 +1,5 @@
[tryton]
-version=6.0.0
+version=6.6.2
depends:
edocument_uncefact
party