From 0e100aa28deb3ef8472b3b246f838770f17df844 Mon Sep 17 00:00:00 2001 From: Thierry Ducrest Date: Fri, 15 Sep 2023 13:12:38 +0200 Subject: [PATCH] [FIX] printing_auto_base: don't print twice Fix same record printed multiple times --- printing_auto_base/models/printing_auto.py | 12 ++-- .../models/printing_auto_mixin.py | 9 ++- printing_auto_base/tests/model_test.py | 37 +++++++++++ .../tests/test_printing_auto_base.py | 61 +++++++++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 printing_auto_base/tests/model_test.py diff --git a/printing_auto_base/models/printing_auto.py b/printing_auto_base/models/printing_auto.py index 351f80b3277..d5c7bd5cc4f 100644 --- a/printing_auto_base/models/printing_auto.py +++ b/printing_auto_base/models/printing_auto.py @@ -94,13 +94,13 @@ def _check_condition(self, record): domain = safe_eval(self.condition, {"env": self.env}) return record.filtered_domain(domain) - def _get_content(self, record): + def _get_content(self, records): generate_data_func = getattr( self, f"_generate_data_from_{self.data_source}", None ) content = [] if generate_data_func: - records = self._get_record(record) + records = self._get_record(records) for record in records: content.append(generate_data_func(record)[0]) return content @@ -127,16 +127,16 @@ def _generate_data_from_report(self, record): ) return [data] - def do_print(self, record): + def do_print(self, records): self.ensure_one() - record.ensure_one() behaviour = self._get_behaviour() printer = behaviour["printer"] if self.nbr_of_copies <= 0: return (printer, 0) - if not self._check_condition(record): + records = self._check_condition(records) + if not records: return (printer, 0) if not printer: @@ -145,7 +145,7 @@ def do_print(self, record): ) count = 0 - for content in self._get_content(record): + for content in self._get_content(records): for _n in range(self.nbr_of_copies): printer.print_document(report=None, content=content, **behaviour) count += 1 diff --git a/printing_auto_base/models/printing_auto_mixin.py b/printing_auto_base/models/printing_auto_mixin.py index 5bb0f851b40..715289c52fe 100644 --- a/printing_auto_base/models/printing_auto_mixin.py +++ b/printing_auto_base/models/printing_auto_mixin.py @@ -38,7 +38,6 @@ def _get_printing_auto(self): return self.auto_printing_ids def _do_print_auto(self, printing_auto): - self.ensure_one() printing_auto.ensure_one() printer, count = printing_auto.do_print(self) if count: @@ -63,6 +62,12 @@ def _handle_print_auto(self, printing_auto): def handle_print_auto(self): """Print some report or attachment directly to the corresponding printer.""" self._on_printing_auto_start() + to_print = {} for record in self: for printing_auto in record._get_printing_auto(): - record._handle_print_auto(printing_auto) + if printing_auto not in to_print.keys(): + to_print[printing_auto] = record + else: + to_print[printing_auto] |= record + for printing_auto, records in to_print.items(): + records._handle_print_auto(printing_auto) diff --git a/printing_auto_base/tests/model_test.py b/printing_auto_base/tests/model_test.py new file mode 100644 index 00000000000..3cf729ba9ab --- /dev/null +++ b/printing_auto_base/tests/model_test.py @@ -0,0 +1,37 @@ +from odoo import fields, models + + +def setup_test_model(env, model_cls): + """Pass a test model class and initialize it. + + Courtesy of SBidoul from https://github.com/OCA/mis-builder :) + """ + model_cls._build_model(env.registry, env.cr) + env.registry.setup_models(env.cr) + env.registry.init_models( + env.cr, [model_cls._name], dict(env.context, update_custom_fields=True) + ) + + +def teardown_test_model(env, model_cls): + """Pass a test model class and deinitialize it. + + Courtesy of SBidoul from https://github.com/OCA/mis-builder :) + """ + if not getattr(model_cls, "_teardown_no_delete", False): + del env.registry.models[model_cls._name] + env.registry.setup_models(env.cr) + + +class PrintingAutoTesterChild(models.Model): + _name = "printingauto.tester.child" + + name = fields.Char() + + +class PrintingAutoTester(models.Model): + _name = "printingauto.tester" + _inherit = "printing.auto.mixin" + + name = fields.Char() + child_ids = fields.Many2many("printingauto.tester.child") diff --git a/printing_auto_base/tests/test_printing_auto_base.py b/printing_auto_base/tests/test_printing_auto_base.py index 1e1607b49ae..c88b10d1318 100644 --- a/printing_auto_base/tests/test_printing_auto_base.py +++ b/printing_auto_base/tests/test_printing_auto_base.py @@ -7,10 +7,17 @@ from odoo.exceptions import UserError, ValidationError from .common import PrintingPrinter, TestPrintingAutoCommon, print_document +from .model_test import PrintingAutoTester, PrintingAutoTesterChild, setup_test_model @mock.patch.object(PrintingPrinter, "print_document", print_document) class TestPrintingAutoBase(TestPrintingAutoCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + setup_test_model(cls.env, PrintingAutoTesterChild) + setup_test_model(cls.env, PrintingAutoTester) + def test_check_data_source(self): with self.assertRaises(UserError): self._create_printing_auto_report({"report_id": False}) @@ -90,3 +97,57 @@ def test_do_print(self): printing_auto.condition = "[('name', '=', 'test_printing_auto')]" expected = (self.printer_1, 0) self.assertEqual(expected, printing_auto.do_print(self.record)) + + def test_do_not_print_multiple_time_the_same_record(self): + """Check the same record is not printed multiple times. + + When the 'record_change' field is being used on the printing auto configuration + and 'handle_print_auto' is called from a recrodset. + The same record could be send for printing multiple times. + + """ + printing_auto = self._create_printing_auto_report( + vals={"record_change": "child_ids", "printer_id": self.printer_1.id} + ) + child1 = self.env["printingauto.tester.child"].create({"name": "Child One"}) + child2 = self.env["printingauto.tester.child"].create({"name": "Child Two"}) + parent1 = self.env["printingauto.tester"].create( + { + "name": "Customer One", + "child_ids": [(4, child1.id, 0)], + "auto_printing_ids": [(4, printing_auto.id, 0)], + } + ) + parent2 = self.env["printingauto.tester"].create( + { + "name": "Customer Two", + "child_ids": [(4, child1.id, 0)], + "auto_printing_ids": [(4, printing_auto.id, 0)], + } + ) + parents = parent1 | parent2 + generate_data_from = ( + "odoo.addons.printing_auto_base.models.printing_auto." + "PrintingAuto._generate_data_from_report" + ) + with mock.patch(generate_data_from) as generate_data_from: + # Both parents have the same child only print the child report once + parents.handle_print_auto() + self.assertEqual(generate_data_from.call_count, 1) + generate_data_from.assert_called_with(child1) + generate_data_from.reset_mock() + # Both parents have different childs, print both child reports + parent2.child_ids = [(6, 0, child2.ids)] + parents.handle_print_auto() + self.assertEqual(generate_data_from.call_count, 2) + generate_data_from.assert_has_calls( + [mock.call(child1), mock.call(child2)], any_order=True + ) + generate_data_from.reset_mock() + # THe parents have one child in common and one parent has a 2nd child + parent2.child_ids = [(4, child1.id, 0)] + parents.handle_print_auto() + self.assertEqual(generate_data_from.call_count, 2) + generate_data_from.assert_has_calls( + [mock.call(child1), mock.call(child2)], any_order=True + )