From 9bed83c3fa1761902ba3c1e6a15cd32340fac476 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Fri, 29 Nov 2024 09:51:10 +0100 Subject: [PATCH] [IMP] product_import: Import products asynchronously --- product_import/__manifest__.py | 3 ++ product_import/data/job_function.xml | 14 ++++++ product_import/static/description/index.html | 1 - product_import/tests/common.py | 7 +-- product_import/tests/test_product_import.py | 10 ++++- product_import/wizard/product_import.py | 44 ++++++++++--------- .../tests/test_ubl_catalogue_import.py | 8 +++- 7 files changed, 60 insertions(+), 27 deletions(-) create mode 100644 product_import/data/job_function.xml diff --git a/product_import/__manifest__.py b/product_import/__manifest__.py index 6411437d65..8f570d1383 100644 --- a/product_import/__manifest__.py +++ b/product_import/__manifest__.py @@ -13,9 +13,12 @@ "stock", # OCA/edi "base_business_document_import", + # OCA/queue + "queue_job", ], "data": [ "security/ir.model.access.csv", "wizard/product_import_view.xml", + "data/job_function.xml", ], } diff --git a/product_import/data/job_function.xml b/product_import/data/job_function.xml new file mode 100644 index 0000000000..f41529062d --- /dev/null +++ b/product_import/data/job_function.xml @@ -0,0 +1,14 @@ + + + + product_import + + + + + + _create_update_product + + + + diff --git a/product_import/static/description/index.html b/product_import/static/description/index.html index 6f715fc295..dde7dd5502 100644 --- a/product_import/static/description/index.html +++ b/product_import/static/description/index.html @@ -1,4 +1,3 @@ - diff --git a/product_import/tests/common.py b/product_import/tests/common.py index dac4cb1b48..a69e6ebabd 100644 --- a/product_import/tests/common.py +++ b/product_import/tests/common.py @@ -11,11 +11,12 @@ class TestCommon(SavepointCase): def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) - cls.wiz_model = cls.env["product.import"] + # Execute directly, no job + cls.wiz_model = cls.env["product.import"].with_context(queue_job__no_delay=True) cls.supplier = cls.env["res.partner"].create({"name": "Catalogue Vendor"}) - def _mock(self, method_name): - return mock.patch.object(type(self.wiz_model), method_name) + def _mock(self, method_name, **kw): + return mock.patch.object(type(self.wiz_model), method_name, **kw) @property def wiz_form(self): diff --git a/product_import/tests/test_product_import.py b/product_import/tests/test_product_import.py index 408780a716..80c0b8fac7 100644 --- a/product_import/tests/test_product_import.py +++ b/product_import/tests/test_product_import.py @@ -86,9 +86,15 @@ def test_get_company_id(self): def test_product_import(self): # product.product - products = self.wiz_model._create_products( - self.parsed_catalog, seller=self.supplier + product_obj = self.env["product.product"].with_context(active_test=False) + existing = product_obj.search([], order="id") + + wiz = self.wiz_model.create( + {"product_file": b"", "product_filename": "test_import.xml"} ) + with self._mock("parse_product_catalogue", return_value=self.parsed_catalog): + wiz.import_button() + products = product_obj.search([], order="id") - existing self.assertEqual(len(products), 3) for product, parsed in zip(products, PARSED_CATALOG["products"]): diff --git a/product_import/wizard/product_import.py b/product_import/wizard/product_import.py index ef89714b4c..cd18533aa4 100644 --- a/product_import/wizard/product_import.py +++ b/product_import/wizard/product_import.py @@ -195,14 +195,21 @@ def _prepare_product(self, parsed_product, chatter_msg, seller=None): return product_vals @api.model - def create_product(self, parsed_product, chatter_msg, seller=None): + def _create_update_product(self, parsed_product, seller_id): + """Create / Update a product. + + This method is called from a queue job. + """ + chatter_msg = [] + + seller = self.env["res.partner"].browse(seller_id) product_vals = self._prepare_product(parsed_product, chatter_msg, seller=seller) if not product_vals: return False product = product_vals.pop("recordset", None) if product: product.write(product_vals) - logger.info("Product %d updated", product.id) + logger.debug("Product %s updated", product.default_code) else: product_active = product_vals.pop("active") product = self.env["product.product"].create(product_vals) @@ -211,23 +218,10 @@ def create_product(self, parsed_product, chatter_msg, seller=None): # all characteristics into product.template product.flush() product.action_archive() - logger.info("Product %d created", product.id) - return product + logger.debug("Product %s created", product.default_code) - @api.model - def _create_products(self, catalogue, seller, filename=None): - products = self.env["product.product"].browse() - for product in catalogue.get("products"): - record = self.create_product( - product, - catalogue["chatter_msg"], - seller=seller, - ) - if record: - products |= record - self._bdimport.post_create_or_update(catalogue, seller, doc_filename=filename) - logger.info("Products updated for vendor %d", seller.id) - return products + log_msg = f"Product created/updated {product.id}\n" + "\n".join(chatter_msg) + return log_msg def import_button(self): self.ensure_one() @@ -237,7 +231,17 @@ def import_button(self): raise UserError(_("This catalogue doesn't have any product!")) company_id = self._get_company_id(catalogue) seller = self._get_seller(catalogue) - self.with_context(product_company_id=company_id)._create_products( - catalogue, seller, filename=self.product_filename + wiz = self.with_context(product_company_id=company_id) + # Create products asynchronously + for product_vals in catalogue["products"]: + # One job per product + wiz.with_delay()._create_update_product(product_vals, seller.id) + # Save imported file as attachment + self._bdimport.post_create_or_update( + catalogue, seller, doc_filename=self.product_filename + ) + logger.info( + "Update for vendor %s: %d products", seller.name, len(catalogue["products"]) ) + return {"type": "ir.actions.act_window_close"} diff --git a/product_import_ubl/tests/test_ubl_catalogue_import.py b/product_import_ubl/tests/test_ubl_catalogue_import.py index 9eabff57f3..aef9e9dccc 100644 --- a/product_import_ubl/tests/test_ubl_catalogue_import.py +++ b/product_import_ubl/tests/test_ubl_catalogue_import.py @@ -11,7 +11,13 @@ class TestUblOrderImport(SavepointCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.env = cls.env( + context={ + **cls.env.context, + "tracking_disable": True, + "queue_job__no_delay": True, + } + ) cls.supplier = cls.env["res.partner"].create( {"name": "Medical", "ref": "78456123"} )