-
-
Notifications
You must be signed in to change notification settings - Fork 309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[14.0][FIX] l10n_it_fatturapa_out: Configurazione in azienda per numero massimo fatture per fattura elettronica #2606
base: 14.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -49,12 +49,26 @@ def _compute_fatturapa_state(self): | |||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def preventive_checks(self): | ||||||||||||||||||||||||||
for invoice in self: | ||||||||||||||||||||||||||
if invoice.state != "posted": | ||||||||||||||||||||||||||
raise UserError( | ||||||||||||||||||||||||||
_("Impossible to generate XML: invoice not posted: %s") | ||||||||||||||||||||||||||
% invoice.name | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not invoice.is_sale_document(): | ||||||||||||||||||||||||||
raise UserError( | ||||||||||||||||||||||||||
_("Impossible to generate XML: not a customer invoice: %s") | ||||||||||||||||||||||||||
% invoice.name | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not invoice.fiscal_document_type_id: | ||||||||||||||||||||||||||
raise UserError( | ||||||||||||||||||||||||||
_( | ||||||||||||||||||||||||||
"Invoice %s fiscal document type must be set", | ||||||||||||||||||||||||||
invoice.name, | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||
invoice.invoice_payment_term_id | ||||||||||||||||||||||||||
and invoice.invoice_payment_term_id.fatturapa_pt_id.code is False | ||||||||||||||||||||||||||
|
@@ -81,6 +95,28 @@ def preventive_checks(self): | |||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not invoice.partner_id.city: | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sto guardando le specifiche in https://www.agenziaentrate.gov.it/portale/web/guest/specifiche-tecniche-versione-1.7 perché il link nel README a https://www.fatturapa.gov.it/export/fatturazione/it/normativa/f-2.htm risponde 404. Il template però prende il campo l10n-italy/l10n_it_fatturapa_out/data/invoice_it_template.xml Lines 464 to 466 in f114377
ma anche del partner della company: l10n-italy/l10n_it_fatturapa_out/data/invoice_it_template.xml Lines 331 to 333 in f114377
e altri, tipo: l10n-italy/l10n_it_fatturapa_out/data/invoice_it_template.xml Lines 338 to 343 in f114377
quindi se vogliamo fare controlli preventivi per non far fallire la generazione del XML, ci sarebbe da controllare il campo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fatto. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏻 |
||||||||||||||||||||||||||
raise UserError(_("Invoice %s partner city must be set", invoice.name)) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not invoice.company_id.partner_id.city: | ||||||||||||||||||||||||||
raise UserError( | ||||||||||||||||||||||||||
_( | ||||||||||||||||||||||||||
"Invoice %s city in our company's partner must be set", | ||||||||||||||||||||||||||
invoice.name, | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||
invoice.company_id.fatturapa_stabile_organizzazione | ||||||||||||||||||||||||||
and not invoice.company_id.fatturapa_stabile_organizzazione.city | ||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||
raise UserError( | ||||||||||||||||||||||||||
_( | ||||||||||||||||||||||||||
"Invoice %s city in our company's Stable Organization must be set", | ||||||||||||||||||||||||||
invoice.name, | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not all( | ||||||||||||||||||||||||||
aml.tax_ids for aml in invoice.invoice_line_ids if aml.product_id | ||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -960,6 +960,7 @@ def test_multicompany_fail(self): | |
company_form = Form(self.env["res.company"].sudo(True)) | ||
company_form.name = "YourCompany 2" | ||
company_form.vat = "IT07973780013" | ||
company_form.city = "Roma" | ||
company_form.fatturapa_fiscal_position_id = ( | ||
self.env.company.fatturapa_fiscal_position_id | ||
) | ||
|
@@ -1199,3 +1200,168 @@ def test_18_xml_export(self): | |
# XML doc to be validated | ||
xml_content = base64.decodebytes(attachment.datas) | ||
self.check_content(xml_content, "IT06363391001_00018.xml") | ||
|
||
def test_max_invoice_in_xml(self): | ||
invoice1_form = Form( | ||
self.env["account.move"].with_context({"default_move_type": "out_invoice"}) | ||
) | ||
invoice1_form.partner_id = self.res_partner_fatturapa_0 | ||
with invoice1_form.line_ids.new() as line_form: | ||
line_form.product_id = self.product_product_10 | ||
line_form.account_id = self.a_sale | ||
line_form.tax_ids.clear() | ||
line_form.tax_ids.add(self.tax_22) | ||
invoice1 = invoice1_form.save() | ||
invoice2 = invoice1.copy() | ||
invoice3 = invoice1.copy() | ||
|
||
invoice1.action_post() | ||
invoice2.action_post() | ||
invoice3.action_post() | ||
invoices = invoice1 | invoice2 | invoice3 | ||
|
||
# partner limited, company limited (expect 3 xml attachments) | ||
self.res_partner_fatturapa_0.max_invoice_in_xml = 1 | ||
self.env.company.max_invoice_in_xml = 2 | ||
res = self.run_wizard(invoices.ids) | ||
attachments = self.attach_model.search(res["domain"]) | ||
self.assertEqual(len(attachments), 3) | ||
attachments.unlink() | ||
|
||
# partner limited, company unlimited (expect 3 xml attachments) | ||
self.res_partner_fatturapa_0.max_invoice_in_xml = 1 | ||
self.env.company.max_invoice_in_xml = 0 | ||
res = self.run_wizard(invoices.ids) | ||
attachments = self.attach_model.search(res["domain"]) | ||
self.assertEqual(len(attachments), 3) | ||
attachments.unlink() | ||
|
||
# partner unlimited, company limited (expect 2 xml attachments) | ||
self.res_partner_fatturapa_0.max_invoice_in_xml = 0 | ||
self.env.company.max_invoice_in_xml = 2 | ||
res = self.run_wizard(invoices.ids) | ||
attachments = self.attach_model.search(res["domain"]) | ||
self.assertEqual(len(attachments), 2) | ||
attachments.unlink() | ||
|
||
# partner unlimited, company unlimited (expect 1 xml attachment) | ||
self.res_partner_fatturapa_0.max_invoice_in_xml = 0 | ||
self.env.company.max_invoice_in_xml = 0 | ||
res = self.run_wizard(invoices.ids) | ||
attachments = self.attach_model.browse(res["res_id"]) | ||
self.assertEqual(len(attachments), 1) | ||
|
||
def test_preventive_checks(self): | ||
invoice_form = Form( | ||
self.env["account.move"].with_context({"default_move_type": "in_invoice"}) | ||
) | ||
invoice_form.partner_id = self.res_partner_fatturapa_0 | ||
invoice_form.invoice_date = fields.Date.today() | ||
with invoice_form.line_ids.new() as line_form: | ||
line_form.product_id = self.product_product_10 | ||
line_form.account_id = self.a_sale | ||
line_form.tax_ids.clear() | ||
line_form.tax_ids.add(self.tax_22) | ||
invoice = invoice_form.save() | ||
|
||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn("invoice not posted", ue.exception.args[0]) | ||
|
||
invoice.action_post() | ||
|
||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. La fattura non dovrebbe essere confermata prima di generare il file XML? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ho aggiunto anche quel controllo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏻 |
||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Puoi chiarire quale dev'essere l'errore sollevato qui e negli There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adesso faccio il controllo anche sul testo del messaggio (che è autoesplicativo). Dovrebbe cambiare con la localizzazione ma i test in github credo possiamo assumere siano in inglese. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏻 |
||
self.assertIn("not a customer invoice", ue.exception.args[0]) | ||
|
||
# everything's fine with this invoice | ||
invoice_form = Form( | ||
self.env["account.move"].with_context({"default_move_type": "out_invoice"}) | ||
) | ||
invoice_form.partner_id = self.res_partner_fatturapa_0 | ||
invoice_form.invoice_payment_term_id = self.account_payment_term | ||
with invoice_form.line_ids.new() as line_form: | ||
line_form.product_id = self.product_product_10 | ||
line_form.account_id = self.a_sale | ||
line_form.tax_ids.clear() | ||
line_form.tax_ids.add(self.tax_22) | ||
invoice = invoice_form.save() | ||
invoice.action_post() | ||
res = self.run_wizard(invoice.ids) | ||
attachments = self.attach_model.browse(res["res_id"]) | ||
self.assertEqual(len(attachments), 1) | ||
|
||
# change/remove something and see if preventive_checks() catches it | ||
invoice = invoice.copy() | ||
fiscal_document_type_id = invoice.fiscal_document_type_id | ||
invoice.fiscal_document_type_id = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn("fiscal document type must be set", ue.exception.args[0]) | ||
invoice.fiscal_document_type_id = fiscal_document_type_id | ||
|
||
invoice = invoice.copy() | ||
pt_id = invoice.invoice_payment_term_id.fatturapa_pt_id | ||
invoice.invoice_payment_term_id.fatturapa_pt_id = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn( | ||
"fiscal payment term must be set for the selected payment term", | ||
ue.exception.args[0], | ||
) | ||
invoice.invoice_payment_term_id.fatturapa_pt_id = pt_id | ||
|
||
invoice = invoice.copy() | ||
pm_id = invoice.invoice_payment_term_id.fatturapa_pm_id | ||
invoice.invoice_payment_term_id.fatturapa_pm_id = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn( | ||
"fiscal payment method must be set for the selected payment term", | ||
ue.exception.args[0], | ||
) | ||
invoice.invoice_payment_term_id.fatturapa_pm_id = pm_id | ||
|
||
invoice = invoice.copy() | ||
city = invoice.partner_id.city | ||
invoice.partner_id.city = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn("city must be set", ue.exception.args[0]) | ||
invoice.partner_id.city = city | ||
|
||
invoice = invoice.copy() | ||
city = invoice.company_id.partner_id.city | ||
invoice.company_id.partner_id.city = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn("city in our company's partner must be set", ue.exception.args[0]) | ||
invoice.company_id.partner_id.city = city | ||
|
||
invoice = invoice.copy() | ||
invoice.company_id.fatturapa_stabile_organizzazione = ( | ||
invoice.company_id.partner_id.copy() | ||
) | ||
city = invoice.company_id.fatturapa_stabile_organizzazione.city | ||
invoice.company_id.fatturapa_stabile_organizzazione.city = False | ||
invoice.action_post() | ||
with self.assertRaises(UserError) as ue: | ||
self.run_wizard(invoice.id) | ||
self.assertIn(invoice.name, ue.exception.args[0]) | ||
self.assertIn( | ||
"city in our company's Stable Organization must be set", | ||
ue.exception.args[0], | ||
) | ||
invoice.company_id.fatturapa_stabile_organizzazione.city = city | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Si potrebbe aggiungere anche un'esportazione che finalmente va a buon fine (giusto per dare soddisfazione alla CI 😄) una volta che abbiamo fatto tutte le configurazioni corrette There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Beh per quello ci sono gli altri test, che lo fanno ripetutamente. Tuttavia, ha senso, una volta fatti i controlli per le fatture sbagliate in partenza (non confermate o passive), verificare che stiamo partendo da una fattura corretta, per poi modificare una cosa alla volta e vedere se preventive_check() fa il suo lavoro, punto per punto. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sto guardando le specifiche in https://www.agenziaentrate.gov.it/portale/web/guest/specifiche-tecniche-versione-1.7 perché il link nel README a https://www.fatturapa.gov.it/export/fatturazione/it/normativa/f-2.htm risponde 404.
Per il nodo
TipoDocumento
vedo che c'è anche un vincolo di lunghezza a 4 caratteri mentre inl10n-italy/l10n_it_fiscal_document_type/models/fiscal_document_type.py
Line 8 in fd80fdc
Visto che con queste modifiche viene aggiunto un controllo dedicato a questo campo, forse sarebbe anche da controllare che la lunghezza sia corretta, che ne pensi?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direi che va modificato dall'altra parte, in l10n_it_fiscal_document_type. Io il controllo lo potrei mettere, ma se la size diventa 4 di là non serve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potrebbe essere che esistano dei document type che possono avere lunghezza 5, ma quelli che si possono inserire in una FE devono invece avere lunghezza 4? Anzi, per la FE vedo nel XSD può avere solo certi valori TD01, ..., TD27
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dovrebbe essere un campo che serve solo a fatturapa, c'è una tabella che sono i valori dell'XML: https://github.com/OCA/l10n-italy/blob/fd80fdc7d4aeef613e7aa193674cd62cf0bd4f7b/l10n_it_fiscal_document_type/data/fiscal.document.type.csv
Non ho idea del perché sia size=5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Se stiamo controllando i dati della fattura in modo che poi la generazione del XML non esploda, allora andrebbe controllato che il codice del
fiscal_document_type_id
rientri tra quelli consentiti nel XSD del XML.Controllare che almeno sia valorizzato il campo
fiscal_document_type_id
è comunque un miglioramento rispetto al codice esistente perché il nodoTipoDocumento
è obbligatorio, quindi per me può andare bene anche così.