Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
157cbfc
[IMP]Changed name of module.
Sep 11, 2015
495cf78
[MIG]Migrated shop_by_brand module in v9.
Mar 31, 2016
6174b0e
[ADD][website_sale_wishlist] Wishlist for your online shop. (#122)
yajo Oct 18, 2016
74f8f0c
[FIX] website_sale_product_brand: Page not found in category pagination
carlosdauden Nov 24, 2017
f968a80
[MIG] website_sale_product_brand: Migration to 11.0
ernestotejeda Jun 2, 2018
bc612c6
[MIG] website_sale_product_brand: Migration to v12.0
sergio-teruel Jul 29, 2019
cc59bd2
[IMP] website_sale_product_brand: Better website view
sergio-teruel Jan 23, 2020
49e5b55
[IMP] website_sale_product_brand: black, isort
Tardo Jan 30, 2020
641ea04
[MIG] website_sale_product_brand: Migration to 13.0
Tardo Jan 30, 2020
bdda6ec
[FIX] website_sale_product_brand: Fix _get_search_domain parameters
sergio-teruel Mar 31, 2020
b4548bf
Translated using Weblate (Portuguese (Brazil))
Aug 12, 2020
458ee3c
Added translation using Weblate (Dutch)
bosd Dec 15, 2020
601b58a
Translated using Weblate (Dutch)
bosd Dec 15, 2020
9c471c4
Translated using Weblate (Catalan)
claudiagn Feb 25, 2021
6779197
[IMP] website_sale_product_brand: black, isort, prettier
victoralmau Nov 11, 2021
39e0aca
[MIG] website_sale_product_brand: Migration to 14.0
victoralmau Nov 11, 2021
8da6603
Translated using Weblate (Spanish (Argentina))
ibuioli Nov 14, 2021
44a8d50
[MIG] website_sale_product_brand: Migration to 15.0
CarlosRoca13 May 24, 2022
013dcca
[FIX] website_sale_product_brand: image proportions
chienandalu Feb 23, 2022
9975a86
[IMP] website_sale_product_brand: add website.published.mixin
chienandalu Aug 20, 2020
8ef9866
[IMP] website_sale_product_brand: Use options to define the brand
CarlosRoca13 Jun 29, 2022
c272fb0
[MIG] website_sale_product_brand: Migration to 16.0
miguel-S73 Jan 30, 2023
828f5b6
Translated using Weblate (Spanish)
Ivorra78 Aug 3, 2023
ac6a7d3
Translated using Weblate (Spanish (Argentina))
ibuioli Aug 6, 2023
a7649ff
[IMP] website_sale_product_brand: test performance improvement
josep-tecnativa Oct 18, 2023
03a93ea
Translated using Weblate (Italian)
mymage May 7, 2024
d4dc824
Translated using Weblate (Portuguese (Brazil))
May 22, 2024
4d97d11
Translated using Weblate (Swedish)
jakobkrabbe Jun 19, 2024
85cc175
[IMP] website_sale_product_brand: pre-commit auto fixes
carlos-lopez-tecnativa Jan 15, 2025
19e6eb3
[MIG] website_sale_product_brand: Migration to version 17.0
carlos-lopez-tecnativa Jan 15, 2025
8f25fbc
[IMP] website_sale_product_brand: pre-commit auto fixes
pilarvargas-tecnativa Feb 24, 2025
5f137ee
[MIG] website_sale_product_brand: Migration to version 18.0
pilarvargas-tecnativa Feb 24, 2025
b4073a9
[IMP] website_sale_product_brand: merged website_sale_filter_product_…
eduezerouali-tecnativa Oct 17, 2025
f35a7aa
[MIG] website_sale_product_brand: Migration to 19.0
Alexgars73 Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions website_sale_product_brand/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

==================================
Product Brand Filtering in Website
==================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2866648306719d101a1a7e53c7797ebb0c3f171e68905bf0f627ed69f5f020f4
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fe--commerce-lightgray.png?logo=github
:target: https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_brand
:alt: OCA/e-commerce
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/e-commerce-19-0/e-commerce-19-0-website_sale_product_brand
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/e-commerce&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module was written to extend the functionality of product filtering
on website. It will allow you to filter product based on its brand.

While shopping online, we have seen various eShops having a feature to
shop by brands which ODOO does not yet provide officially. Website Sale
Product Brand fills the gap at certain extent and by providing basic
search by brands, thus reducing end-user’s efforts in searching the
products he/she wants to purchase.

**Table of contents**

.. contents::
:local:

Configuration
=============

In order to hide brands from the e-commerce:

1. Go to *Website > eCommerce > Product brands*
2. Click on the brand you want to unpublish.
3. Click on the *Website published* smart button.

Usage
=====

Shop by brand feature is available on various famous e-commerce websites
like amazon, flipkart and many. Shop by brand feature enables you to
display product relevant to that particular brand.

To use this module, you need to:

Once you install this module, user will be able to create a new brand
and define the brand to a product.

To create product brand go to *Website > Settings > Products > Product
brands*:

- User can assign a nice logo with brand description.
- After configuring the brand, user can assign a particular brand to
a particular products.

Based on this configuration, you will see the menuitem shop by brand
next to shop menu. It will show all the brands and once you select that
brand it will show product's which is related to this brand.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/e-commerce/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/e-commerce/issues/new?body=module:%20website_sale_product_brand%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Serpent Consulting Services Pvt. Ltd.
* Tecnativa

Contributors
------------

- Jay Vora <jay.vora@serpentcs.com>

- Meet Dholakia <m.dholakia.serpentcs@gmail.com>

- `Tecnativa <https://www.tecnativa.com>`__:

- Ernesto Tejeda <ernesto.tejeda@tecnativa.com>
- Sergio Teruel <sergio.teruel@tecnativa.com>
- Alexandre Díaz
- David Vidal
- Carlos López
- Pilar Vargas

- `Studio73 <https://www.studio73.es>`__:

- Alex Garcia <[alex@studio73.es]>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/e-commerce <https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_brand>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
5 changes: 5 additions & 0 deletions website_sale_product_brand/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# © 2016 Serpent Consulting Services Pvt. Ltd. (http://www.serpentcs.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import models
from . import controllers
36 changes: 36 additions & 0 deletions website_sale_product_brand/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# © 2016 Serpent Consulting Services Pvt. Ltd. (http://www.serpentcs.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Product Brand Filtering in Website",
"category": "e-commerce",
"author": "Serpent Consulting Services Pvt. Ltd., "
"Tecnativa, "
"Odoo Community Association (OCA)",
"website": "https://github.com/OCA/e-commerce",
"version": "19.0.1.0.0",
"license": "AGPL-3",
"depends": ["product_brand", "website_sale"],
"data": [
"security/ir.model.access.csv",
"data/website_menu.xml",
"views/product_brand.xml",
"views/product_brand_views.xml",
"views/templates.xml",
],
"assets": {
"web.assets_frontend": [
"/website_sale_product_brand/static/src/scss/website_sale_product_brand.scss",
(
"after",
"website_sale/static/src/interactions/website_sale.js",
"/website_sale_product_brand/static/src/interactions/website_sale_brand_filter.esm.js",
),
],
"web.assets_tests": [
"/website_sale_product_brand/static/src/js/tour.esm.js",
"/website_sale_product_brand/static/src/js/test_website_sale_filter_brand.esm.js",
],
},
"installable": True,
}
4 changes: 4 additions & 0 deletions website_sale_product_brand/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# © 2016 Serpent Consulting Services Pvt. Ltd. (http://www.serpentcs.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import main
211 changes: 211 additions & 0 deletions website_sale_product_brand/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# © 2016 Serpent Consulting Services Pvt. Ltd. (http://www.serpentcs.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import http
from odoo.fields import Domain
from odoo.http import request

from odoo.addons.website_sale.controllers.main import QueryURL, WebsiteSale


class WebsiteSale(WebsiteSale):
def _update_domain(self, brands_list, domain):
selected_brand_ids = [int(brand) for brand in brands_list]
if brands_list:
for leaf in domain:
if leaf[0] == "product_brand_id":
domain.remove(leaf)
domain += [("product_brand_id", "in", selected_brand_ids)]
return domain

def _get_brand_ids(self, req):
return req.getlist("brand") or req.getlist("brand_ids") or []

def _build_brands_list(
self,
selected_brand_ids,
search=None,
products=None,
search_products=None,
category=None,
):
domain = []
if not products:
domain = [("id", "in", selected_brand_ids)]
elif search or category:
domain = [("product_ids", "in", search_products.ids)]
return (
request.env["product.brand"]
.sudo()
.search(domain)
.filtered(lambda x: x.is_published and x.published_products_count > 0)
)

def _get_shop_domain_no_brands(
self, search, category, attribute_value_dict, search_in_description
):
return super()._get_shop_domain(
search,
category,
attribute_value_dict,
search_in_description=search_in_description,
)

def _remove_extra_brands(self, brands, search_products, attrib_values):
if attrib_values:
search_product_brands = search_products.mapped("product_brand_id")
brands = brands.filtered(lambda b: b.id in search_product_brands.ids)
# sort brands by name
return brands.sorted(key=lambda brand: brand.name)

def _get_search_options(
self,
category=None,
attribute_value_dict=None,
tags=None,
min_price=0.0,
max_price=0.0,
conversion_rate=1,
**post,
):
res = super()._get_search_options(
category=category,
attribute_value_dict=attribute_value_dict,
tags=tags,
min_price=min_price,
max_price=max_price,
conversion_rate=conversion_rate,
**post,
)
res["brand_ids"] = request.env.context.get("brand_ids")
return res

def _get_shop_domain(
self, search, category, attribute_value_dict, search_in_description=True
):
domain = super()._get_shop_domain(
search,
category,
attribute_value_dict,
search_in_description=search_in_description,
)
brand_ids = request.env.context.get("brand_ids") or [
int(b)
for b in (
request.httprequest.args.getlist("brand")
or request.httprequest.args.getlist("brand_ids")
)
if b and str(b).isdigit()
]
if brand_ids:
domain = Domain.AND([domain, [("product_brand_id", "in", brand_ids)]])
return domain

@http.route(
[
"/shop",
"/shop/page/<int:page>",
'/shop/category/<model("product.public.category"):category>',
'/shop/category/<model("product.public.category"):category'
">/page/<int:page>", # Continue previous line
"/shop/brands",
],
type="http",
auth="public",
website=True,
)
def shop(
self,
page=0,
category=None,
search="",
min_price=0.0,
max_price=0.0,
ppg=False,
brand=None,
**post,
):
brands_list = self._get_brand_ids(request.httprequest.args)
if brands_list:
request.update_context(brand_ids=[int(b) for b in brands_list])
elif brand:
context = dict(request.env.context)
context.setdefault("brand_ids", [int(brand)])
request.update_context(**context)
res = super().shop(
page=page,
category=category,
search=search,
min_price=min_price,
max_price=max_price,
ppg=ppg,
brand=brand,
**post,
)
qcontext = getattr(res, "qcontext", None) or {}
attrib_values = qcontext.get("attrib_values")
products = qcontext.get("products")
if attrib_values is None or products is None:
return res
attribute_values = request.httprequest.args.getlist("attribute_values")
if attribute_values:
post["attribute_values"] = attribute_values
domain = self._get_shop_domain_no_brands(
search, category, attrib_values, search_in_description=False
)
search_products = request.env["product.template"].search(domain)
# build brands list
selected_brand_ids = [int(brand) for brand in brands_list]
brands = self._build_brands_list(
selected_brand_ids, search, products, search_products, category
)
brands = self._remove_extra_brands(brands, search_products, attrib_values)
# use search() domain instead of mapped() for better performance:
# will basically search for product's related attribute values
attrib_valid_ids = (
request.env["product.attribute.value"]
.search(
[
"&",
(
"pav_attribute_line_ids.product_tmpl_id",
"in",
search_products._ids,
),
("pav_attribute_line_ids.value_ids", "!=", False),
]
)
.ids
)
keep = qcontext.get("keep")
if keep and hasattr(keep, "args"):
keep.args["brand"] = brands_list
keep.args["brand_ids"] = selected_brand_ids
# assign values for usage in qweb
qcontext.update(
{
"brands": brands,
"selected_brand_ids": selected_brand_ids,
"attr_valid": attrib_valid_ids,
}
)
return res

# Method to get the brands.
@http.route(["/page/product_brands"], type="http", auth="public", website=True)
def product_brands(self, **post):
b_obj = request.env["product.brand"]
domain = [("website_published", "=", True)]
if post.get("search"):
domain += [("name", "ilike", post.get("search"))]
brand_rec = (
b_obj.sudo()
.search(domain)
.filtered(lambda x: x.published_products_count > 0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

published_products_count > 0 can be added to the domain

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ricardoalso i think not it's a compute

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it's a computed and stored field. Not searchable as-is 👍

This leads me to question the utility of filtering on published_products_count > 0 🤔

It wasn't the case in previous versions, and doing so will exclude brands from the website even if we want to display a landing (for example) page for brands we have but have not yet configured any product.template for. I am currently working on such improvements (product brand landing page), and with this business case it makes more sense to display all brands even if no product.template is associated. What do you think? Did you have any specific technical or business need or issue that led you to implement this filter on published_products_count > 0?

Thanks in advance for your input 🙏

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened a PR #1204 with the feature "brand landing page"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ricardoalso We were looking into it and saw the need to make that improvement because I think it makes perfect sense; why publish a brand if there isn't an active product yet?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can make sense in the current state indeed. I will add a configuration in my PR where I add the brand "landing pages" feature

Thanks for you feedback

)

keep = QueryURL("/page/product_brands", brand_id=[])
values = {"brand_rec": brand_rec, "keep": keep}
if post.get("search"):
values.update({"search": post.get("search")})
return request.render("website_sale_product_brand.product_brands", values)
Loading
Loading