diff --git a/l10n_it_sdi_channel/__manifest__.py b/l10n_it_sdi_channel/__manifest__.py index 44102362438e..c16f07a4cc2b 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..d0a4f76e47d8 --- /dev/null +++ b/l10n_it_sdi_channel/models/account_move.py @@ -0,0 +1,43 @@ +# 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() + + # 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 @@ +
+ +
+
+