diff --git a/.gitignore b/.gitignore index 574795e..3f5d3d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # +# Ignore Opened csv files +.~lock.* # Do not share pdf file. (grap-odoo-business-supplier-invoice) *.pdf # diff --git a/grap_custom_import_account_product_fiscal_classification/README.rst b/grap_custom_import_account_product_fiscal_classification/README.rst new file mode 100644 index 0000000..e69de29 diff --git a/grap_custom_import_account_product_fiscal_classification/__init__.py b/grap_custom_import_account_product_fiscal_classification/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/grap_custom_import_account_product_fiscal_classification/__manifest__.py b/grap_custom_import_account_product_fiscal_classification/__manifest__.py new file mode 100644 index 0000000..3e11ffb --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "GRAP - Custom Import Fiscal Classification", + "summary": "Extra GRAP Tools to import data for" + " Account Product Fiscal Classification", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "GRAP", + "website": "https://github.com/grap/grap-odoo-import", + "license": "AGPL-3", + "depends": ["grap_custom_import_product", "account_product_fiscal_classification"], + "auto_install": True, + "installable": True, +} diff --git a/grap_custom_import_account_product_fiscal_classification/models/__init__.py b/grap_custom_import_account_product_fiscal_classification/models/__init__.py new file mode 100644 index 0000000..5c74c8c --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/models/__init__.py @@ -0,0 +1 @@ +from . import product_product diff --git a/grap_custom_import_account_product_fiscal_classification/models/product_product.py b/grap_custom_import_account_product_fiscal_classification/models/product_product.py new file mode 100644 index 0000000..fd0193f --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/models/product_product.py @@ -0,0 +1,70 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, fields, models +from odoo.exceptions import ValidationError +from odoo.osv import expression + + +class ProductProduct(models.Model): + _inherit = "product.product" + + grap_import_vat_amount = fields.Float(string="VAT Amount (For import)", store=False) + + # pylint: disable=missing-return + def _custom_import_hook_vals(self, old_vals, new_vals): + super()._custom_import_hook_vals(old_vals, new_vals) + self._custom_import_handle_fiscal_classification_id(old_vals, new_vals) + + def _custom_import_get_fiscal_classifications(self, vat_amount): + domain = expression.OR( + [[("company_id", "=", self.env.company.id)], [("company_id", "=", False)]] + ) + if vat_amount: + domain = expression.AND( + [domain, [("sale_tax_ids.amount", "=", 100 * vat_amount)]] + ) + else: + domain = expression.AND([domain, [("sale_tax_ids", "=", False)]]) + + return ( + self.env["account.product.fiscal.classification"] + .search(domain) + .filtered(lambda x: len(x.sale_tax_ids) < 2) + ) + + def _custom_import_handle_fiscal_classification_id(self, old_vals, new_vals): + vat_amount = old_vals.get("grap_import_vat_amount") + if not vat_amount and not self.env.context.get("install_mode"): + raise ValidationError( + _( + "No VAT Amount found for the product %(product_name)s", + product_name=old_vals.get("name"), + ) + ) + classifications = self._custom_import_get_fiscal_classifications(vat_amount) + + if len(classifications) == 1: + new_vals["fiscal_classification_id"] = classifications.id + return + + elif len(classifications) == 0: + raise ValidationError( + _( + "No Fiscal Classification Found for the product %(product_name)s." + " Vat Amount %(vat_amount)s", + product_name=old_vals.get("name"), + vat_amount=vat_amount, + ) + ) + + raise ValidationError( + _( + "Many Fiscal Classifications Found for the product %(product_name)s." + " Vat Amount %(vat_amount)s. Fiscal Classifications : %(classification_names)s", + product_name=old_vals.get("name"), + vat_amount=vat_amount, + classification_names=",".join(classifications.mapped("name")), + ) + ) diff --git a/grap_custom_import_account_product_fiscal_classification/readme/CONTRIBUTORS.rst b/grap_custom_import_account_product_fiscal_classification/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..9f76a75 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL diff --git a/grap_custom_import_account_product_fiscal_classification/readme/DESCRIPTION.rst b/grap_custom_import_account_product_fiscal_classification/readme/DESCRIPTION.rst new file mode 100644 index 0000000..6723531 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module improve the "import" features provided by Odoo. + +* ``product.product``: + + * Allow to recover ``multiplier_qty`` field in the supplier info level. diff --git a/grap_custom_import_account_product_fiscal_classification/readme/ROADMAP.rst b/grap_custom_import_account_product_fiscal_classification/readme/ROADMAP.rst new file mode 100644 index 0000000..a09c923 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +* handle selection of classifications for ``recurring_consignment``, + once the module is ported in V16. \ No newline at end of file diff --git a/grap_custom_import_account_product_fiscal_classification/tests/__init__.py b/grap_custom_import_account_product_fiscal_classification/tests/__init__.py new file mode 100644 index 0000000..d9b96c4 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/grap_custom_import_account_product_fiscal_classification/tests/templates/product.product/product.csv b/grap_custom_import_account_product_fiscal_classification/tests/templates/product.product/product.csv new file mode 100644 index 0000000..b6b521e --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/tests/templates/product.product/product.csv @@ -0,0 +1,2 @@ +name,uom_id,categ_id,barcode,list_price,grap_import_supplier_name,grap_import_supplier_product_code,grap_import_supplier_product_name,grap_import_supplier_gross_price,grap_import_vat_amount +Mention Good (Late chocolate),Units,All / Saleable,3222472195092,2.29,Ready Mat,GOOD,MENTION GOOD,1.98,0.20 diff --git a/grap_custom_import_account_product_fiscal_classification/tests/test_module.py b/grap_custom_import_account_product_fiscal_classification/tests/test_module.py new file mode 100644 index 0000000..8c707d0 --- /dev/null +++ b/grap_custom_import_account_product_fiscal_classification/tests/test_module.py @@ -0,0 +1,28 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import tagged + +from odoo.addons.grap_custom_import_product.tests.test_module import TestModuleProduct + + +@tagged("post_install", "-at_install") +class TestModuleProductSupplierinfoQtyMultiplier(TestModuleProduct): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.ProductProduct = cls.env["product.product"] + cls.classification_20 = cls.env.ref( + "account_product_fiscal_classification.fiscal_classification_A_company_1" + ) + + def test_01_import_product_account_product_fiscal_classification(self): + products, messages = self._test_import_file( + "grap_custom_import_account_product_fiscal_classification", + "product.product", + "product.csv", + ) + self.assertFalse(messages) + self.assertEqual(len(products), 1) + self.assertEqual(products.fiscal_classification_id, self.classification_20) diff --git a/grap_custom_import_base/README.rst b/grap_custom_import_base/README.rst new file mode 100644 index 0000000..e69de29 diff --git a/grap_custom_import_base/__init__.py b/grap_custom_import_base/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/grap_custom_import_base/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/grap_custom_import_base/__manifest__.py b/grap_custom_import_base/__manifest__.py new file mode 100644 index 0000000..6d27624 --- /dev/null +++ b/grap_custom_import_base/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "GRAP - Custom Import Base Module", + "summary": "Extra GRAP Tools to import data for base module", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "GRAP", + "website": "https://github.com/grap/grap-odoo-import", + "license": "AGPL-3", + "depends": ["base_import"], + "installable": True, +} diff --git a/grap_custom_import_base/models/__init__.py b/grap_custom_import_base/models/__init__.py new file mode 100644 index 0000000..79c0518 --- /dev/null +++ b/grap_custom_import_base/models/__init__.py @@ -0,0 +1,2 @@ +from . import custom_import_mixin +from . import res_partner diff --git a/grap_custom_import_base/models/custom_import_mixin.py b/grap_custom_import_base/models/custom_import_mixin.py new file mode 100644 index 0000000..8b155c7 --- /dev/null +++ b/grap_custom_import_base/models/custom_import_mixin.py @@ -0,0 +1,82 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, models +from odoo.exceptions import ValidationError + + +class CustomImportMixin(models.AbstractModel): + _name = "custom.import.mixin" + _description = "Mixin providing helper for imports" + + def _custom_import_prevent_duplicate_fields(self): + """Define the fields that will be used to prevent + Duplicates in the import""" + return [] + + def _custom_import_hook_vals(self, old_vals, new_vals): + # Check if existing duplicates are present in the database + for field in self._custom_import_prevent_duplicate_fields(): + if new_vals.get(field): + items = self.search([(field, "=", new_vals.get(field))]) + if items: + raise ValidationError( + _( + "The following items still exist in the database" + " for the field %(field)s. Values: %(values)s", + field=field, + values=",".join(items.mapped(field)), + ) + ) + return new_vals + + def _custom_import_check_duplicates_new_vals(self, vals_list): + for field in self._custom_import_prevent_duplicate_fields(): + duplicates = [] + for vals in vals_list: + if vals.get(field): + if vals[field] in duplicates: + raise ValidationError( + _( + "The file contain many item(s) with" + " the same value '%(value)s' for the" + " field '%(field)s'.", + field=field, + value=vals[field], + ) + ) + duplicates.append(vals[field]) + + def _custom_import_get_or_create( + self, model_name, search_field_name, vals, field_name + ): + ItemModel = self.env[model_name] + if not vals.get(field_name): + return False + items = ItemModel.search([(search_field_name, "=", vals[field_name])]) + if len(items) == 0: + return ItemModel.create({"name": vals[field_name]}) + elif len(items) == 1: + return items + elif len(items) >= 2: + raise ValidationError( + _( + "%(item_qty)d items found for the field %(field_name)s." + " Value: '%(value)s'." + ), + item_qty=len(items), + field_name=field_name, + value=vals["field_name"], + ) + + # Overload Section + def _load_records_create(self, vals_list): + new_vals_list = [] + for vals in vals_list: + new_vals = vals.copy() + self._custom_import_hook_vals(vals, new_vals) + new_vals_list.append(new_vals) + # TODO, move this check in another part + self._custom_import_check_duplicates_new_vals(new_vals_list) + return super()._load_records_create(new_vals_list) diff --git a/grap_custom_import_base/models/res_partner.py b/grap_custom_import_base/models/res_partner.py new file mode 100644 index 0000000..4dcc46c --- /dev/null +++ b/grap_custom_import_base/models/res_partner.py @@ -0,0 +1,19 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import models + +logger = logging.getLogger(__name__) + + +class ResPartner(models.Model): + _name = "res.partner" + _inherit = ["res.partner", "custom.import.mixin"] + + def _custom_import_prevent_duplicate_fields(self): + res = super()._custom_import_prevent_duplicate_fields() + res += ["name", "vat"] + return res diff --git a/grap_custom_import_base/readme/CONTRIBUTORS.rst b/grap_custom_import_base/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..9f76a75 --- /dev/null +++ b/grap_custom_import_base/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL diff --git a/grap_custom_import_base/readme/DESCRIPTION.rst b/grap_custom_import_base/readme/DESCRIPTION.rst new file mode 100644 index 0000000..2a7a089 --- /dev/null +++ b/grap_custom_import_base/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +This module improve the "import" features provided by Odoo. + +It provides generic tools for that purpose, and improve imports for some models. + +* ``res.partner``: + + * Prevent to create duplicates regarding ``name`` and ``vat`` fields. \ No newline at end of file diff --git a/grap_custom_import_base/tests/__init__.py b/grap_custom_import_base/tests/__init__.py new file mode 100644 index 0000000..d9b96c4 --- /dev/null +++ b/grap_custom_import_base/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/grap_custom_import_base/tests/templates/res.partner/supplier.csv b/grap_custom_import_base/tests/templates/res.partner/supplier.csv new file mode 100644 index 0000000..2d35ac1 --- /dev/null +++ b/grap_custom_import_base/tests/templates/res.partner/supplier.csv @@ -0,0 +1,2 @@ +name,email,phone,mobile,website,street,street2,zip,city,country_id,vat +Relais Vert,contact84@relais-vert.com,04 90 67 23 72,06 12 34 56 78,https://www.relais-vert.com/,ZONE BELLECOUR 3,621 Allée BELLECOUR,84200,CARPENTRAS,France,FR72352867493 diff --git a/grap_custom_import_base/tests/templates/res.partner/supplier_new_duplicates_vat.csv b/grap_custom_import_base/tests/templates/res.partner/supplier_new_duplicates_vat.csv new file mode 100644 index 0000000..9aeb3d0 --- /dev/null +++ b/grap_custom_import_base/tests/templates/res.partner/supplier_new_duplicates_vat.csv @@ -0,0 +1,3 @@ +name,email,phone,mobile,website,street,street2,zip,city,country_id,vat +Relais Vert,contact84@relais-vert.com,04 90 67 23 72,06 12 34 56 78,https://www.relais-vert.com/,ZONE BELLECOUR 3,621 Allée BELLECOUR,84200,CARPENTRAS,France,FR72352867493 +Reloud Vert,contact84@relais-vert.com,04 90 67 23 72,06 12 34 56 78,https://www.relais-vert.com/,ZONE BELLECOUR 3,621 Allée BELLECOUR,84200,CARPENTRAS,France,FR72352867493 diff --git a/grap_custom_import_base/tests/test_module.py b/grap_custom_import_base/tests/test_module.py new file mode 100644 index 0000000..1294863 --- /dev/null +++ b/grap_custom_import_base/tests/test_module.py @@ -0,0 +1,71 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.modules.module import get_module_resource +from odoo.tests import tagged +from odoo.tests.common import TransactionCase + + +@tagged("post_install", "-at_install") +class TestModuleBase(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.ResPartner = cls.env["res.partner"] + cls.Wizard = cls.env["base_import.import"] + + def _test_import_file(self, module, model, file_name): + preview_options = {"headers": True, "quoting": '"'} + import_options = {"has_headers": True, "quoting": '"'} + + # Read File + file_path = get_module_resource(module, "tests/templates/", model, file_name) + extension = file_path.split(".")[-1] + if extension == "csv": + file_type = "text/csv" + else: + file_type = "Unimplemented Extension" + + file_content = open(file_path, "rb").read() + + # Create Wizard + import_wizard = self.Wizard.create( + {"res_model": model, "file": file_content, "file_type": file_type} + ) + + # Run Preview + result_parse = import_wizard.parse_preview(preview_options) + column_list = [x[0] for x in result_parse["preview"]] + + # Execute Import + results = import_wizard.execute_import(column_list, column_list, import_options) + + items = self.env[model].browse(results.get("ids")) + return items, results["messages"] + + def test_01_import_supplier(self): + partners, messages = self._test_import_file( + "grap_custom_import_base", "res.partner", "supplier.csv" + ) + self.assertFalse(messages) + self.assertEqual(len(partners), 1) + self.assertEqual(partners.name, "Relais Vert") + + def test_02_existing_duplicates_name(self): + partners, messages = self._test_import_file( + "grap_custom_import_base", "res.partner", "supplier.csv" + ) + self.assertFalse(messages) + partners, messages = self._test_import_file( + "grap_custom_import_base", "res.partner", "supplier.csv" + ) + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].get("type"), "error") + + def test_03_import_supplier_new_duplicates_vat(self): + partners, messages = self._test_import_file( + "grap_custom_import_base", "res.partner", "supplier_new_duplicates_vat.csv" + ) + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].get("type"), "error") diff --git a/grap_custom_import_product/README.rst b/grap_custom_import_product/README.rst new file mode 100644 index 0000000..e69de29 diff --git a/grap_custom_import_product/__init__.py b/grap_custom_import_product/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/grap_custom_import_product/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/grap_custom_import_product/__manifest__.py b/grap_custom_import_product/__manifest__.py new file mode 100644 index 0000000..2604767 --- /dev/null +++ b/grap_custom_import_product/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "GRAP - Custom Import Product Module", + "summary": "Extra GRAP Tools to import data for product module", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "GRAP", + "website": "https://github.com/grap/grap-odoo-import", + "license": "AGPL-3", + "depends": ["grap_custom_import_base", "product"], + "auto_install": True, + "installable": True, +} diff --git a/grap_custom_import_product/models/__init__.py b/grap_custom_import_product/models/__init__.py new file mode 100644 index 0000000..5c74c8c --- /dev/null +++ b/grap_custom_import_product/models/__init__.py @@ -0,0 +1 @@ +from . import product_product diff --git a/grap_custom_import_product/models/product_product.py b/grap_custom_import_product/models/product_product.py new file mode 100644 index 0000000..fb0e04d --- /dev/null +++ b/grap_custom_import_product/models/product_product.py @@ -0,0 +1,70 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResPartner(models.Model): + _name = "product.product" + _inherit = ["product.product", "custom.import.mixin"] + + def _custom_import_prevent_duplicate_fields(self): + res = super()._custom_import_prevent_duplicate_fields() + res += ["name", "barcode"] + return res + + grap_import_supplier_name = fields.Char( + string="Supplier Name (For import)", store=False + ) + grap_import_supplier_product_code = fields.Char( + string="Product Code - Supplier (For import)", store=False + ) + grap_import_supplier_product_name = fields.Char( + string="Product Name - Supplier (For import)", store=False + ) + grap_import_supplier_min_qty = fields.Monetary( + string="Product Min Quantity - Supplier (For import)", store=False + ) + grap_import_supplier_gross_price = fields.Monetary( + string="Product Gross Price - Supplier (For import)", store=False + ) + + # pylint: disable=missing-return + def _custom_import_hook_vals(self, old_vals, new_vals): + super()._custom_import_hook_vals(old_vals, new_vals) + self._custom_import_handle_supplierinfo_vals(old_vals, new_vals) + + def _custom_import_handle_supplierinfo_vals(self, old_vals, new_vals): + supplier = self._custom_import_get_or_create( + "res.partner", "name", old_vals, "grap_import_supplier_name" + ) + if supplier: + new_vals.update( + { + "seller_ids": [ + ( + 0, + False, + self._custom_import_prepare_supplierinfo_vals( + supplier, old_vals + ), + ) + ], + "standard_price": self._custom_import_prepare_standard_price( + old_vals + ), + } + ) + + def _custom_import_prepare_supplierinfo_vals(self, partner, vals): + return { + "partner_id": partner.id, + "price": vals.get("grap_import_supplier_gross_price"), + "product_code": vals.get("grap_import_supplier_product_code"), + "product_name": vals.get("grap_import_supplier_product_name"), + "min_qty": vals.get("grap_import_supplier_min_qty"), + } + + def _custom_import_prepare_standard_price(self, vals): + return vals.get("grap_import_supplier_gross_price") diff --git a/grap_custom_import_product/readme/CONTRIBUTORS.rst b/grap_custom_import_product/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..9f76a75 --- /dev/null +++ b/grap_custom_import_product/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL diff --git a/grap_custom_import_product/readme/DESCRIPTION.rst b/grap_custom_import_product/readme/DESCRIPTION.rst new file mode 100644 index 0000000..8bdabeb --- /dev/null +++ b/grap_custom_import_product/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module improve the "import" features provided by Odoo. + +It provides generic tools for that purpose, and improve imports for some models. + +* ``product.product``: + + * Prevent to create duplicates regarding ``name`` and ``barcode`` fields. + + * Allow to create main ``seller_ids``, based on supplier information fields. diff --git a/grap_custom_import_product/tests/__init__.py b/grap_custom_import_product/tests/__init__.py new file mode 100644 index 0000000..d9b96c4 --- /dev/null +++ b/grap_custom_import_product/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/grap_custom_import_product/tests/templates/product.product/product.csv b/grap_custom_import_product/tests/templates/product.product/product.csv new file mode 100644 index 0000000..c453af2 --- /dev/null +++ b/grap_custom_import_product/tests/templates/product.product/product.csv @@ -0,0 +1,4 @@ +name,uom_id,categ_id,barcode,list_price,grap_import_supplier_name,grap_import_supplier_product_code,grap_import_supplier_product_name,grap_import_supplier_gross_price +Coca Cola (Import),Units,All / Saleable / Office Furniture,5000112602791,4.12,Coke Corp,CC,BOTTLE 33CL,3.33 +Produit B,Units,All / Saleable / Office Furniture,,8.00,Supplier From Product Import,PB,product_B,6.00 +Produit C,Units,All / Saleable,,2.30,,,, diff --git a/grap_custom_import_product/tests/test_module.py b/grap_custom_import_product/tests/test_module.py new file mode 100644 index 0000000..118aa66 --- /dev/null +++ b/grap_custom_import_product/tests/test_module.py @@ -0,0 +1,28 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import tagged + +from odoo.addons.grap_custom_import_base.tests.test_module import TestModuleBase + + +@tagged("post_install", "-at_install") +class TestModuleProduct(TestModuleBase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.ProductProduct = cls.env["product.product"] + + def test_01_import_product(self): + products, messages = self._test_import_file( + "grap_custom_import_product", "product.product", "product.csv" + ) + self.assertFalse(messages) + self.assertEqual(len(products), 3) + coca_cola = products.filtered(lambda x: x.name == "Coca Cola (Import)") + self.assertEqual(len(coca_cola), 1) + self.assertEqual(coca_cola.standard_price, 3.33) + self.assertEqual(coca_cola.mapped("seller_ids.partner_id.name"), ["Coke Corp"]) + self.assertEqual(coca_cola.mapped("seller_ids.product_code"), ["CC"]) + self.assertEqual(coca_cola.mapped("seller_ids.product_name"), ["BOTTLE 33CL"]) diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/README.rst b/grap_custom_import_product_supplierinfo_qty_multiplier/README.rst new file mode 100644 index 0000000..e69de29 diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/__init__.py b/grap_custom_import_product_supplierinfo_qty_multiplier/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/__manifest__.py b/grap_custom_import_product_supplierinfo_qty_multiplier/__manifest__.py new file mode 100644 index 0000000..7252558 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "GRAP - Custom Import Product Supplierinfo Quantity Multiplier Module", + "summary": "Extra GRAP Tools to import data for" + " product Supplierinfo Quantity Multiplier module", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "GRAP", + "website": "https://github.com/grap/grap-odoo-import", + "license": "AGPL-3", + "depends": ["grap_custom_import_product", "product_supplierinfo_qty_multiplier"], + "auto_install": True, + "installable": True, +} diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/models/__init__.py b/grap_custom_import_product_supplierinfo_qty_multiplier/models/__init__.py new file mode 100644 index 0000000..5c74c8c --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/models/__init__.py @@ -0,0 +1 @@ +from . import product_product diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/models/product_product.py b/grap_custom_import_product_supplierinfo_qty_multiplier/models/product_product.py new file mode 100644 index 0000000..83921a7 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/models/product_product.py @@ -0,0 +1,18 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + grap_import_supplier_multiplier_qty = fields.Float( + string="Product Package Quantity - Supplier (For import)", store=False + ) + + def _custom_import_prepare_supplierinfo_vals(self, partner, vals): + res = super()._custom_import_prepare_supplierinfo_vals(partner, vals) + res["multiplier_qty"] = vals.get("grap_import_supplier_multiplier_qty") + return res diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/readme/CONTRIBUTORS.rst b/grap_custom_import_product_supplierinfo_qty_multiplier/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..9f76a75 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/readme/DESCRIPTION.rst b/grap_custom_import_product_supplierinfo_qty_multiplier/readme/DESCRIPTION.rst new file mode 100644 index 0000000..6723531 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module improve the "import" features provided by Odoo. + +* ``product.product``: + + * Allow to recover ``multiplier_qty`` field in the supplier info level. diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/tests/__init__.py b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/__init__.py new file mode 100644 index 0000000..d9b96c4 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/tests/templates/product.product/product.csv b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/templates/product.product/product.csv new file mode 100644 index 0000000..dfa2cc3 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/templates/product.product/product.csv @@ -0,0 +1,2 @@ +name,uom_id,categ_id,barcode,list_price,grap_import_supplier_name,grap_import_supplier_product_code,grap_import_supplier_product_name,grap_import_supplier_gross_price,grap_import_supplier_multiplier_qty +Mention Good (Late chocolate),Units,All / Saleable,3222472195092,2.29,Ready Mat,GOOD,MENTION GOOD,1.98,24 diff --git a/grap_custom_import_product_supplierinfo_qty_multiplier/tests/test_module.py b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/test_module.py new file mode 100644 index 0000000..d2fe301 --- /dev/null +++ b/grap_custom_import_product_supplierinfo_qty_multiplier/tests/test_module.py @@ -0,0 +1,25 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import tagged + +from odoo.addons.grap_custom_import_product.tests.test_module import TestModuleProduct + + +@tagged("post_install", "-at_install") +class TestModuleProductSupplierinfoQtyMultiplier(TestModuleProduct): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.ProductProduct = cls.env["product.product"] + + def test_01_import_product_supplierinfo_qty_multiplier(self): + products, messages = self._test_import_file( + "grap_custom_import_product_supplierinfo_qty_multiplier", + "product.product", + "product.csv", + ) + self.assertFalse(messages) + self.assertEqual(len(products), 1) + self.assertEqual(products.mapped("seller_ids.multiplier_qty"), [24.0]) diff --git a/setup/grap_custom_import_account_product_fiscal_classification/odoo/addons/grap_custom_import_account_product_fiscal_classification b/setup/grap_custom_import_account_product_fiscal_classification/odoo/addons/grap_custom_import_account_product_fiscal_classification new file mode 120000 index 0000000..5b341ff --- /dev/null +++ b/setup/grap_custom_import_account_product_fiscal_classification/odoo/addons/grap_custom_import_account_product_fiscal_classification @@ -0,0 +1 @@ +../../../../grap_custom_import_account_product_fiscal_classification \ No newline at end of file diff --git a/setup/grap_custom_import_account_product_fiscal_classification/setup.py b/setup/grap_custom_import_account_product_fiscal_classification/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/grap_custom_import_account_product_fiscal_classification/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/grap_custom_import_base/odoo/addons/grap_custom_import_base b/setup/grap_custom_import_base/odoo/addons/grap_custom_import_base new file mode 120000 index 0000000..45b52aa --- /dev/null +++ b/setup/grap_custom_import_base/odoo/addons/grap_custom_import_base @@ -0,0 +1 @@ +../../../../grap_custom_import_base \ No newline at end of file diff --git a/setup/grap_custom_import_base/setup.py b/setup/grap_custom_import_base/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/grap_custom_import_base/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/grap_custom_import_product/odoo/addons/grap_custom_import_product b/setup/grap_custom_import_product/odoo/addons/grap_custom_import_product new file mode 120000 index 0000000..3a0e364 --- /dev/null +++ b/setup/grap_custom_import_product/odoo/addons/grap_custom_import_product @@ -0,0 +1 @@ +../../../../grap_custom_import_product \ No newline at end of file diff --git a/setup/grap_custom_import_product/setup.py b/setup/grap_custom_import_product/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/grap_custom_import_product/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/grap_custom_import_product_supplierinfo_qty_multiplier/odoo/addons/grap_custom_import_product_supplierinfo_qty_multiplier b/setup/grap_custom_import_product_supplierinfo_qty_multiplier/odoo/addons/grap_custom_import_product_supplierinfo_qty_multiplier new file mode 120000 index 0000000..b633d52 --- /dev/null +++ b/setup/grap_custom_import_product_supplierinfo_qty_multiplier/odoo/addons/grap_custom_import_product_supplierinfo_qty_multiplier @@ -0,0 +1 @@ +../../../../grap_custom_import_product_supplierinfo_qty_multiplier \ No newline at end of file diff --git a/setup/grap_custom_import_product_supplierinfo_qty_multiplier/setup.py b/setup/grap_custom_import_product_supplierinfo_qty_multiplier/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/grap_custom_import_product_supplierinfo_qty_multiplier/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..e69de29