diff --git a/COPYRIGHT b/COPYRIGHT index 27458f9..d99fbe4 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,5 +1,6 @@ -Copyright (C) 2021-2025 martin-data services. -Copyright (C) 2024-2025 Mathias Behrle +Copyright (C) 2015-2023 Cédric Krier. +Copyright (C) 2015-2023 B2CK SPRL. +Copyright (C) 2021-2023 martin-data services. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/README.rst b/README.rst index 9eabda9..affb395 100644 --- a/README.rst +++ b/README.rst @@ -14,11 +14,6 @@ Requires Changes ======= -*7.0.10 - 12.12.2024* - -- fix missing views -- Remove arguments in super() calls. (Mathias Behrle) - *7.0.9 - 11.12.2024* - fix name of party in exceptions diff --git a/docs/xrechnung.txt b/docs/xrechnung.txt index dab13fa..3cf8cff 100644 --- a/docs/xrechnung.txt +++ b/docs/xrechnung.txt @@ -9,8 +9,3 @@ validator: überweisungsdaten https://portal3.gefeg.com/projectdata/invoice/deliverables/installed/publishingproject/zugferd%202.0.1%20-%20facturx%201.03/en%2016931%20%E2%80%93%20facturx%201.03%20%E2%80%93%20zugferd%202.0.1%20-%20basic.scm/html/de/021.htm?https://portal3.gefeg.com/projectdata/invoice/deliverables/installed/publishingproject/zugferd%202.0.1%20-%20facturx%201.03/en%2016931%20%E2%80%93%20facturx%201.03%20%E2%80%93%20zugferd%202.0.1%20-%20basic.scm/html/de/02134.htm - -https://erechnungsvalidator.service-bw.de/ -https://ecosio.com/de/peppol-und-xml-dokumente-online-validieren/ - -https://www.e-rechnungs-checker.de/ diff --git a/edocument.py b/edocument.py index 395df5d..d196140 100644 --- a/edocument.py +++ b/edocument.py @@ -70,7 +70,7 @@ class XRechnung(EdocumentMixin, Invoice): else: raise ValueError('invalid type-code "%s"' % self.type_code) else: - return super()._get_template(version) + return super(XRechnung, self)._get_template(version) # end XRechnung @@ -92,6 +92,6 @@ class FacturX(EdocumentMixin, Invoice): else: raise ValueError('invalid type-code "%s"' % self.type_code) else: - return super()._get_template(version) + return super(FacturX, self)._get_template(version) # end FacturX diff --git a/mixin.py b/mixin.py index dc235ac..04e94f4 100644 --- a/mixin.py +++ b/mixin.py @@ -10,8 +10,6 @@ from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.tools import cached_property from trytond.pool import Pool -from trytond.transaction import Transaction -from trytond.modules.product import round_price class EdocumentMixin(object): @@ -49,7 +47,7 @@ class EdocumentMixin(object): Returns: record : model party.address """ - result = super().seller_trade_address + result = super(EdocumentMixin, self).seller_trade_address if not result: raise UserError(gettext( 'edocument_xrechnung.msg_no_seller_address', @@ -77,7 +75,7 @@ class EdocumentMixin(object): if self.invoice and self.invoice.party else '-')) - result = super().buyer_trade_address + result = super(EdocumentMixin, self).buyer_trade_address if result and not result.country: raise UserError(gettext( 'edocument_xrechnung.msg_no_address_country', @@ -102,29 +100,21 @@ class EdocumentMixin(object): """ get tax of invoice-line, fire exception if no/multiple taxes exists """ - Tax = Pool().get('account.tax') - - if len(line.taxes) != 1: + if len(line.invoice_taxes) != 1: raise UserError(gettext( 'edocument_xrechnung.msg_linetax_invalid_number', linename=line.rec_name, - numtax=len(line.taxes))) - - taxlines = Tax.compute( - line.taxes, Decimal('1'), 1.0, - line.invoice.accounting_date or line.invoice.invoice_date) - assert len(taxlines) == 1 - tax = taxlines[0]['tax'] + numtax=len(line.invoice_taxes))) allowed_cat = ['AE', 'L', 'M', 'E', 'S', 'Z', 'G', 'O', 'K', 'B'] - unece_category_code = self.get_category_code(tax) + 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=tax.rec_name, + taxname=line.invoice_taxes[0].tax.rec_name, allowed=', '.join(allowed_cat))) - return tax + return line.invoice_taxes[0].tax def taxident_data(self, tax_identifier): """ get tax-scheme-id and codes @@ -161,44 +151,6 @@ class EdocumentMixin(object): taxname=tax.rec_name)) return tax.unece_code - def get_line_amount(self, line): - """ get amount of current invoice-line, - depends on modegross of invoice, set used-modegross to 'net' - - Args: - line (record): model account.invoice.line - """ - if not hasattr(line, 'modegross'): - return line.amount - - if line.modegross == 'net': - return line.amount - elif line.modegross == 'gross': - # get net-amount - # copy from account_invoice/invoice.py:2416-2434 - currency = ( - line.invoice.currency - if line.invoice else line.currency) - - amount = (Decimal(str(line.quantity or 0)) * ( - line.unit_price or Decimal(0))) - invoice_type = ( - line.invoice.type - if line.invoice else line.invoice_type) - - if (invoice_type == 'in' - and line.taxes_deductible_rate is not None - and line.taxes_deductible_rate != 1): - with Transaction().set_context(_deductible_rate=1): - tax_amount = sum( - t['amount'] for t in line._get_taxes().values()) - non_deductible_amount = ( - tax_amount * (1 - line.taxes_deductible_rate)) - amount += non_deductible_amount - if currency: - return currency.round(amount) - return amount - def get_tax_unece_code(self, tax): while tax: if tax.unece_code: @@ -223,49 +175,10 @@ class EdocumentMixin(object): taxname=tax.rec_name)) return unece_category_code - def round_unitprice(self, value): - """ round value by digits in unit_price of account.invoice.line - - Args: - value (Decimal): unit-price - - Returns: - Decimal: rounded value - """ - if value is not None: - return round_price(value) - return value - def quote_text(self, text): """ replace critical chars """ if text: return html.escape(text) - def _party_legal_types(self): - """ get list of identifier-types to be used as - legal-ids - """ - return ['de_handelsregisternummer'] - - def party_legal_ids(self, party, address): - """ get list of legal-ids of party - - Args: - party (record): model party.party - address (record): model party.address - """ - result = super().party_legal_ids(party, address) - - legal_types = self._party_legal_types() - if party and party.identifiers: - for x in party.identifiers: - if x.type in legal_types: - if x.address: - if x.address == address: - result.append((x.rec_name, {'schemeID': '0002'})) - else: - result.append((x.rec_name, {'schemeID': '0002'})) - return result - # end EdocumentMixin diff --git a/party.py b/party.py index de60ce8..adcd519 100644 --- a/party.py +++ b/party.py @@ -47,7 +47,7 @@ class Party(metaclass=PoolMeta): Args: records (list): records of party.party """ - super().validate(records) + super(Party, cls).validate(records) for record in records: record.get_xrechnung_route_id() @@ -59,7 +59,7 @@ class PartyConfiguration(metaclass=PoolMeta): @classmethod def __setup__(cls): - super().__setup__() + super(PartyConfiguration, cls).__setup__() cls.identifier_types.selection.append( ('edoc_route_id', 'X-Rechnung Route-ID')) diff --git a/setup.py b/setup.py index c82d777..04b912a 100644 --- a/setup.py +++ b/setup.py @@ -13,9 +13,11 @@ here = path.abspath(path.dirname(__file__)) MODULE = 'edocument_xrechnung' PREFIX = 'mds' +# Get the long description from the README file with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() +# tryton.cfg einlesen config = ConfigParser() config.readfp(open('tryton.cfg')) info = dict(config.items('tryton')) @@ -85,8 +87,6 @@ setup( 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', ], keywords='tryton xrechnung edcoument', @@ -99,7 +99,7 @@ setup( info.get('xml', []) + ['tryton.cfg', 'locale/*.po', 'tests/*.py', 'template/*/*.xml', 'versiondep.txt', 'README.rst', - 'tests/*/*/*/*.xsd', 'view/*.xml', + 'tests/*/*/*/*.xsd', 'tests/*/*.xsd', 'tests/*/*.sch', 'tests/*/*.xml', 'tests/*/*/*.xslt', 'tests/*/*/*.xml']), }, diff --git a/template/Factur-X-1.07.2-extended/invoice.xml b/template/Factur-X-1.07.2-extended/invoice.xml index a4620c5..8a7a664 100644 --- a/template/Factur-X-1.07.2-extended/invoice.xml +++ b/template/Factur-X-1.07.2-extended/invoice.xml @@ -19,7 +19,7 @@ this repository contains the full copyright notices and license terms. --> ${TradeAddress(address)} - + ${tax_identifier.code} @@ -38,7 +38,7 @@ this repository contains the full copyright notices and license terms. --> ${amount * this.type_sign} ${this.tax_unece_code(tax)} - ${tax.legal_notice} + ${tax.legal_notice} ${base * this.type_sign} ${this.tax_category_code(tax)} ${tax.rate * 100} @@ -71,21 +71,23 @@ this repository contains the full copyright notices and license terms. --> ${line.product.code} - ${this.quote_text(line.product.name if line.product else line.description if line.description else 'name not set')} - ${this.quote_text(line.description if line.product else '')} + ${this.quote_text(line.product.name if line.product else '')} + ${this.quote_text(line.description)} - ${this.round_unitprice(line.unit_price)} + ${this.invoice.currency.round(line.unit_price)} ${line.quantity * this.type_sign} - ${TradeTax(this.invoice_line_tax(line))} + + ${TradeTax(tax.tax)} + - ${this.get_line_amount(line)} + ${line.amount} diff --git a/tests/test_edocument.py b/tests/test_edocument.py index 179957d..4ff38b7 100644 --- a/tests/test_edocument.py +++ b/tests/test_edocument.py @@ -5,180 +5,20 @@ 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, activate_module) +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 trytond.modules.company.tests import create_company, set_company -from trytond.modules.account.tests import create_chart, get_fiscalyear from trytond.exceptions import UserError -def set_invoice_sequences(fiscalyear): - pool = Pool() - Sequence = pool.get('ir.sequence.strict') - SequenceType = pool.get('ir.sequence.type') - InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence') - ModelData = pool.get('ir.model.data') - - sequence = Sequence( - name=fiscalyear.name, - sequence_type=SequenceType(ModelData.get_id( - 'account_invoice', 'sequence_type_account_invoice')), - company=fiscalyear.company) - sequence.save() - fiscalyear.invoice_sequences = [] - invoice_sequence = InvoiceSequence() - invoice_sequence.fiscalyear = fiscalyear - invoice_sequence.in_invoice_sequence = sequence - invoice_sequence.in_credit_note_sequence = sequence - invoice_sequence.out_invoice_sequence = sequence - invoice_sequence.out_credit_note_sequence = sequence - invoice_sequence.save() - return fiscalyear - - class EdocTestCase(ModuleTestCase): 'Test e-rechnung module' module = 'edocument_xrechnung' - @classmethod - def setUpClass(cls): - super().setUpClass() - activate_module([ - 'edocument_uncefact', 'party', 'bank', - 'account_invoice', 'sale_point_invoice', - 'product_grossprice'], 'en') - - def prep_fiscalyear(self, company1): - """ prepare fiscal year, sequences... - """ - pool = Pool() - FiscalYear = pool.get('account.fiscalyear') - - fisc_year = get_fiscalyear(company1, today=date(2024, 1, 15)) - set_invoice_sequences(fisc_year) - self.assertEqual(len(fisc_year.invoice_sequences), 1) - FiscalYear.create_period([fisc_year]) - - def prep_company(self): - """ create company, add country and bank-account - """ - pool = Pool() - Country = pool.get('country.country') - Party = pool.get('party.party') - Bank = pool.get('bank') - BankAccount = pool.get('bank.account') - - country_de, = Country.create([{ - 'name': 'Germany', - 'code': 'DE', - 'code3': 'DEU'}]) - - company = create_company('m-ds') - Party.write(*[[company.party], { - 'identifiers': [('create', [ - # post.de - {'type': 'de_handelsregisternummer', 'code': 'Bonn HRB 6792'}, - {'type': 'de_vat', 'code': 'DE 169838187'}, - ])], - 'addresses': [('write', [company.party.addresses[0]], { - 'country': country_de.id})]}]) - - bank_party, = Party.create([{ - 'name': 'Bank 123', - 'addresses': [('create', [{}])]}]) - bank, = Bank.create([{'party': bank_party.id}]) - BankAccount.create([{ - 'bank': bank.id, - 'owners': [('add', [company.party.id])], - 'numbers': [('create', [{ - 'type': 'iban', - 'number': 'DE02300209000106531065'}])]}]) - return company - - def prep_invoice(self, credit_note=False, modegross='net'): - """ add invoice - """ - pool = Pool() - Invoice = pool.get('account.invoice') - Taxes = pool.get('account.tax') - Account = pool.get('account.account') - Journal = pool.get('account.journal') - Currency = pool.get('currency.currency') - Uom = pool.get('product.uom') - Country = pool.get('country.country') - Party = pool.get('party.party') - - country_de, = Country.search([('code', '=', 'DE')]) - customer, = Party.create([{ - 'name': 'Customer', - 'identifiers': [('create', [{ - 'type': 'edoc_route_id', 'code': 'xrechn-route-id-123'}])], - 'addresses': [('create', [{ - 'invoice': True, - 'street': 'Customer Street 1', - 'postal_code': '12345', - 'city': 'Usertown', - 'country': country_de.id, - }])], - }]) - - currency1, = Currency.search([('code', '=', 'usd')]) - Currency.write(*[[currency1], {'code': 'USD'}]) - - tax, = Taxes.search([('name', '=', '20% VAT')]) - Taxes.write(*[ - [tax], - {'unece_code': 'VAT', 'unece_category_code': 'S', - 'legal_notice': 'Legal Notice'}]) - - account_lst = Account.search([ - ('name', 'in', ['Main Revenue', 'Main Receivable']) - ], order=[('name', 'ASC')]) - self.assertEqual(len(account_lst), 2) - self.assertEqual(account_lst[0].name, 'Main Receivable') - - journ_lst = Journal.search([('name', '=', 'Revenue')]) - self.assertEqual(len(journ_lst), 1) - - to_create_invoice = [{ - 'type': 'out', - 'modegross': modegross, - 'description': 'description of invoice', - 'comment': 'note line 1\nnote line 2', - 'invoice_date': date(2024, 7, 1), - 'party': customer.id, - 'invoice_address': customer.addresses[0].id, - 'account': account_lst[0].id, - 'journal': journ_lst[0].id, - 'currency': currency1.id, - 'lines': [('create', [{ - 'type': 'line', - 'quantity': 2.0 if not credit_note else -2.0, - 'description': 'Product 1', - 'unit': Uom.search([('symbol', '=', 'u')])[0].id, - 'unit_price': Decimal('50.0'), - 'taxes': [('add', [tax.id])], - 'account': account_lst[1].id, - 'currency': currency1.id, - }])], - }] - - if modegross == 'gross': - to_create_invoice[0]['lines'][0][1][0]['unit_gross_price'] = ( - Decimal('50.0') * Decimal('1.2')) - - inv_lst, = Invoice.create(to_create_invoice) - inv_lst.on_change_lines() - inv_lst.save() - Invoice.validate_invoice([inv_lst]) - Invoice.post([inv_lst]) - self.assertEqual(inv_lst.currency.code, 'USD') - self.assertEqual(len(inv_lst.move.lines), 3) - return inv_lst - @with_transaction() def test_xrechn_bank_account_owned(self): """ check field 'company_owned' on bank.account.number @@ -254,106 +94,51 @@ class EdocTestCase(ModuleTestCase): {'identifiers': [('delete', [party.identifiers[0].id])]}] ) - @with_transaction() - def test_xrechn_export_facturx_gross(self): - """ run export - factur-x, modegross='gross' - """ - pool = Pool() - Template = pool.get('edocument.facturxext.invoice') - - company = self.prep_company() - with set_company(company): - create_chart(company=company, tax=True) - self.prep_fiscalyear(company) - invoice = self.prep_invoice(modegross='gross') - - template = Template(invoice) - - schema_file = os.path.join( - os.path.dirname(__file__), - 'Factur-X_1.07.2_EXTENDED', - 'Factur-X_1.07.2_EXTENDED.xsd') - - invoice_string = template.render('Factur-X-1.07.2-extended') - with open('gross_invoice_string.xml', 'wb') as fhdl: - fhdl.write(invoice_string) - - invoice_xml = etree.fromstring(invoice_string) - - # check values in xml - nodes = invoice_xml.xpath(self._readxml_xpath([ - 'rsm:CrossIndustryInvoice', 'rsm:SupplyChainTradeTransaction', - 'ram:ApplicableHeaderTradeAgreement', 'ram:SellerTradeParty', - 'ram:SpecifiedLegalOrganization', 'ram:ID']), - namespaces=invoice_xml.nsmap) - self.assertEqual(nodes[0].text, 'Bonn HRB 6792') - - nodes = invoice_xml.xpath(self._readxml_xpath([ - 'rsm:CrossIndustryInvoice', 'rsm:SupplyChainTradeTransaction', - 'ram:IncludedSupplyChainTradeLineItem', - 'ram:SpecifiedLineTradeSettlement', - 'ram:SpecifiedTradeSettlementLineMonetarySummation', - 'ram:LineTotalAmount']), - namespaces=invoice_xml.nsmap) - self.assertEqual(nodes[0].text, '100.00') - - schema = etree.XMLSchema(etree.parse(schema_file)) - schema.assertValid(invoice_xml) - - def _readxml_xpath(self, tags): - """ generate xpath - - Args: - tags (list): list of string or integer to build path - """ - parts = [] - for x in tags: - if isinstance(x, str): - parts.append(x) - elif isinstance(x, int): - if parts[-1].endswith(']'): - raise ValueError('multiple list selector') - parts[-1] += '[%d]' % x - result = '/' + '/'.join(parts) - return result - @with_transaction() def test_xrechn_export_facturx(self): """ run export - factur-x """ pool = Pool() Template = pool.get('edocument.facturxext.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') - company = self.prep_company() - with set_company(company): - create_chart(company=company, tax=True) - self.prep_fiscalyear(company) - invoice = self.prep_invoice() + 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 = [ + 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) + template = Template(invoice) - self.assertEqual( - template.party_legal_ids(invoice.company_party, None), - [('Bonn HRB 6792', {'schemeID': '0002'})]) + schema_file = os.path.join( + os.path.dirname(__file__), + 'Factur-X_1.07.2_EXTENDED', + 'Factur-X_1.07.2_EXTENDED.xsd') - schema_file = os.path.join( - os.path.dirname(__file__), - 'Factur-X_1.07.2_EXTENDED', - 'Factur-X_1.07.2_EXTENDED.xsd') - - invoice_string = template.render('Factur-X-1.07.2-extended') - invoice_xml = etree.fromstring(invoice_string) - - # check values in xml - nodes = invoice_xml.xpath(self._readxml_xpath([ - 'rsm:CrossIndustryInvoice', 'rsm:SupplyChainTradeTransaction', - 'ram:ApplicableHeaderTradeAgreement', 'ram:SellerTradeParty', - 'ram:SpecifiedLegalOrganization', 'ram:ID']), - namespaces=invoice_xml.nsmap) - self.assertEqual(nodes[0].text, 'Bonn HRB 6792') - - schema = etree.XMLSchema(etree.parse(schema_file)) - schema.assertValid(invoice_xml) + invoice_string = template.render('Factur-X-1.07.2-extended') + invoice_xml = etree.fromstring(invoice_string) + schema = etree.XMLSchema(etree.parse(schema_file)) + schema.assertValid(invoice_xml) @with_transaction() def test_xrechn_export_xml_invoice(self): @@ -361,24 +146,45 @@ class EdocTestCase(ModuleTestCase): """ 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') - company = self.prep_company() - with set_company(company): - create_chart(company=company, tax=True) - self.prep_fiscalyear(company) - invoice = self.prep_invoice() + 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 = [ + 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) + template = Template(invoice) - schema_file = os.path.join( - os.path.dirname(__file__), 'os-UBL-2.1', - 'xsd', 'maindoc', 'UBL-Invoice-2.1.xsd') + 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) + 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): @@ -386,24 +192,59 @@ class EdocTestCase(ModuleTestCase): """ 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') - company = self.prep_company() - with set_company(company): - create_chart(company=company, tax=True) - self.prep_fiscalyear(company) - invoice = self.prep_invoice(credit_note=True) + invoice = get_invoice() - template = Template(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') - schema_file = os.path.join( - os.path.dirname(__file__), 'os-UBL-2.1', - 'xsd', 'maindoc', 'UBL-CreditNote-2.1.xsd') + 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') + ] - 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) + template = Template(invoice) + + 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 diff --git a/tryton.cfg b/tryton.cfg index ecd8916..88331ef 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,13 +1,10 @@ [tryton] -version=7.0.10 +version=7.0.9 depends: edocument_uncefact party bank account_invoice -extras_depend: - sale_point_invoice - product_grossprice xml: message.xml configuration.xml