Compare commits

...

9 commits
main ... 6.6

Author SHA1 Message Date
Frederik Jaeckel
9ec43a3377 Zweig 6.6 schließen 2023-12-22 15:04:35 +01:00
Frederik Jaeckel
d19a0767bd Etikett ver 6.6.2 zum Änderungssatz 9cf9ca60ea6a hinzugefügt 2023-06-30 15:47:33 +02:00
Frederik Jaeckel
c6c0c08609 Version 6.6.2 2023-06-30 15:47:23 +02:00
Frederik Jaeckel
1a059b8aee formatting 2023-06-30 15:31:47 +02:00
Frederik Jaeckel
7a6055280f add: creditnote 2023-06-30 15:29:51 +02:00
Frederik Jaeckel
117a247281 formatting 2023-06-30 11:36:17 +02:00
Frederik Jaeckel
e8c24873d4 Etikett ver 6.6.1 zum Änderungssatz 9daf2e4e45c4 hinzugefügt 2023-04-04 12:59:47 +02:00
Frederik Jaeckel
a801810874 Version 6.6.1 2023-04-04 12:59:37 +02:00
Frederik Jaeckel
fe3700b38f Version 6.6.0 2023-04-04 12:58:46 +02:00
10 changed files with 334 additions and 78 deletions

View file

@ -9,11 +9,15 @@ pip install mds-edocument-xrechnung
Requires Requires
======== ========
- Tryton 6.0 - Tryton 6.6
Changes 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

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- 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 # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # full copyright notices and license terms.

View file

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- 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 # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # full copyright notices and license terms.
import genshi.template import genshi.template
import os, html import os
import html
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.i18n import gettext from trytond.i18n import gettext
from trytond.modules.edocument_uncefact.edocument import Invoice from trytond.modules.edocument_uncefact.edocument import Invoice
@ -21,6 +22,19 @@ class Invoice(Invoice):
if getattr(self.invoice, 'sales', None) is not None: if getattr(self.invoice, 'sales', None) is not None:
return ', '.join([x.number for x in self.invoice.sales]) 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): def prepaid_amount(self, invoice):
""" compute already paid amount """ compute already paid amount
""" """
@ -45,17 +59,15 @@ class Invoice(Invoice):
if len(line.invoice_taxes) != 1: if len(line.invoice_taxes) != 1:
raise UserError(gettext( raise UserError(gettext(
'edocument_xrechnung.msg_linetax_invalid_number', 'edocument_xrechnung.msg_linetax_invalid_number',
linename = line.rec_name, linename=line.rec_name,
numtax = len(line.invoice_taxes), numtax=len(line.invoice_taxes)))
))
allowed_cat = ['AE', 'L', 'M', 'E', 'S', 'Z', 'G', 'O', 'K', 'B'] 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: if not line.invoice_taxes[0].tax.unece_category_code in allowed_cat:
raise UserError(gettext( raise UserError(gettext(
'edocument_xrechnung.msg_linetax_invalid_catcode', 'edocument_xrechnung.msg_linetax_invalid_catcode',
taxname = line.invoice_taxes[0].tax.rec_name, taxname=line.invoice_taxes[0].tax.rec_name,
allowed = ', '.join(allowed_cat), allowed=', '.join(allowed_cat)))
))
return line.invoice_taxes[0].tax return line.invoice_taxes[0].tax
@ -84,8 +96,7 @@ class Invoice(Invoice):
if len(line.unit.unece_code or '') == 0: if len(line.unit.unece_code or '') == 0:
raise UserError(gettext( raise UserError(gettext(
'edocument_xrechnung.msg_uom_code_missing', 'edocument_xrechnung.msg_uom_code_missing',
uomname = line.unit.rec_name, uomname=line.unit.rec_name))
))
return line.unit.unece_code return line.unit.unece_code
def tax_category_code(self, tax): def tax_category_code(self, tax):
@ -94,8 +105,7 @@ class Invoice(Invoice):
if len(tax.unece_category_code or '') == 0: if len(tax.unece_category_code or '') == 0:
raise UserError(gettext( raise UserError(gettext(
'edocument_xrechnung.mds_tax_category_missing', 'edocument_xrechnung.mds_tax_category_missing',
taxname = tax.rec_name, taxname=tax.rec_name))
))
return tax.unece_category_code return tax.unece_category_code
def quote_text(self, text): def quote_text(self, text):
@ -111,8 +121,15 @@ class Invoice(Invoice):
loader = genshi.template.TemplateLoader( loader = genshi.template.TemplateLoader(
os.path.join(os.path.dirname(__file__), 'template'), os.path.join(os.path.dirname(__file__), 'template'),
auto_reload=True) auto_reload=True)
return loader.load(os.path.join(version, 'XRechnung.xml')) if self.type_code in ['380', '389']:
else : 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) return super(Invoice, self)._get_template(version)
# end Invoice # end Invoice

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- 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 # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # full copyright notices and license terms.
@ -19,8 +19,7 @@ class Party(metaclass=PoolMeta):
return ident.code return ident.code
raise UserError(gettext( raise UserError(gettext(
'edocument_xrechnung.msg_missing_xrechnung_route_id', 'edocument_xrechnung.msg_missing_xrechnung_route_id',
partyname = self.rec_name, partyname=self.rec_name))
))
# end Party # end Party

View file

@ -2,7 +2,7 @@
""" """
# Always prefer setuptools over distutils # Always prefer setuptools over distutils
from setuptools import setup, find_packages from setuptools import setup
# To use a consistent encoding # To use a consistent encoding
from codecs import open from codecs import open
from os import path from os import path
@ -36,7 +36,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
l2 = i.strip().split(';') l2 = i.strip().split(';')
if len(l2) < 4: if len(l2) < 4:
continue 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 # tryton-version
major_version = 6 major_version = 6
@ -51,19 +51,21 @@ for dep in info.get('depends', []):
prefix = modversion[dep]['prefix'] prefix = modversion[dep]['prefix']
if len(modversion[dep]['max']) > 0: if len(modversion[dep]['max']) > 0:
requires.append('%s_%s >= %s, <= %s' % requires.append('%s_%s >= %s, <= %s' % (
(prefix, dep, modversion[dep]['min'], modversion[dep]['max'])) prefix, dep, modversion[dep]['min'],
else : modversion[dep]['max']))
requires.append('%s_%s >= %s' % else:
(prefix, dep, modversion[dep]['min'])) requires.append('%s_%s >= %s' % (
else : prefix, dep, modversion[dep]['min']))
requires.append('%s_%s >= %s.%s, < %s.%s' % else:
('trytond', dep, major_version, minor_version, requires.append('%s_%s >= %s.%s, < %s.%s' % (
'trytond', dep, major_version, minor_version,
major_version, minor_version + 1)) major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' % requires.append('trytond >= %s.%s, < %s.%s' % (
(major_version, minor_version, major_version, minor_version + 1)) 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'), version=info.get('version', '0.0.1'),
description='Tryton module to XRechnung to edocument.', description='Tryton module to XRechnung to edocument.',
long_description=long_description, long_description=long_description,
@ -96,9 +98,10 @@ setup(name='%s_%s' % (PREFIX, MODULE),
'trytond.modules.%s' % MODULE, 'trytond.modules.%s' % MODULE,
], ],
package_data={ package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', []) 'trytond.modules.%s' % MODULE: (
info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py', + ['tryton.cfg', 'locale/*.po', 'tests/*.py',
'template/*/*.xml','versiondep.txt', 'README.rst']), 'template/*/*.xml', 'versiondep.txt', 'README.rst']),
}, },
install_requires=requires, install_requires=requires,

View file

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ubl:CreditNote
xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDataTypes-2"
xmlns:udt="urn:oasis:names:specification:ubl:schema:xsd:UnqualifiedDataTypes-2"
xmlns:py="http://genshi.edgewall.org/"
xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-CreditNote-2.1.xsd">
<py:def function="PostalAddress(value)">
<cbc:StreetName>${', '.join((getattr(value, 'street', None) or '').split('\n'))}</cbc:StreetName>
<cbc:CityName>${getattr(value, 'city', None) or ''}</cbc:CityName>
<cbc:PostalZone>${getattr(value, 'postal_code', None) or ''}</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>${getattr(getattr(value, 'country', None), 'code', None)}</cbc:IdentificationCode>
</cac:Country>
</py:def>
<py:def function="PartyTaxScheme(value)">
<cbc:CompanyID>${this.taxident_data(value)['code']}</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>${this.taxident_data(value)['id']}</cbc:ID>
</cac:TaxScheme>
</py:def>
<py:def function="Contact(value)">
<cbc:Name>${value.name}</cbc:Name>
<py:if test="value.phone">
<cbc:Telephone>${value.phone}</cbc:Telephone>
</py:if>
<py:if test="value.email">
<cbc:ElectronicMail>${value.email}</cbc:ElectronicMail>
</py:if>
</py:def>
<py:def function="PartyLegalEntity(value, company_id=True)">
<cbc:RegistrationName>${value.name}</cbc:RegistrationName>
<cbc:CompanyID py:if="company_id">${this.taxident_data(this.seller_trade_tax_identifier)['code']}</cbc:CompanyID>
</py:def>
<py:def function="PayeeFinancialAccount(value)">
<py:if test="len(value.bank_accounts)>0">
<cbc:ID>${value.bank_accounts[0].numbers[0].number_compact}</cbc:ID>
<cbc:Name>${value.name}</cbc:Name>
</py:if>
</py:def>
<py:def function="TaxCategory(value, incl_reason=False)">
<cbc:ID>${this.tax_category_code(value)}</cbc:ID>
<cbc:Percent>${this.tax_rate(value)}</cbc:Percent>
<cbc:TaxExemptionReason
py:if="(this.tax_category_code(value) in ['E']) and (incl_reason==True)">${value.legal_notice or '-'}</cbc:TaxExemptionReason>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</py:def>
<py:def function="TaxSubTotal(value)">
<cac:TaxSubtotal>
<cbc:TaxableAmount py:attrs="{'currencyID': value.currency.code}">${this.negate_amount(value.base)}</cbc:TaxableAmount>
<cbc:TaxAmount py:attrs="{'currencyID': value.currency.code}">${this.negate_amount(value.amount)}</cbc:TaxAmount>
<cac:TaxCategory>
${TaxCategory(value.tax, True)}
</cac:TaxCategory>
</cac:TaxSubtotal>
</py:def>
<py:def function="CreditNoteLine(value)">
<cac:CreditNoteLine>
<cbc:ID>${value.id}</cbc:ID>
<cbc:CreditedQuantity py:attrs="{'unitCode': this.uom_unece_code(value)}">${this.negate_amount(value.quantity)}</cbc:CreditedQuantity>
<cbc:LineExtensionAmount py:attrs="{'currencyID': value.currency.code}">${this.negate_amount(value.amount)}</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>${value.description or getattr(value.product, 'name', None)}</cbc:Name>
<cac:SellersItemIdentification py:if="getattr(value.product, 'code', None)">
<cbc:ID>${value.product.code}</cbc:ID>
</cac:SellersItemIdentification>
<cac:ClassifiedTaxCategory>
${TaxCategory(this.invoice_line_tax(value))}
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount py:attrs="{'currencyID': value.currency.code}">${value.unit_price}</cbc:PriceAmount>
</cac:Price>
</cac:CreditNoteLine>
</py:def>
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.1</cbc:CustomizationID>
<cbc:ID>${this.invoice.number}</cbc:ID>
<cbc:IssueDate>${this.invoice.invoice_date.isoformat()}</cbc:IssueDate>
<cbc:CreditNoteTypeCode>${this.type_code}</cbc:CreditNoteTypeCode>
<cbc:Note py:if="this.invoice_note()">${this.invoice_note()}</cbc:Note>
<cbc:DocumentCurrencyCode>${this.invoice.currency.code}</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>${this.invoice.party.get_xrechnung_route_id()}</cbc:BuyerReference>
<cac:OrderReference py:if="this.invoice.reference">
<cbc:ID>${this.invoice.reference}</cbc:ID>
<cbc:SalesOrderID py:if="this.sales_order_nums()">${this.sales_order_nums()}</cbc:SalesOrderID>
</cac:OrderReference>
<cac:ContractDocumentReference py:if="False">
<cbc:ID>vertrags-nr</cbc:ID>
</cac:ContractDocumentReference>
<cac:ProjectReference py:if="False">
<cbc:ID>proj-referenz</cbc:ID>
</cac:ProjectReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PostalAddress>
${PostalAddress(this.seller_trade_address)}
</cac:PostalAddress>
<cac:PartyTaxScheme>
${PartyTaxScheme(this.seller_trade_tax_identifier)}
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
${PartyLegalEntity(this.seller_trade_party)}
</cac:PartyLegalEntity>
<cac:Contact>
${Contact(this.seller_trade_party)}
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>${this.buyer_trade_party.code}</cbc:ID>
</cac:PartyIdentification>
<cac:PostalAddress>
${PostalAddress(this.buyer_trade_address)}
</cac:PostalAddress>
<cac:PartyTaxScheme>
${PartyTaxScheme(this.buyer_trade_tax_identifier)}
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
${PartyLegalEntity(this.buyer_trade_party, False)}
</cac:PartyLegalEntity>
<cac:Contact>
${Contact(this.buyer_trade_party)}
</cac:Contact>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cbc:PaymentID>${this.payment_reference}</cbc:PaymentID>
<cac:PayeeFinancialAccount>
${PayeeFinancialAccount(this.seller_trade_party)}
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms py:if="this.invoice.payment_term is not None">
<cbc:Note>${this.invoice.payment_term.rec_name}</cbc:Note>
</cac:PaymentTerms>
<cac:AllowanceCharge py:if="False">
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReason>Neukundenrabatt</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="EUR">5.00</cbc:Amount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:AllowanceCharge>
<cac:TaxTotal>
<cbc:TaxAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.invoice.tax_amount)}</cbc:TaxAmount>
<py:for each="taxline in this.invoice.taxes">
${TaxSubTotal(taxline)}
</py:for>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.invoice.untaxed_amount)}</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.invoice.untaxed_amount)}</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.invoice.total_amount)}</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount py:if="False" py:attrs="{'currencyID': this.invoice.currency.code}">0.00</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount py:if="False" py:attrs="{'currencyID': this.invoice.currency.code}">0.00</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.prepaid_amount(this.invoice))}</cbc:PrepaidAmount>
<cbc:PayableAmount py:attrs="{'currencyID': this.invoice.currency.code}">${this.negate_amount(this.invoice.amount_to_pay)}</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<py:for each="line in this.lines">
${CreditNoteLine(line)}
</py:for>
</ubl:CreditNote>

View file

@ -27,8 +27,12 @@
<py:def function="Contact(value)"> <py:def function="Contact(value)">
<cbc:Name>${value.name}</cbc:Name> <cbc:Name>${value.name}</cbc:Name>
<py:if test="value.phone">
<cbc:Telephone>${value.phone}</cbc:Telephone> <cbc:Telephone>${value.phone}</cbc:Telephone>
</py:if>
<py:if test="value.email">
<cbc:ElectronicMail>${value.email}</cbc:ElectronicMail> <cbc:ElectronicMail>${value.email}</cbc:ElectronicMail>
</py:if>
</py:def> </py:def>
<py:def function="PartyLegalEntity(value, company_id=True)"> <py:def function="PartyLegalEntity(value, company_id=True)">

View file

@ -1,23 +1,18 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of # -*- coding: utf-8 -*-
# this repository contains the full copyright notices and license terms. # 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 trytond.tests.test_tryton
import unittest import unittest
from trytond.modules.edocument_xrechnung.tests.test_edocument import EdocTestCase from .test_edocument import EdocTestCase
__all__ = ['suite'] __all__ = ['suite']
class EDocumentTestCase(\
EdocTestCase,\
):
'Test edocument module'
module = 'edocument_xrechnung'
# end EDocumentTestCase
def suite(): def suite():
suite = trytond.tests.test_tryton.suite() suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(EDocumentTestCase)) suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
EdocTestCase))
return suite return suite

View file

@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- 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 # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # full copyright notices and license terms.
from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool from trytond.pool import Pool
from trytond.modules.edocument_uncefact.tests.test_module import get_invoice 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 from decimal import Decimal
@ -15,12 +15,11 @@ class EdocTestCase(ModuleTestCase):
module = 'edocument_xrechnung' module = 'edocument_xrechnung'
@with_transaction() @with_transaction()
def test_xrechn_export_xml(self): def test_xrechn_export_xml_invoice(self):
""" run export """ run export - invoice
""" """
pool = Pool() pool = Pool()
Template = pool.get('edocument.xrechnung.invoice') Template = pool.get('edocument.xrechnung.invoice')
Address = pool.get('party.address')
Identifier = pool.get('party.identifier') Identifier = pool.get('party.identifier')
Party = pool.get('party.party') Party = pool.get('party.party')
Bank = pool.get('bank') Bank = pool.get('bank')
@ -28,28 +27,81 @@ class EdocTestCase(ModuleTestCase):
BankNumber = pool.get('bank.account.number') BankNumber = pool.get('bank.account.number')
invoice = get_invoice() 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 = [ invoice.company.party.bank_accounts = [
Mock(spec=BankAccount, Mock(
spec=BankAccount,
currency=invoice.currency, currency=invoice.currency,
bank=Mock(spec=Bank, party=Mock(spec=Party, name='Bank')), bank=Mock(spec=Bank, party=Mock(spec=Party, name='Bank')),
owners = [invoice.company.party], owners=[invoice.company.party],
numbers = [Mock(spec=BankNumber, type='other', number='123456')], numbers=[Mock(spec=BankNumber, type='other', number='123456')],
)] )]
invoice.description = 'description of invoice' invoice.description = 'description of invoice'
invoice.comment = 'note line 1\nnote line 2' invoice.comment = 'note line 1\nnote line 2'
invoice.taxes[0].tax.rate = Decimal('0.1') invoice.taxes[0].tax.rate = Decimal('0.1')
invoice.identifiers = [ invoice.identifiers = [
Mock(spec=Identifier, Mock(
spec=Identifier,
type='edoc_route_id', type='edoc_route_id',
code='xrechn-route-id-123') code='xrechn-route-id-123')
] ]
template = Template(invoice) template = Template(invoice)
invoice_string = template.render('XRechnung-2.2') 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')) fhdl.write(invoice_string.decode('utf8'))
# end EdocTestCase # end EdocTestCase
del ModuleTestCase del ModuleTestCase

View file

@ -1,5 +1,5 @@
[tryton] [tryton]
version=6.0.0 version=6.6.2
depends: depends:
edocument_uncefact edocument_uncefact
party party