From a8717234b8cb4b010185aa1f3a5083939cd34a04 Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Thu, 14 Jul 2022 11:53:56 +0200 Subject: [PATCH 1/3] [REF] l10n_it_fatturapa_out: Invoice used in tests is open when validated --- .../tests/fatturapa_common.py | 26 ++++--------------- .../tests/test_fatturapa_xml_validation.py | 12 +++++++++ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/l10n_it_fatturapa_out/tests/fatturapa_common.py b/l10n_it_fatturapa_out/tests/fatturapa_common.py index f2ffcaa5b4ce..c9f6f4eb436a 100644 --- a/l10n_it_fatturapa_out/tests/fatturapa_common.py +++ b/l10n_it_fatturapa_out/tests/fatturapa_common.py @@ -233,28 +233,12 @@ def getFile(self, filename, module_name=None): return self.getFilePath(path) def _create_invoice(self): - return self.invoice_model.create( - { - "name": "Test Invoice", - "journal_id": self.sales_journal.id, - "partner_id": self.res_partner_fatturapa_0.id, - "move_type": "out_invoice", - "invoice_line_ids": [ - ( - 0, - 0, - { - "account_id": self.a_sale.id, - "product_id": self.product_product_10.id, - "name": self.product_product_10.name, - "quantity": 1, - "price_unit": 1, - "tax_ids": [(6, 0, {self.tax_22.id})], - }, - ), - ], - } + invoice = self.init_invoice( + "out_invoice", + partner=self.res_partner_fatturapa_0, + products=self.product_product_10, ) + return invoice def _create_e_invoice(self): invoice = self._create_invoice() diff --git a/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py b/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py index 2ee5b78cea73..ac64e1e76a64 100644 --- a/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py +++ b/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py @@ -947,3 +947,15 @@ def test_trasmittente_xml_export(self): self.set_e_invoice_file_id(attachment, "IT03297040366_00019.xml") xml_content = base64.decodebytes(attachment.datas) self.check_content(xml_content, "IT03297040366_00019.xml") + + def test_validate_invoice(self): + """ + Check that the invoice used for tests + is posted when validated. + """ + invoice = self._create_invoice() + self.assertEqual(invoice.state, "draft") + + invoice.action_post() + + self.assertEqual(invoice.state, "posted") From 86d046c84b9b732ca943543e212ad8d926ca7a48 Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Thu, 14 Jul 2022 10:43:57 +0200 Subject: [PATCH 2/3] [IMP] l10n_it_sdi_channel: One button to validate, export and send --- l10n_it_sdi_channel/__manifest__.py | 1 + l10n_it_sdi_channel/models/__init__.py | 1 + l10n_it_sdi_channel/models/account_move.py | 51 ++++++++++++ l10n_it_sdi_channel/models/company.py | 6 ++ l10n_it_sdi_channel/security/security.xml | 3 + l10n_it_sdi_channel/tests/__init__.py | 3 + .../tests/test_account_invoice.py | 81 +++++++++++++++++++ .../views/account_move_views.xml | 36 +++++++++ l10n_it_sdi_channel/views/company_view.xml | 9 +++ 9 files changed, 191 insertions(+) create mode 100644 l10n_it_sdi_channel/models/account_move.py create mode 100644 l10n_it_sdi_channel/tests/__init__.py create mode 100644 l10n_it_sdi_channel/tests/test_account_invoice.py create mode 100644 l10n_it_sdi_channel/views/account_move_views.xml diff --git a/l10n_it_sdi_channel/__manifest__.py b/l10n_it_sdi_channel/__manifest__.py index 891dc7720e9a..56bfcfb70cb4 100644 --- a/l10n_it_sdi_channel/__manifest__.py +++ b/l10n_it_sdi_channel/__manifest__.py @@ -25,6 +25,7 @@ "data": [ "security/ir.model.access.csv", "security/security.xml", + "views/account_move_views.xml", "views/sdi_view.xml", "views/company_view.xml", "views/fatturapa_attachment_views.xml", diff --git a/l10n_it_sdi_channel/models/__init__.py b/l10n_it_sdi_channel/models/__init__.py index 6523ee902c8c..8147d519b05a 100644 --- a/l10n_it_sdi_channel/models/__init__.py +++ b/l10n_it_sdi_channel/models/__init__.py @@ -1,5 +1,6 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import account_move from . import fatturapa_attachment_out from . import company from . import sdi diff --git a/l10n_it_sdi_channel/models/account_move.py b/l10n_it_sdi_channel/models/account_move.py new file mode 100644 index 000000000000..02d2545681c8 --- /dev/null +++ b/l10n_it_sdi_channel/models/account_move.py @@ -0,0 +1,51 @@ +# Copyright 2022 Simone Rubino - TAKOBI +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountMove(models.Model): + _inherit = "account.move" + + def action_open_export_send_sdi(self): + """Validate, export and send to SdI the invoices.""" + # Validate + self.action_post() + + # Export + export_action = self.env["ir.actions.act_window"]._for_xml_id( + "l10n_it_fatturapa_out.action_wizard_export_fatturapa", + ) + export_wizard_model = export_action.get("res_model") + export_wizard = ( + self.env[export_wizard_model] + .with_context( + active_model=self._name, + active_ids=self.ids, + ) + .create([{}]) + ) + export_result = export_wizard.exportFatturaPA() + # Ensure the link of the invoice to its attachment before exporting: + # otherwise an error during the export might break the link + self.flush( + fnames=[ + "fatturapa_attachment_out_id", + ], + records=self, + ) + + # Get the exported attachments + attachment_model = self.env[export_result.get("res_model")] + exported_attachments_domain = export_result.get("domain") + if not exported_attachments_domain: + exported_attachments_domain = [ + ("id", "=", export_result.get("res_id")), + ] + exported_attachments = attachment_model.search( + exported_attachments_domain, + ) + + # Send + send_result = exported_attachments.send_to_sdi() + return send_result diff --git a/l10n_it_sdi_channel/models/company.py b/l10n_it_sdi_channel/models/company.py index dfe0217cc412..f3a684da77db 100644 --- a/l10n_it_sdi_channel/models/company.py +++ b/l10n_it_sdi_channel/models/company.py @@ -32,3 +32,9 @@ class AccountConfigSettings(models.TransientModel): related="company_id.e_invoice_user_id", readonly=False, ) + group_sdi_channel_validate_send = fields.Boolean( + string="Validate, export and send invoices", + help="Allow users to validate, export and send invoices to SdI " + "in one click.", + implied_group="l10n_it_sdi_channel.res_groups_validate_send", + ) diff --git a/l10n_it_sdi_channel/security/security.xml b/l10n_it_sdi_channel/security/security.xml index 62bc9515d6a3..b0739be9c24c 100644 --- a/l10n_it_sdi_channel/security/security.xml +++ b/l10n_it_sdi_channel/security/security.xml @@ -10,4 +10,7 @@ >['|',('company_id','=',False),('company_id','in',company_ids)] + + Validate, export and send invoices + diff --git a/l10n_it_sdi_channel/tests/__init__.py b/l10n_it_sdi_channel/tests/__init__.py new file mode 100644 index 000000000000..6b95a3395eeb --- /dev/null +++ b/l10n_it_sdi_channel/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_account_invoice diff --git a/l10n_it_sdi_channel/tests/test_account_invoice.py b/l10n_it_sdi_channel/tests/test_account_invoice.py new file mode 100644 index 000000000000..cb9e165b5d3a --- /dev/null +++ b/l10n_it_sdi_channel/tests/test_account_invoice.py @@ -0,0 +1,81 @@ +# Copyright 2022 Simone Rubino - TAKOBI +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import tagged + +from odoo.addons.l10n_it_fatturapa_out.tests.fatturapa_common import FatturaPACommon + + +@tagged("post_install", "-at_install") +class TestAccountInvoice(FatturaPACommon): + def setUp(self): + super().setUp() + # XXX - a company named "YourCompany" alread exists + # we move it out of the way but we should do better here + self.env.company.sudo().search([("name", "=", "YourCompany")]).write( + {"name": "YourCompany_"} + ) + self.env.company.name = "YourCompany" + self.env.company.vat = "IT06363391001" + self.env.company.fatturapa_art73 = True + self.env.company.partner_id.street = "Via Milano, 1" + self.env.company.partner_id.city = "Roma" + self.env.company.partner_id.state_id = self.env.ref("base.state_us_2").id + self.env.company.partner_id.zip = "00100" + self.env.company.partner_id.phone = "06543534343" + self.env.company.email = "info@yourcompany.example.com" + self.env.company.partner_id.country_id = self.env.ref("base.it").id + self.env.company.fatturapa_fiscal_position_id = self.env.ref( + "l10n_it_fatturapa.fatturapa_RF01" + ).id + + def test_action_open_export_send_sdi(self): + """ + Check that the "Validate, export and send to SdI" button + validates the invoice and exports the attachment. + """ + # Arrange: create a draft invoice with no attachment + invoice = self._create_invoice() + self.assertEqual(invoice.state, "draft") + self.assertFalse(invoice.fatturapa_attachment_out_id) + + # Act: open, export and send. + # This raises an exception because there is no channel, + # we can't create a channel yet + # because channel types are defined by depending modules + with self.assertRaises(ValueError) as ve: + invoice.action_open_export_send_sdi() + exc_message = ve.exception.args[0] + + # Assert: we are missing the SdI channel, + # but invoice is validated and attachment has been created + self.assertIn("Expected singleton", exc_message) + self.assertIn("sdi.channel", exc_message) + self.assertEqual(invoice.state, "posted") + self.assertTrue(invoice.fatturapa_attachment_out_id) + + def test_action_open_export_send_sdi_ui(self): + """ + Check that the "Validate, export and send to SdI" button + clicked in the UI (where an exception causes a ROLLBACK) + does not validate the invoice or export the attachment. + """ + # Arrange: create a draft invoice with no attachment + invoice = self._create_invoice() + self.assertEqual(invoice.state, "draft") + self.assertFalse(invoice.fatturapa_attachment_out_id) + + # Act: open, export and send. + # This raises an exception because there is no channel, + # we can't create a channel yet + # because channel types are defined by depending modules + with self.assertRaises(ValueError) as ve, self.env.cr.savepoint(): + invoice.action_open_export_send_sdi() + exc_message = ve.exception.args[0] + + # Assert: we are missing the SdI channel, + # but invoice is still in draft and attachment has not been created + self.assertIn("Expected singleton", exc_message) + self.assertIn("sdi.channel", exc_message) + self.assertEqual(invoice.state, "draft") + self.assertFalse(invoice.fatturapa_attachment_out_id) diff --git a/l10n_it_sdi_channel/views/account_move_views.xml b/l10n_it_sdi_channel/views/account_move_views.xml new file mode 100644 index 000000000000..f7e48b702da5 --- /dev/null +++ b/l10n_it_sdi_channel/views/account_move_views.xml @@ -0,0 +1,36 @@ + + + + + Add SdI channel edits to invoice's form view + account.move + + + + + + diff --git a/l10n_it_sdi_channel/views/company_view.xml b/l10n_it_sdi_channel/views/company_view.xml index 68799078b5ed..75e071298339 100644 --- a/l10n_it_sdi_channel/views/company_view.xml +++ b/l10n_it_sdi_channel/views/company_view.xml @@ -43,6 +43,15 @@ +
+ +
+
+
From b6b78bb6716afa449e7000b878885db84c7a2261 Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Thu, 14 Jul 2022 11:57:44 +0200 Subject: [PATCH 3/3] [REF] l10n_it_fatturapa_pec: Test one button to validate, export and send --- .../tests/test_e_invoice_send.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/l10n_it_fatturapa_pec/tests/test_e_invoice_send.py b/l10n_it_fatturapa_pec/tests/test_e_invoice_send.py index ad04ab1223b0..7580183ca9f6 100644 --- a/l10n_it_fatturapa_pec/tests/test_e_invoice_send.py +++ b/l10n_it_fatturapa_pec/tests/test_e_invoice_send.py @@ -108,3 +108,20 @@ def test_resend_after_regenerate(self): # Send it again e_invoice.send_to_sdi() self.assertEqual(e_invoice.state, "sent") + + def test_action_open_export_send_sdi(self): + """ + Check that the "Validate, export and send to SdI" button + sends the e-invoice. + """ + # Arrange: create a draft invoice with no attachment + invoice = self._create_invoice() + self._create_fetchmail_pec_server() + self.assertEqual(invoice.state, "draft") + self.assertFalse(invoice.fatturapa_attachment_out_id) + + # Act: open, export and send the invoice + invoice.action_open_export_send_sdi() + e_invoice = invoice.fatturapa_attachment_out_id + + self.assertEqual(e_invoice.state, "sent")