From 1d3db3394dd017bcfd7d6a72fd1d75ab3b457587 Mon Sep 17 00:00:00 2001 From: Olivier Nibart Date: Tue, 31 Dec 2024 10:25:35 +0100 Subject: [PATCH] [14.0][IMP] sale_automatic_workflow: add send invoice option - backport from 16.0 --- sale_automatic_workflow/README.rst | 2 + .../data/automatic_workflow_data.xml | 13 +++++ .../i18n/sale_automatic_workflow.pot | 24 ++++++++- .../models/automatic_workflow_job.py | 51 +++++++++++++++++++ .../models/sale_workflow_process.py | 12 +++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 3 +- sale_automatic_workflow/tests/common.py | 13 +++++ .../tests/test_automatic_workflow.py | 46 +++++++++++++++++ .../views/sale_workflow_process_view.xml | 29 +++++++++++ 11 files changed, 193 insertions(+), 2 deletions(-) diff --git a/sale_automatic_workflow/README.rst b/sale_automatic_workflow/README.rst index c1457ee99ef..c6fea6f4d04 100644 --- a/sale_automatic_workflow/README.rst +++ b/sale_automatic_workflow/README.rst @@ -47,6 +47,7 @@ A workflow can: * Send order confirmation mail (only when order confirmed) * Create an invoice * Validate the invoice + * Send the invoice via e-mail * Confirm the picking This module is used by Magentoerpconnect and Prestashoperpconnect. @@ -91,6 +92,7 @@ Contributors * Akim Juillerat * Thomas Fossoul * Phuc Tran Thanh +* John Herholz Other credits ~~~~~~~~~~~~~ diff --git a/sale_automatic_workflow/data/automatic_workflow_data.xml b/sale_automatic_workflow/data/automatic_workflow_data.xml index d016c54e6d0..065a706abd6 100644 --- a/sale_automatic_workflow/data/automatic_workflow_data.xml +++ b/sale_automatic_workflow/data/automatic_workflow_data.xml @@ -34,6 +34,14 @@ >[('state', '=', 'draft'), ('posted_before', '=', False)] + + Automatic Workflow Send Invoice Filter + account.move + [('state', '=', 'posted'), ('is_move_sent', '=', False), ('move_type', '=', 'out_invoice')] + + Automatic Workflow Sale Done Filter sale.order @@ -66,6 +74,11 @@ name="validate_invoice_filter_id" eval="automatic_workflow_validate_invoice_filter" /> + + diff --git a/sale_automatic_workflow/i18n/sale_automatic_workflow.pot b/sale_automatic_workflow/i18n/sale_automatic_workflow.pot index 888a18aa1da..338e2a25d0e 100644 --- a/sale_automatic_workflow/i18n/sale_automatic_workflow.pot +++ b/sale_automatic_workflow/i18n/sale_automatic_workflow.pot @@ -6,6 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-31 11:29+0000\n" +"PO-Revision-Date: 2024-12-31 11:29+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -59,6 +61,11 @@ msgstr "" msgid "Automatic Workflow Sale Done Filter" msgstr "" +#. module: sale_automatic_workflow +#: model:ir.filters,name:sale_automatic_workflow.automatic_workflow_send_invoice_filter +msgid "Automatic Workflow Send Invoice Filter" +msgstr "" + #. module: sale_automatic_workflow #: model:ir.filters,name:sale_automatic_workflow.automatic_workflow_validate_invoice_filter msgid "Automatic Workflow Validate Invoice Filter" @@ -279,6 +286,21 @@ msgid "" "pickings..." msgstr "" +#. module: sale_automatic_workflow +#: model:ir.model.fields,field_description:sale_automatic_workflow.field_sale_workflow_process__send_invoice +msgid "Send Invoice" +msgstr "" + +#. module: sale_automatic_workflow +#: model:ir.model.fields,field_description:sale_automatic_workflow.field_sale_workflow_process__send_invoice_filter_id +msgid "Send Invoice Filter" +msgstr "" + +#. module: sale_automatic_workflow +#: model:ir.model.fields,field_description:sale_automatic_workflow.field_sale_workflow_process__send_invoice_filter_domain +msgid "Send Invoice Filter Domain" +msgstr "" + #. module: sale_automatic_workflow #: model:ir.model.fields,field_description:sale_automatic_workflow.field_sale_workflow_process__send_order_confirmation_mail msgid "Send order confirmation mail" @@ -355,4 +377,4 @@ msgstr "" #: code:addons/sale_automatic_workflow/models/sale_order.py:0 #, python-format msgid "Workflow Warning" -msgstr "" +msgstr "" \ No newline at end of file diff --git a/sale_automatic_workflow/models/automatic_workflow_job.py b/sale_automatic_workflow/models/automatic_workflow_job.py index 2f1cfba0665..f86a51a1d2f 100644 --- a/sale_automatic_workflow/models/automatic_workflow_job.py +++ b/sale_automatic_workflow/models/automatic_workflow_job.py @@ -95,6 +95,53 @@ def _validate_invoices(self, validate_invoice_filter): invoice.with_company(invoice.company_id), validate_invoice_filter ) + def _do_send_invoice(self, invoice, domain_filter): + """Send an invoice by email, re-filter to ensure + it has not been sent since""" + if not self.env["account.move"].search_count( + [("id", "=", invoice.id)] + domain_filter + ): + return "{} {} job bypassed".format(invoice.display_name, invoice) + + # take the context from the actual action_invoice_sent method + action = invoice.action_invoice_sent() + action_context = action["context"] + + # Create the email using the wizard + invoice_send_wizard = ( + self.env["account.invoice.send"] + .with_context( + action_context, + mark_invoice_as_sent=True, + active_ids=[invoice.id], + force_email=True, + ) + .create( + { + "is_print": False, + "composition_mode": "comment", + "model": "account.move", + "res_id": invoice.id, + } + ) + ) + + invoice_send_wizard.onchange_is_email() + invoice_send_wizard._send_email() + + return "{} {} sent invoice successfully".format(invoice.display_name, invoice) + + @api.model + def _send_invoices(self, send_invoice_filter): + move_obj = self.env["account.move"] + invoices = move_obj.search(send_invoice_filter) + _logger.debug("Invoices to send: %s", invoices.ids) + for invoice in invoices: + with savepoint(self.env.cr): + self._do_send_invoice( + invoice.with_company(invoice.company_id), send_invoice_filter + ) + def _do_validate_picking(self, picking, domain_filter): """Validate a stock.picking""" picking.validate_picking() @@ -190,6 +237,10 @@ def run_with_workflow(self, sale_workflow): safe_eval(sale_workflow.validate_invoice_filter_id.domain) + workflow_domain ) + if sale_workflow.send_invoice: + self._send_invoices( + safe_eval(sale_workflow.send_invoice_filter_id.domain) + workflow_domain + ) if sale_workflow.sale_done: self._sale_done( safe_eval(sale_workflow.sale_done_filter_id.domain) + workflow_domain diff --git a/sale_automatic_workflow/models/sale_workflow_process.py b/sale_automatic_workflow/models/sale_workflow_process.py index 0615648ca46..34dffb1a406 100644 --- a/sale_automatic_workflow/models/sale_workflow_process.py +++ b/sale_automatic_workflow/models/sale_workflow_process.py @@ -54,6 +54,11 @@ def _default_filter(self, xmlid): string="Validate Invoice Filter Domain", related="validate_invoice_filter_id.domain", ) + send_invoice = fields.Boolean() + send_invoice_filter_domain = fields.Text( + string="Send Invoice Filter Domain", + related="send_invoice_filter_id.domain", + ) validate_picking = fields.Boolean(string="Confirm and Transfer Picking") picking_filter_domain = fields.Text( string="Picking Filter Domain", related="picking_filter_id.domain" @@ -113,6 +118,13 @@ def _default_filter(self, xmlid): "sale_automatic_workflow." "automatic_workflow_validate_invoice_filter" ), ) + send_invoice_filter_id = fields.Many2one( + "ir.filters", + string="Send Invoice Filter", + default=lambda self: self._default_filter( + "sale_automatic_workflow." "automatic_workflow_send_invoice_filter" + ), + ) sale_done_filter_id = fields.Many2one( "ir.filters", string="Sale Done Filter", diff --git a/sale_automatic_workflow/readme/CONTRIBUTORS.rst b/sale_automatic_workflow/readme/CONTRIBUTORS.rst index 64fff8040a9..324b53be26d 100644 --- a/sale_automatic_workflow/readme/CONTRIBUTORS.rst +++ b/sale_automatic_workflow/readme/CONTRIBUTORS.rst @@ -9,3 +9,4 @@ * Akim Juillerat * Thomas Fossoul * Phuc Tran Thanh +* John Herholz diff --git a/sale_automatic_workflow/readme/DESCRIPTION.rst b/sale_automatic_workflow/readme/DESCRIPTION.rst index d1df9a9a320..05496fd95bf 100644 --- a/sale_automatic_workflow/readme/DESCRIPTION.rst +++ b/sale_automatic_workflow/readme/DESCRIPTION.rst @@ -17,6 +17,7 @@ A workflow can: * Send order confirmation mail (only when order confirmed) * Create an invoice * Validate the invoice + * Send the invoice via e-mail * Confirm the picking This module is used by Magentoerpconnect and Prestashoperpconnect. diff --git a/sale_automatic_workflow/static/description/index.html b/sale_automatic_workflow/static/description/index.html index 7d09ac78cf9..a999e716f2a 100644 --- a/sale_automatic_workflow/static/description/index.html +++ b/sale_automatic_workflow/static/description/index.html @@ -1,4 +1,3 @@ - @@ -387,6 +386,7 @@

Sale Automatic Workflow

  • Send order confirmation mail (only when order confirmed)
  • Create an invoice
  • Validate the invoice
  • +
  • Send the invoice via e-mail
  • Confirm the picking
  • @@ -438,6 +438,7 @@

    Contributors

  • Akim Juillerat <akim.juillerat@camptocamp.com>
  • Thomas Fossoul <thomas@niboo.com>
  • Phuc Tran Thanh <phuc@trobz.com>
  • +
  • John Herholz <j.longneck@gmail.com>
  • diff --git a/sale_automatic_workflow/tests/common.py b/sale_automatic_workflow/tests/common.py index 2142b8e9e07..1165767f83a 100644 --- a/sale_automatic_workflow/tests/common.py +++ b/sale_automatic_workflow/tests/common.py @@ -10,6 +10,18 @@ class TestCommon(SavepointCase): def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.user = cls.env["res.users"].create( + { + "name": "Sales Person", + "login": "salesperson", + "password": "salesperson", + "groups_id": [ + (4, cls.env.ref("sales_team.group_sale_manager").id), + (4, cls.env.ref("account.group_account_manager").id), + ], + } + ) + cls.user.partner_id.email = "salesperson@example.com" class TestAutomaticWorkflowMixin(object): @@ -79,6 +91,7 @@ def create_full_automatic(self, override=None): "validate_picking": True, "create_invoice": True, "validate_invoice": True, + "send_invoice": True, "invoice_date_is_order_date": True, } ) diff --git a/sale_automatic_workflow/tests/test_automatic_workflow.py b/sale_automatic_workflow/tests/test_automatic_workflow.py index e253205f7b8..75dd1dc9973 100644 --- a/sale_automatic_workflow/tests/test_automatic_workflow.py +++ b/sale_automatic_workflow/tests/test_automatic_workflow.py @@ -7,6 +7,7 @@ from odoo import fields from odoo.exceptions import UserError from odoo.tests import tagged +from odoo.tools.safe_eval import safe_eval from .common import TestAutomaticWorkflowMixin, TestCommon @@ -234,3 +235,48 @@ def test_create_payment_with_invoice_currency_id(self): ) self.assertTrue(payment_id) self.assertEqual(invoice.currency_id.id, payment_id.currency_id.id) + + def test_automatic_invoice_send_mail(self): + workflow = self.create_full_automatic() + workflow.send_invoice = False + sale = self.create_sale_order(workflow) + sale.user_id = self.user.id + sale._onchange_workflow_process_id() + self.run_job() + invoice = sale.invoice_ids + invoice.message_subscribe(partner_ids=[invoice.partner_id.id]) + invoice.company_id.invoice_is_email = True + previous_message_ids = invoice.message_ids + workflow.send_invoice = True + sale._onchange_workflow_process_id() + self.run_job() + + new_messages = self.env["mail.message"].search( + [ + ("id", "in", invoice.message_ids.ids), + ("id", "not in", previous_message_ids.ids), + ] + ) + + self.assertTrue( + new_messages.filtered( + lambda x: x.subtype_id == self.env.ref("mail.mt_comment") + ) + ) + + def test_job_bypassing(self): + """We can only test send_invoice for now as it is the only option + to actually implement this.""" + workflow = self.create_full_automatic() + workflow_job = self.env["automatic.workflow.job"] + sale = self.create_sale_order(workflow) + sale._onchange_workflow_process_id() + send_invoice_filter = safe_eval(workflow.send_invoice_filter_id.domain) + + # Trigger everything, then check if send invoice jobs is bypassed + self.run_job() + + invoice = sale.invoice_ids + res_send_invoice = workflow_job._do_send_invoice(invoice, send_invoice_filter) + + self.assertIn("job bypassed", res_send_invoice) diff --git a/sale_automatic_workflow/views/sale_workflow_process_view.xml b/sale_automatic_workflow/views/sale_workflow_process_view.xml index d816613a6d7..ac7e1bbcd34 100644 --- a/sale_automatic_workflow/views/sale_workflow_process_view.xml +++ b/sale_automatic_workflow/views/sale_workflow_process_view.xml @@ -127,6 +127,35 @@ />
    + +