Compare commits

...

2 commits

Author SHA1 Message Date
Frederik Jaeckel
d861c9a6ff add test 2024-12-18 17:23:01 +01:00
Frederik Jaeckel
bb5d5d27ff add wizard for upload 2024-12-18 16:51:30 +01:00
11 changed files with 528 additions and 0 deletions

View file

@ -8,6 +8,7 @@ from .wizard_runreport import RunXRechnungReport, RunXRechnungReportStart
from .invoice import InvoiceLine from .invoice import InvoiceLine
from .xreport import XReport from .xreport import XReport
from .configuration import ConfigurationXRechnungexport, Configuration from .configuration import ConfigurationXRechnungexport, Configuration
from .wizard_import import ImportXmlStart, ImportXml, ImportXmlShowcontent
def register(): def register():
@ -16,9 +17,12 @@ def register():
ConfigurationXRechnungexport, ConfigurationXRechnungexport,
InvoiceLine, InvoiceLine,
RunXRechnungReportStart, RunXRechnungReportStart,
ImportXmlStart,
ImportXmlShowcontent,
module='account_invoice_xrechnung', type_='model') module='account_invoice_xrechnung', type_='model')
Pool.register( Pool.register(
RunXRechnungReport, RunXRechnungReport,
ImportXml,
module='account_invoice_xrechnung', type_='wizard') module='account_invoice_xrechnung', type_='wizard')
Pool.register( Pool.register(
XReport, XReport,

View file

@ -30,6 +30,10 @@ msgctxt "model:ir.action,name:report_edocument"
msgid "eDocument Export" msgid "eDocument Export"
msgstr "eDocument Export" msgstr "eDocument Export"
msgctxt "model:ir.action,name:act_import_wiz"
msgid "eDocument Import"
msgstr "eDocument Import"
########################################## ##########################################
# account_invoice_xrechnung.runrep.start # # account_invoice_xrechnung.runrep.start #
@ -193,3 +197,83 @@ msgstr "ZUGFeRD 2.3.2"
msgctxt "selection:account_invoice_xrechnung.configuration,xrechn_default:" msgctxt "selection:account_invoice_xrechnung.configuration,xrechn_default:"
msgid "CII CrossIndustryInvoice D16B" msgid "CII CrossIndustryInvoice D16B"
msgstr "CII CrossIndustryInvoice D16B" msgstr "CII CrossIndustryInvoice D16B"
##########################################
# account_invoice_xrechnung.import.start #
##########################################
msgctxt "model:account_invoice_xrechnung.import.start,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "field:account_invoice_xrechnung.import.start,company:"
msgid "Company"
msgstr "Unternehmen"
msgctxt "field:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Invoice Type"
msgstr "Rechnungstyp"
msgctxt "selection:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Customer Invoice"
msgstr "Kundenrechnung"
msgctxt "selection:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Supplier Invoice"
msgstr "Lieferantenrechnung"
msgctxt "field:account_invoice_xrechnung.import.start,file_:"
msgid "ZIP or XML-File"
msgstr "ZIP oder XML-Datei"
################################################
# account_invoice_xrechnung.import.showcontent #
################################################
msgctxt "model:account_invoice_xrechnung.import.showcontent,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "field:account_invoice_xrechnung.import.showcontent,company:"
msgid "Company"
msgstr "Unternehmen"
msgctxt "field:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Invoice Type"
msgstr "Rechnungstyp"
msgctxt "selection:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Customer Invoice"
msgstr "Kundenrechnung"
msgctxt "selection:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Supplier Invoice"
msgstr "Lieferantenrechnung"
####################################
# account_invoice_xrechnung.import #
####################################
msgctxt "model:account_invoice_xrechnung.import,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "wizard_button:account_invoice_xrechnung.import,start,end:"
msgid "Cancel"
msgstr "Abbruch"
msgctxt "wizard_button:account_invoice_xrechnung.import,start,doupload:"
msgid "Upload"
msgstr "Hochladen"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,end:"
msgid "Cancel"
msgstr "Abbruch"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,start:"
msgid "Back"
msgstr "Zurück"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,doimport:"
msgid "Import"
msgstr "Import"

View file

@ -22,6 +22,10 @@ msgctxt "model:ir.action,name:report_edocument"
msgid "eDocument Export" msgid "eDocument Export"
msgstr "eDocument Export" msgstr "eDocument Export"
msgctxt "model:ir.action,name:act_import_wiz"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "model:account_invoice_xrechnung.runrep.start,name:" msgctxt "model:account_invoice_xrechnung.runrep.start,name:"
msgid "eDocument Export" msgid "eDocument Export"
msgstr "eDocument Export" msgstr "eDocument Export"
@ -165,3 +169,72 @@ msgstr "ZUGFeRD 2.3.2"
msgctxt "selection:account_invoice_xrechnung.configuration,xrechn_default:" msgctxt "selection:account_invoice_xrechnung.configuration,xrechn_default:"
msgid "CII CrossIndustryInvoice D16B" msgid "CII CrossIndustryInvoice D16B"
msgstr "CII CrossIndustryInvoice D16B" msgstr "CII CrossIndustryInvoice D16B"
msgctxt "model:account_invoice_xrechnung.import.start,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "field:account_invoice_xrechnung.import.start,company:"
msgid "Company"
msgstr "Company"
msgctxt "field:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Invoice Type"
msgstr "Invoice Type"
msgctxt "selection:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Customer Invoice"
msgstr "Customer Invoice"
msgctxt "selection:account_invoice_xrechnung.import.start,invoice_type:"
msgid "Supplier Invoice"
msgstr "Supplier Invoice"
msgctxt "field:account_invoice_xrechnung.import.start,file_:"
msgid "ZIP or XML-File"
msgstr "ZIP or XML-File"
msgctxt "model:account_invoice_xrechnung.import.showcontent,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "field:account_invoice_xrechnung.import.showcontent,company:"
msgid "Company"
msgstr "Company"
msgctxt "field:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Invoice Type"
msgstr "Invoice Type"
msgctxt "selection:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Customer Invoice"
msgstr "Customer Invoice"
msgctxt "selection:account_invoice_xrechnung.import.showcontent,invoice_type:"
msgid "Supplier Invoice"
msgstr "Supplier Invoice"
msgctxt "model:account_invoice_xrechnung.import,name:"
msgid "eDocument Import"
msgstr "eDocument Import"
msgctxt "wizard_button:account_invoice_xrechnung.import,start,end:"
msgid "Cancel"
msgstr "Cancel"
msgctxt "wizard_button:account_invoice_xrechnung.import,start,doupload:"
msgid "Upload"
msgstr "Upload"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,end:"
msgid "Cancel"
msgstr "Cancel"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,start:"
msgid "Back"
msgstr "Back"
msgctxt "wizard_button:account_invoice_xrechnung.import,showcontent,doimport:"
msgid "Import"
msgstr "Import"

13
menu.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of the account-invoice-xrechnung-module
from m-ds for Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<menuitem id="menu_import" action="act_import_wiz"
icon="tryton-launch"
parent="account_invoice.menu_invoices"/>
</data>
</tryton>

114
tests/facturx-extended.xml Normal file
View file

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>RE2024.01234</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20240617</udt:DateTimeString>
</ram:IssueDateTime>
<ram:IncludedNote>
<ram:Content>Some notes to the customer.</ram:Content>
</ram:IncludedNote>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Name of Product 1</ram:Name>
<ram:Description>Description of Product 1</ram:Description>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount currencyID="EUR">1350.00</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="C62">1.0</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount currencyID="EUR">1350.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>Name of the Comany</ram:Name>
<ram:SpecifiedLegalOrganization>
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress>
<ram:PostcodeCode>12345</ram:PostcodeCode>
<ram:LineOne>Street of Company No 1</ram:LineOne>
<ram:CityName>Berlin</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
<ram:CountrySubDivisionName>Berlin</ram:CountrySubDivisionName>
</ram:PostalTradeAddress>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Customer Company</ram:Name>
<ram:SpecifiedLegalOrganization>
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress>
<ram:PostcodeCode>23456</ram:PostcodeCode>
<ram:LineOne>Address Line 1</ram:LineOne>
<ram:LineTwo>Address Line 2</ram:LineTwo>
<ram:CityName>Potsdam</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
<ram:CountrySubDivisionName>Brandenburg</ram:CountrySubDivisionName>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
<ram:BuyerOrderReferencedDocument>
<ram:IssuerAssignedID/>
</ram:BuyerOrderReferencedDocument>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery>
</ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement>
<ram:PaymentReference>RE2024.01234</ram:PaymentReference>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>30</ram:TypeCode>
<ram:Information>Wire transfer</ram:Information>
<ram:PayeePartyCreditorFinancialAccount>
<ram:IBANID>DE02300209000106531065</ram:IBANID>
<ram:AccountName>mbs</ram:AccountName>
</ram:PayeePartyCreditorFinancialAccount>
<ram:PayeeSpecifiedCreditorFinancialInstitution>
<ram:BICID>WELADED1PMB</ram:BICID>
</ram:PayeeSpecifiedCreditorFinancialInstitution>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount currencyID="EUR">256.5</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>1350</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradePaymentTerms>
<ram:DueDateDateTime>
<udt:DateTimeString format="102">20240701</udt:DateTimeString>
</ram:DueDateDateTime>
<ram:PartialPaymentAmount currencyID="EUR">1606.50</ram:PartialPaymentAmount>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount currencyID="EUR">1350.00</ram:LineTotalAmount>
<ram:TaxBasisTotalAmount currencyID="EUR">1350.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">256.5</ram:TaxTotalAmount>
<ram:GrandTotalAmount currencyID="EUR">1606.50</ram:GrandTotalAmount>
<ram:DuePayableAmount currencyID="EUR">1606.50</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

View file

@ -3,6 +3,7 @@
# from m-ds for Tryton. The COPYRIGHT file at the top level of # from m-ds for Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
import os.path
from decimal import Decimal from decimal import Decimal
from datetime import date from datetime import date
from facturx import get_facturx_xml_from_pdf from facturx import get_facturx_xml_from_pdf
@ -109,6 +110,57 @@ class InvoiceTestCase(ModuleTestCase):
self.assertEqual(len(inv_lst.move.lines), 3) self.assertEqual(len(inv_lst.move.lines), 3)
return inv_lst return inv_lst
@with_transaction()
def test_wiz_import_single_xml(self):
""" run import wizard, upload a single file
"""
pool = Pool()
ImportWiz = pool.get(
'account_invoice_xrechnung.import', type='wizard')
company1 = create_company('m-ds')
with Transaction().set_context({
'company': company1.id,
'active_model': 'party.party'}):
(sess_id, start_state, end_state) = ImportWiz.create()
w_obj = ImportWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
# run start
result = ImportWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
step_values = {
x: result['view']['defaults'][x]
for x in result['view']['defaults'].keys()
if '.' not in x}
self.assertEqual(step_values, {
'company': company1.id,
'invoice_type': 'in',
'file_': None})
# step 1: upload xml-file
with open(os.path.join(
os.path.split(__file__)[0],
'facturx-extended.xml'), 'rb') as fhdl:
step_values['file_'] = fhdl.read()
for i in step_values.keys():
setattr(w_obj.start, i, step_values[i])
result = ImportWiz.execute(
sess_id, {start_state: step_values}, 'doupload')
step_values = {
x: result['view']['defaults'][x]
for x in result['view']['defaults'].keys()
if '.' not in x}
self.assertEqual(step_values, {
'company': company1.id,
'invoice_type': 'in'})
@with_transaction() @with_transaction()
def test_xrechnung_configuration(self): def test_xrechnung_configuration(self):
""" test configuration """ test configuration

View file

@ -8,4 +8,6 @@ xml:
message.xml message.xml
configuration.xml configuration.xml
wizard_runreport.xml wizard_runreport.xml
wizard_import.xml
xreport.xml xreport.xml
menu.xml

View file

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of the account-invoice-xrechnung-module
from m-ds for Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="4">
<label name="invoice_type"/>
<field name="invoice_type"/>
<label name="company"/>
<field name="company"/>
</form>

View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of the account-invoice-xrechnung-module
from m-ds for Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="4">
<label name="invoice_type"/>
<field name="invoice_type"/>
<label name="company"/>
<field name="company"/>
<label name="file_"/>
<field name="file_" colspan="3"/>
</form>

128
wizard_import.py Normal file
View file

@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
# This file is part of the account-invoice-xrechnung-module
# from m-ds for Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelView, fields
from trytond.wizard import (
Wizard, StateView, StateTransition, StateAction, Button)
from trytond.transaction import Transaction
from trytond.pyson import Bool, Eval
sel_invoice_type = [
('out', 'Customer Invoice'),
('in', 'Supplier Invoice')]
class ImportXmlStart(ModelView):
'eDocument Import'
__name__ = 'account_invoice_xrechnung.import.start'
company = fields.Many2One(
string='Company', model_name='company.company', required=True,
readonly=True)
invoice_type = fields.Selection(
string='Invoice Type', required=True,
selection=sel_invoice_type)
file_ = fields.Binary(
string="ZIP or XML-File", required=True)
# end ImportStart
class ImportXmlShowcontent(ModelView):
'eDocument Import'
__name__ = 'account_invoice_xrechnung.import.showcontent'
company = fields.Many2One(
string='Company', model_name='company.company', required=True,
readonly=True)
invoice_type = fields.Selection(
string='Invoice Type', required=True, readonly=True,
selection=sel_invoice_type)
# end ImportXmlShowcontent
class ImportXml(Wizard):
'eDocument Import'
__name__ = 'account_invoice_xrechnung.import'
start_state = 'start'
start = StateView(
model_name='account_invoice_xrechnung.import.start',
view='account_invoice_xrechnung.import_start_form',
buttons=[
Button(string='Cancel', state='end', icon='tryton-cancel'),
Button(
string='Upload', state='doupload', icon='tryton-open',
states={'readonly': ~Bool(Eval('file_'))}),
])
doupload = StateTransition()
showcontent = StateView(
model_name='account_invoice_xrechnung.import.showcontent',
view='account_invoice_xrechnung.import_content_form',
buttons=[
Button(string='Cancel', state='end', icon='tryton-cancel'),
Button(string='Back', state='start', icon='tryton-back'),
Button(string='Import', state='doimport', icon='tryton-open'),
])
doimport = StateTransition()
invoices_out = StateAction('account_invoice.act_invoice_out_form')
invoices_in = StateAction('account_invoice.act_invoice_in_form')
def default_start(self, fields):
""" fill fields of start-form
"""
context = Transaction().context
inv_type = getattr(getattr(
self, 'showcontent', {}), 'invoice_type', None)
result = {
'company': context.get('company', None),
'invoice_type': inv_type if inv_type else 'in',
'file_': getattr(self.start, 'file_', None)}
return result
def default_showcontent(self, fields):
""" fill fields of start-form
"""
context = Transaction().context
inv_type = getattr(self.start, 'invoice_type')
result = {
'company': context.get('company', None),
'invoice_type': inv_type if inv_type else 'in'}
return result
def transition_doupload(self):
""" load file, detect content
"""
self.showcontent.company = self.start.company
self.showcontent.invoice_type = self.start.invoice_type
return 'showcontent'
def transition_doimport(self):
""" create invoices
"""
return 'end'
def do_invoices_out(self, action):
""" show created customer invoices
"""
data = {}
data['res_id'] = []
action['views'].reverse()
return action, data
def do_invoices_in(self, action):
""" show created supplier invoices
"""
data = {}
data['res_id'] = []
action['views'].reverse()
return action, data
# end ImportXml

33
wizard_import.xml Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<!-- This file is part of the account-invoice-xrechnung-module
from m-ds for Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="import_start_form">
<field name="model">account_invoice_xrechnung.import.start</field>
<field name="type">form</field>
<field name="priority" eval="10"/>
<field name="name">import_start_form</field>
</record>
<record model="ir.ui.view" id="import_content_form">
<field name="model">account_invoice_xrechnung.import.showcontent</field>
<field name="type">form</field>
<field name="priority" eval="20"/>
<field name="name">import_content_form</field>
</record>
<record model="ir.action.wizard" id="act_import_wiz">
<field name="name">eDocument Import</field>
<field name="wiz_name">account_invoice_xrechnung.import</field>
</record>
<record model="ir.action.keyword" id="act_import_wiz-keyword">
<field name="keyword">form_action</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="act_import_wiz"/>
</record>
</data>
</tryton>