Skip to content
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][ADD] l10n_it_fatturapa_out: edit invoice sent SDI #2966

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions l10n_it_fatturapa_out/models/account.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Copyright 2014 Davide Corio
# Copyright 2016 Lorenzo Battistini - Agile Business Group

import base64

from lxml import etree

from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.http import request
from odoo.tools.translate import _

fatturapa_attachment_state_mapping = {
Expand Down Expand Up @@ -95,11 +100,243 @@ def preventive_checks(self):
)
return

@api.model
def check_tag(self, new_xml, original_xml, tags, precision=None):
"""
This function check if tag in new xml generated after function write()
is the same of original xml
:param new_xml: new xml generated after function write()
:param original_xml: original xml linked to invoice
:param tags: tags of xml to check
:param precision: precision to apply on text tag for check
:return: True if tags is the same else
"""
for tag in tags:
new_tag_text = new_xml.find(tag) is not None and new_xml.find(tag).text
original_tag_text = (
original_xml.find(tag) is not None and original_xml.find(tag).text
)
if precision:
new_tag_text = "{text:.{precision}f}".format(
text=float(new_tag_text), precision=precision
)
original_tag_text = "{text:.{precision}f}".format(
text=float(original_tag_text), precision=precision
)
if (new_tag_text or "").strip() != (original_tag_text or "").strip():
raise UserError(
_(
"{} isn't equal to tag in file e-invoice already created!".format(
tag[2:]
)
)
)

def check_CessionarioCommittente(self, new_xml, original_xml):
list_tag = [
".//CessionarioCommittente/DatiAnagrafici/IdFiscaleIVA/IdPaese",
".//CessionarioCommittente/DatiAnagrafici/IdFiscaleIVA/IdCodice",
]
self.check_tag(new_xml, original_xml, list_tag)

def check_DatiGeneraliDocumento(self, new_xml, original_xml):
price_precision = self.env["decimal.precision"].precision_get(
"Product Price for XML e-invoices"
)

list_tag = [
".//DatiGeneraliDocumento/Data",
".//DatiGeneraliDocumento/TipoDocumento",
".//DatiGeneraliDocumento/Divisa",
".//DatiGeneraliDocumento/Numero",
]
self.check_tag(new_xml, original_xml, list_tag)
list_tag = [
".//DatiGeneraliDocumento/ImportoTotaleDocumento",
]
self.check_tag(new_xml, original_xml, list_tag, price_precision)

if len(new_xml.findall(".//DatiGeneraliDocumento/DatiRitenuta")) != len(
original_xml.findall(".//DatiGeneraliDocumento/DatiRitenuta")
):
raise UserError(
_(
"{} isn't equal to tag in file e-invoice already created!".format(
"DatiGeneraliDocumento/DatiRitenuta"
)
)
)
lr = 0
for new_line_ritenuta in new_xml.findall(
".//DatiGeneraliDocumento/DatiRitenuta"
):
original_line_ritenuta = original_xml.findall(
".//DatiGeneraliDocumento/DatiRitenuta"
)[lr]
list_tag_DatiRitenuta = [
".//TipoRitenuta",
".//CausalePagamento",
]
self.check_tag(
new_line_ritenuta, original_line_ritenuta, list_tag_DatiRitenuta
)
list_tag_DatiRitenuta = [
".//ImportoRitenuta",
".//AliquotaRitenuta",
]
self.check_tag(
new_line_ritenuta,
original_line_ritenuta,
list_tag_DatiRitenuta,
price_precision,
)
lr += 1

def check_DatiBeniServizi(self, new_xml, original_xml):
price_precision = self.env["decimal.precision"].precision_get(
"Product Price for XML e-invoices"
)
uom_precision = self.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)

if len(new_xml.findall(".//DatiBeniServizi/DettaglioLinee")) != len(
original_xml.findall(".//DatiBeniServizi/DettaglioLinee")
):
raise UserError(
_(
"{} isn't equal to tag in file e-invoice already created!".format(
"DatiBeniServizi/DettaglioLinee"
)
)
)
ld = 0
for new_line_details in new_xml.findall(".//DatiBeniServizi/DettaglioLinee"):
original_line_details = original_xml.findall(
".//DatiBeniServizi/DettaglioLinee"
)[ld]
list_tag_DettaglioLinee = [
".//NumeroLinea",
".//CodiceTipo",
".//CodiceValore",
".//Descrizione",
".//Natura",
".//Ritenuta",
]
self.check_tag(
new_line_details, original_line_details, list_tag_DettaglioLinee
)
list_tag_DettaglioLinee = [
".//Quantita",
]
self.check_tag(
new_line_details,
original_line_details,
list_tag_DettaglioLinee,
uom_precision,
)
list_tag_DettaglioLinee = [
".//PrezzoUnitario",
".//AliquotaIVA",
".//PrezzoTotale",
]
self.check_tag(
new_line_details,
original_line_details,
list_tag_DettaglioLinee,
price_precision,
)
ld += 1

if len(new_xml.findall(".//DatiBeniServizi/DatiRiepilogo")) != len(
original_xml.findall(".//DatiBeniServizi/DatiRiepilogo")
):
raise UserError(
_(
"{} isn't equal to tag in file e-invoice already created!".format(
"DatiBeniServizi/DatiRiepilogo"
)
)
)
lr = 0
for new_line_riepilogo in new_xml.findall(".//DatiBeniServizi/DatiRiepilogo"):
original_line_riepilogo = original_xml.findall(
".//DatiBeniServizi/DatiRiepilogo"
)[lr]
list_tag_DatiRiepilogo = [
".//AliquotaIVA",
".//ImponibileImporto",
".//Imposta",
]
self.check_tag(
new_line_riepilogo,
original_line_riepilogo,
list_tag_DatiRiepilogo,
price_precision,
)
lr += 1

def elements_equal(self, new_xml, original_xml):
self.check_CessionarioCommittente(new_xml, original_xml)
self.check_DatiGeneraliDocumento(new_xml, original_xml)
self.check_DatiBeniServizi(new_xml, original_xml)

def check_move_confirmable(self):
self.ensure_one()

if not self.state == "posted" and not (
request
and request.params.get("method", False)
and request.params["method"] == "action_post"
):
return True
return False

def write(self, vals):
is_draft = {}
for move in self:
is_draft[move.id] = True if move.state == "draft" else False
res = super(AccountInvoice, self).write(vals)
for move in self:
if (
move.is_sale_document()
and move.fatturapa_attachment_out_id
and is_draft[move.id]
and not move.state == "cancel"
and not move.env.context.get("skip_check_xml", False)
and not (
request
and request.params.get("method", False)
and request.params["method"] == "button_draft"
)
):
context_partner = self.env.context.copy()
context_partner.update({"lang": move.partner_id.lang})
context_partner.update(skip_check_xml=True)
fatturapa, progressivo_invio = self.env[
"wizard.export.fatturapa"
].exportInvoiceXML(move.partner_id, [move.id], context=context_partner)
new_xml_content = fatturapa.to_xml(self.env)
original_xml_content = base64.decodebytes(
move.fatturapa_attachment_out_id.datas
)
parser = etree.XMLParser(remove_blank_text=True)
new_xml = etree.fromstring(new_xml_content, parser)
original_xml = etree.fromstring(original_xml_content, parser)
move.elements_equal(new_xml, original_xml)
if move.check_move_confirmable():
move.with_context(skip_check_xml=True).action_post()
return res

def button_draft(self):
for invoice in self:
if (
invoice.fatturapa_state != "error"
and invoice.fatturapa_attachment_out_id
and not self.env.user.has_group(
"l10n_it_fatturapa_out.group_edit_invoice_sent_sdi"
)
):
raise UserError(
_(
Expand Down
8 changes: 8 additions & 0 deletions l10n_it_fatturapa_out/security/res_groups.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@
<field name="name">Allow to force e-invoice export state</field>
<field name="category_id" ref="base.module_category_hidden" />
</record>

<record id="group_edit_invoice_sent_sdi" model="res.groups">
<field name="name">Edit Invoice Sent SDI</field>
<field
name="comment"
>Can reset to draft and then edit invoice sent to SDI</field>
<field name="category_id" ref="base.module_category_hidden" />
</record>
</odoo>
31 changes: 31 additions & 0 deletions l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,3 +1194,34 @@ 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_edit_invoice_sent_sdi(self):
"""
Check e-invoice tags after edit invoice.
"""
invoice = self._create_invoice()
invoice.action_post()
self.run_wizard(invoice.id)
self.assertEqual(invoice.state, "posted")
self.assertTrue(invoice.fatturapa_attachment_out_id.exists())

with self.assertRaises(UserError), self.cr.savepoint():
invoice.with_user(self.account_manager.id).button_draft()

self.account_manager.groups_id += self.env.ref(
"l10n_it_fatturapa_out.group_edit_invoice_sent_sdi"
)
invoice.with_user(self.account_manager.id).button_draft()
self.assertEqual(invoice.state, "draft")

with self.assertRaises(UserError), self.cr.savepoint():
move_form = Form(invoice)
with move_form.invoice_line_ids.edit(0) as line_form:
line_form.price_unit = 800
move_form.save()

move_form = Form(invoice)
move_form.invoice_payment_term_id = self.account_payment_term
move_form.save()
self.assertEqual(invoice.state, "posted")

6 changes: 5 additions & 1 deletion l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,11 @@ def exportInvoiceXML(self, partner, invoice_ids, attach=False, context=None):

# generate attachments (PDF version of invoice)
for inv in invoice_ids:
if not attach and inv.fatturapa_attachment_out_id:
if (
not attach
and inv.fatturapa_attachment_out_id
and not context.get("skip_check_xml", False)
):
raise UserError(
_("E-invoice export file still present for invoice %s.")
% (inv.name or "")
Expand Down
Loading