# -*- 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 lxml import etree import os 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.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' 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], { '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): """ 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')]) tax, = Taxes.search([('name', '=', '20% VAT')]) Taxes.write(*[ [tax], {'unece_code': 'GST', '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', '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, }])], }] 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 """ pool = Pool() BankAccount = pool.get('bank.account') AccountNumber = pool.get('bank.account.number') Bank = pool.get('bank') Party = pool.get('party.party') company = create_company() with set_company(company): bank_party, = Party.create([{ 'name': 'Bank 123', 'addresses': [('create', [{}])]}]) customer_party, = Party.create([{ 'name': 'Someone', 'addresses': [('create', [{}])]}]) bank, = Bank.create([{'party': bank_party.id}]) acc_company, acc_other, = BankAccount.create([ { 'bank': bank.id, 'owners': [('add', [company.party.id])], 'numbers': [('create', [ {'type': 'iban', 'number': 'DE02300209000106531065'}])] }, { 'bank': bank.id, 'owners': [('add', [customer_party.id])], 'numbers': [('create', [ {'type': 'iban', 'number': 'DE02200505501015871393'}])] }]) self.assertEqual(len(acc_company.numbers), 1) self.assertEqual(acc_company.numbers[0].company_owned, True) self.assertEqual(len(acc_other.numbers), 1) self.assertEqual(acc_other.numbers[0].company_owned, False) company_numbers = AccountNumber.search( [('company_owned', '=', True)]) self.assertEqual(len(company_numbers), 1) self.assertEqual(company_numbers[0].id, acc_company.numbers[0].id) other_numbers = AccountNumber.search( [('company_owned', '=', False)]) self.assertEqual(len(other_numbers), 1) self.assertEqual(other_numbers[0].id, acc_other.numbers[0].id) @with_transaction() def test_xrechn_check_validator(self): """ check validation of optional route-id """ Party = Pool().get('party.party') party, = Party.create([{'name': 'P1'}]) self.assertEqual(party.xrechnung_routeid, False) Party.write(*[ [party], { 'xrechnung_routeid': True, 'identifiers': [('create', [{ 'type': 'edoc_route_id', 'code': '1234'}])]}]) self.assertEqual(party.xrechnung_routeid, True) self.assertEqual(party.get_xrechnung_route_id(), '1234') self.assertRaisesRegex( UserError, "No XRechnung routing ID is stored for the party 'P1'.", Party.write, *[ [party], {'identifiers': [('delete', [party.identifiers[0].id])]}] ) @with_transaction() def test_xrechn_export_facturx(self): """ run export - factur-x """ 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() 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') 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): """ run export - invoice """ pool = Pool() Template = pool.get('edocument.xrechnung.invoice') company = self.prep_company() with set_company(company): create_chart(company=company, tax=True) self.prep_fiscalyear(company) invoice = self.prep_invoice() template = Template(invoice) 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): """ run export - creditnote """ pool = Pool() Template = pool.get('edocument.xrechnung.invoice') 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) 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) # end EdocTestCase del ModuleTestCase