diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1059e0cac7..78c01dadfe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,6 @@ exclude: | ^shopinvader_pos/| ^shopinvader_price_per_qty/| ^shopinvader_product_attribute_set/| - ^shopinvader_product_brand/| ^shopinvader_product_brand_image/| ^shopinvader_product_brand_tag/| ^shopinvader_product_manufactured_for/| diff --git a/setup/shopinvader_product_brand/odoo/addons/shopinvader_product_brand b/setup/shopinvader_product_brand/odoo/addons/shopinvader_product_brand new file mode 120000 index 0000000000..6e1ffcc1b5 --- /dev/null +++ b/setup/shopinvader_product_brand/odoo/addons/shopinvader_product_brand @@ -0,0 +1 @@ +../../../../shopinvader_product_brand \ No newline at end of file diff --git a/setup/shopinvader_product_brand/setup.py b/setup/shopinvader_product_brand/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/shopinvader_product_brand/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/shopinvader_search_engine_product_brand/odoo/addons/shopinvader_search_engine_product_brand b/setup/shopinvader_search_engine_product_brand/odoo/addons/shopinvader_search_engine_product_brand new file mode 120000 index 0000000000..a380645194 --- /dev/null +++ b/setup/shopinvader_search_engine_product_brand/odoo/addons/shopinvader_search_engine_product_brand @@ -0,0 +1 @@ +../../../../shopinvader_search_engine_product_brand \ No newline at end of file diff --git a/setup/shopinvader_search_engine_product_brand/setup.py b/setup/shopinvader_search_engine_product_brand/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/shopinvader_search_engine_product_brand/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/shopinvader_product_brand/__init__.py b/shopinvader_product_brand/__init__.py index 0650744f6b..106fc57265 100644 --- a/shopinvader_product_brand/__init__.py +++ b/shopinvader_product_brand/__init__.py @@ -1 +1,2 @@ from . import models +from . import schemas diff --git a/shopinvader_product_brand/__manifest__.py b/shopinvader_product_brand/__manifest__.py index eb098d26f8..64c6ec24f6 100644 --- a/shopinvader_product_brand/__manifest__.py +++ b/shopinvader_product_brand/__manifest__.py @@ -6,30 +6,25 @@ { "name": "Shopinvader Product Brand", "summary": "Shopinvader product Brand", - "version": "14.0.1.3.1", + "version": "16.0.1.0.0", "category": "Shopinvader", "website": "https://github.com/shopinvader/odoo-shopinvader", "author": " Akretion", "license": "AGPL-3", "application": False, - "installable": False, - "external_dependencies": { - "python": [], - "bin": [], - }, + "installable": True, "depends": [ + # OCA "product_brand", - # To avoid too much glue module we directly depend of shopinvader_search_engine - # if someone really need this module without the search part please do a PR - "shopinvader_search_engine", + # Shopinvader + "shopinvader_base_url", + "shopinvader_product", + "shopinvader_product_seo", ], "data": [ "views/product_brand_view.xml", - "views/shopinvader_brand_view.xml", - "views/shopinvader_backend_view.xml", - "data/ir_export_brand.xml", - "data/ir_export_product.xml", - "security/ir.model.access.csv", ], "demo": [], + "external_dependencies": {"python": ["extendable_pydantic>=1.2.0"]}, + "development_status": "Alpha", } diff --git a/shopinvader_product_brand/data/ir_export_brand.xml b/shopinvader_product_brand/data/ir_export_brand.xml deleted file mode 100644 index 632e43cb4d..0000000000 --- a/shopinvader_product_brand/data/ir_export_brand.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - ShopInvader Brand Export - shopinvader.brand - - - - record_id - record_id:id - _jsonify_m2o_to_id - - - - - name - - - - - url_key - - - - - redirect_url_key - - - - - seo_title - - - - - sequence - - - - - meta_keywords - - - - - meta_description - - - - - description - - - - - short_description - - - - diff --git a/shopinvader_product_brand/data/ir_export_product.xml b/shopinvader_product_brand/data/ir_export_product.xml deleted file mode 100644 index 9c72022c6d..0000000000 --- a/shopinvader_product_brand/data/ir_export_product.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - shopinvader_brand_id/record_id - shopinvader_brand_id:brand/record_id:id - _jsonify_m2o_to_id - - - - - shopinvader_brand_id/name - shopinvader_brand_id:brand/name - - - - - shopinvader_brand_id/url_key - shopinvader_brand_id:brand/url_key - - - - diff --git a/shopinvader_product_brand/models/__init__.py b/shopinvader_product_brand/models/__init__.py index f845c10d9d..6cb55ac7f7 100644 --- a/shopinvader_product_brand/models/__init__.py +++ b/shopinvader_product_brand/models/__init__.py @@ -1,4 +1 @@ -from . import shopinvader_brand from . import product_brand -from . import shopinvader_product -from . import shopinvader_backend diff --git a/shopinvader_product_brand/models/product_brand.py b/shopinvader_product_brand/models/product_brand.py index 28507c9974..85d5f01955 100644 --- a/shopinvader_product_brand/models/product_brand.py +++ b/shopinvader_product_brand/models/product_brand.py @@ -6,16 +6,11 @@ class ProductBrand(models.Model): - _inherit = "product.brand" + _name = "product.brand" + _inherit = ["product.brand", "seo.title.mixin", "abstract.url"] - shopinvader_bind_ids = fields.One2many( - "shopinvader.brand", - "record_id", - string="Shopinvader Binding", - context={"active_test": False}, - ) - active = fields.Boolean(default=True, inverse="_inverse_active") - - def _inverse_active(self): - brands = self.filtered(lambda p: not p.active) - brands.mapped("shopinvader_bind_ids").write({"active": False}) + active = fields.Boolean(default=True) + sequence = fields.Integer() + meta_description = fields.Char() + meta_keywords = fields.Char() + short_description = fields.Text() diff --git a/shopinvader_product_brand/models/shopinvader_backend.py b/shopinvader_product_brand/models/shopinvader_backend.py deleted file mode 100644 index 2f57549384..0000000000 --- a/shopinvader_product_brand/models/shopinvader_backend.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2021 Camptocamp (http://www.camptocamp.com). -# @author Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class ShopinvaderBackend(models.Model): - - _inherit = "shopinvader.backend" - - nbr_brand = fields.Integer(compute="_compute_nbr_content") - - def _to_compute_nbr_content(self): - res = super()._to_compute_nbr_content() - res["nbr_brand"] = "shopinvader.brand" - return res - - def bind_all_brands(self, domain=None): - domain = domain or [("product_ids.shopinvader_bind_ids", "!=", False)] - result = self._bind_all_content( - "product.brand", - "shopinvader.brand", - domain, - ) - return result diff --git a/shopinvader_product_brand/models/shopinvader_brand.py b/shopinvader_product_brand/models/shopinvader_brand.py deleted file mode 100644 index 753057ed96..0000000000 --- a/shopinvader_product_brand/models/shopinvader_brand.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2021 Akretion (https://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class ShopinvaderBrand(models.Model): - _name = "shopinvader.brand" - _description = "Shopinvader Brand" - - _inherit = [ - "shopinvader.binding", - "abstract.url", - "seo.title.mixin", - "shopinvader.se.binding", - ] - _inherits = {"product.brand": "record_id"} - _order = "sequence" - - record_id = fields.Many2one( - "product.brand", required=True, ondelete="cascade", index=True - ) - sequence = fields.Integer() - meta_description = fields.Char() - meta_keywords = fields.Char() - short_description = fields.Html() - description = fields.Html() - active = fields.Boolean(default=True) - - _sql_constraints = [ - ( - "record_uniq", - "unique(backend_id, record_id, lang_id)", - "A category can only have one binding by backend.", - ) - ] - - def _compute_automatic_url_key(self): - self._generic_compute_automatic_url_key() diff --git a/shopinvader_product_brand/models/shopinvader_product.py b/shopinvader_product_brand/models/shopinvader_product.py deleted file mode 100644 index c215a96f95..0000000000 --- a/shopinvader_product_brand/models/shopinvader_product.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 Akretion (https://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -import logging - -from odoo import api, fields, models - -_logger = logging.getLogger(__name__) - - -class ShopinvaderProduct(models.Model): - _inherit = "shopinvader.product" - - shopinvader_brand_id = fields.Many2one( - "shopinvader.brand", - compute="_compute_shopinvader_brand_id", - string="Shopinvader Brand", - ) - - @api.depends("product_brand_id.shopinvader_bind_ids") - def _compute_shopinvader_brand_id(self): - for record in self: - shopinvader_brand = record.product_brand_id.shopinvader_bind_ids.filtered( - lambda s: s.backend_id == record.backend_id - and s.lang_id == record.lang_id - ) - if len(shopinvader_brand) > 1: - _logger.error("Too many shopinvader_brand found take the first one") - record.shopinvader_brand_id = fields.first(shopinvader_brand) diff --git a/shopinvader_product_brand/readme/CONTRIBUTORS.rst b/shopinvader_product_brand/readme/CONTRIBUTORS.rst index 89739049f4..575ccab7ef 100644 --- a/shopinvader_product_brand/readme/CONTRIBUTORS.rst +++ b/shopinvader_product_brand/readme/CONTRIBUTORS.rst @@ -1,2 +1,3 @@ * Sebastien BEAU * Simone Orsi +* Marie Lejeune diff --git a/shopinvader_product_brand/readme/DESCRIPTION.rst b/shopinvader_product_brand/readme/DESCRIPTION.rst index 265873de71..5b5bf39110 100644 --- a/shopinvader_product_brand/readme/DESCRIPTION.rst +++ b/shopinvader_product_brand/readme/DESCRIPTION.rst @@ -1,3 +1,12 @@ -This module allow you to manage brand on your `Shopinvader`_ website +This module focus on the serialization of brand information into JSON objects +in the context of the `Shopinvader`_ project. These information are mainly used +to be made available for a `Shopinvader`_ website through an export to search +engine indexation services like ElasticSearch / OpenSearch. + +* It defines a new specific Pydantic model to be used to serialize a *product.brand* + record into a JSON object. + +* It extends the ProductProduct Pydantic model to add the brand information in the + JSON exported for a product.product record. .. _Shopinvader: https://shopinvader.com diff --git a/shopinvader_product_brand/schemas/__init__.py b/shopinvader_product_brand/schemas/__init__.py new file mode 100644 index 0000000000..cc7453a6f7 --- /dev/null +++ b/shopinvader_product_brand/schemas/__init__.py @@ -0,0 +1,2 @@ +from .brand import ProductBrand +from .product import ProductTemplate diff --git a/shopinvader_product_brand/schemas/brand.py b/shopinvader_product_brand/schemas/brand.py new file mode 100644 index 0000000000..033e618805 --- /dev/null +++ b/shopinvader_product_brand/schemas/brand.py @@ -0,0 +1,32 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from extendable_pydantic import StrictExtendableBaseModel + + +class ProductBrand(StrictExtendableBaseModel): + id: int + name: str + sequence: int | None = None + description: str | None = None + short_description: str | None = None + meta_description: str | None = None + meta_keywords: str | None = None + seo_title: str | None = None + url_key: str | None = None + redirect_url_key: str | None = None + + @classmethod + def from_product_brand(cls, odoo_rec): + return cls.model_construct( + id=odoo_rec.id, + name=odoo_rec.name, + sequence=odoo_rec.sequence or None, + description=odoo_rec.description or None, + short_description=odoo_rec.short_description or None, + meta_description=odoo_rec.meta_description or None, + meta_keywords=odoo_rec.meta_keywords or None, + seo_title=odoo_rec.seo_title or None, + url_key=odoo_rec.url_key or None, + redirect_url_key=odoo_rec.redirect_url_key or None, + ) diff --git a/shopinvader_product_brand/schemas/product.py b/shopinvader_product_brand/schemas/product.py new file mode 100644 index 0000000000..46aaf2dcb7 --- /dev/null +++ b/shopinvader_product_brand/schemas/product.py @@ -0,0 +1,22 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.shopinvader_product.schemas import ( + ProductTemplate as BaseProductTemplate, +) + +from . import ProductBrand + + +class ProductTemplate(BaseProductTemplate, extends=True): + brand: ProductBrand | None = None + + @classmethod + def from_product_template(cls, odoo_rec): + obj = super().from_product_template(odoo_rec) + obj.brand = ( + ProductBrand.from_product_brand(odoo_rec.product_brand_id) + if odoo_rec.product_brand_id + else None + ) + return obj diff --git a/shopinvader_product_brand/security/ir.model.access.csv b/shopinvader_product_brand/security/ir.model.access.csv deleted file mode 100644 index 03bafa9868..0000000000 --- a/shopinvader_product_brand/security/ir.model.access.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_shopinvader_brand_edit,shopinvader_brand edit,model_shopinvader_brand,shopinvader.group_shopinvader_manager,1,1,1,1 -access_shopinvader_brand_read,shopinvader_brand read,model_shopinvader_brand,base.group_user,1,0,0,0 diff --git a/shopinvader_product_brand/tests/__init__.py b/shopinvader_product_brand/tests/__init__.py deleted file mode 100644 index 8a0fc81c0c..0000000000 --- a/shopinvader_product_brand/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import common -from . import test_brand diff --git a/shopinvader_product_brand/tests/common.py b/shopinvader_product_brand/tests/common.py deleted file mode 100644 index 90e142600f..0000000000 --- a/shopinvader_product_brand/tests/common.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021 Akretion (https://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from odoo.addons.shopinvader.tests.common import ProductCommonCase - - -class ProductBrandCommonCase(ProductCommonCase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.brand = cls.env["product.brand"].create({"name": "Foo Brand"}) - cls.lang_en = cls.env.ref("base.lang_en") - cls.binding = cls.env["shopinvader.brand"].create( - { - "backend_id": cls.backend.id, - "lang_id": cls.lang_en.id, - "record_id": cls.brand.id, - "active": True, - } - ) diff --git a/shopinvader_product_brand/tests/test_brand.py b/shopinvader_product_brand/tests/test_brand.py deleted file mode 100644 index e75ff42d95..0000000000 --- a/shopinvader_product_brand/tests/test_brand.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2021 Akretion (https://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from .common import ProductBrandCommonCase - - -class ProductBrandCase(ProductBrandCommonCase): - def test_url_key(self): - binding = self.binding - self.assertEqual(binding.url_key, "foo-brand") - self.brand.write({"name": "BAR Brand"}) - binding.refresh() - self.assertEqual(binding.url_key, "bar-brand") - self.assertEqual(binding.redirect_url_key, ["foo-brand"]) - - def test_inactive(self): - self.brand.active = False - self.assertFalse(self.binding.active) - - def test_add_brand(self): - self.assertFalse(self.shopinvader_variant.shopinvader_brand_id) - self.template.product_brand_id = self.brand - self.assertEqual(self.shopinvader_variant.shopinvader_brand_id, self.binding) diff --git a/shopinvader_product_brand/views/product_brand_view.xml b/shopinvader_product_brand/views/product_brand_view.xml index b19c14cf56..5605c0c65f 100644 --- a/shopinvader_product_brand/views/product_brand_view.xml +++ b/shopinvader_product_brand/views/product_brand_view.xml @@ -5,17 +5,30 @@ product.brand - - - - - - - - - + + + + + + + + + + + + - + + + + + + product.brand + + + + + diff --git a/shopinvader_product_brand/views/shopinvader_backend_view.xml b/shopinvader_product_brand/views/shopinvader_backend_view.xml deleted file mode 100644 index ddabc4b92f..0000000000 --- a/shopinvader_product_brand/views/shopinvader_backend_view.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - shopinvader.backend.form (in shopinvader_product_brand) - shopinvader.backend - - - - -
- -
- - - - -