Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Contributors

- David Vidal
- David Bañón
- Carlos Roca

- Jairo Llopis (`Moduon <https://www.moduon.team>`__)

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import models
from . import wizard
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
"license": "AGPL-3",
"depends": ["product_sold_by_delivery_week", "sale_order_product_recommendation"],
"auto_install": True,
"data": ["wizard/sale_order_recommendation_view.xml"],
"data": [
"views/product_views.xml",
"wizard/sale_order_recommendation_view.xml",
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import product_product
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2026 Tecnativa - Carlos Roca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models


class ProductProduct(models.Model):
_inherit = "product.product"

weekly_sold_delivered_catalog_shown = fields.Char(
string="Weekly Sold for Catalog",
compute="_compute_weekly_sold_delivered_catalog_shown",
groups="sales_team.group_sale_salesman",
)
Comment on lines +9 to +13

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't this fit in the base module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right that there's no hard technical dependency: the catalog field doesn't use anything from sale_order_product_recommendation and the base module already has the catalog view and the weekly_partner_id filtering, so it could live there.

I kept it in the glue module on purpose, though. Its only reason to exist is to make the catalog match the recommendation wizard. And the base field is intentionally stored + cron-refreshed for performance, showing a global hint; moving this in would force a live, per-customer recompute on every catalog open for everyone using only the base module, changing its behavior and performance. So the field belongs where the "match the wizard" intent lives.

If you'd still prefer the global catalog hint to be live/per-customer for base users too, I can move the field + view there instead.


def _weekly_sold_delivered_shown_map(self, partner):
"""Return ``{product: formatted weekly string}`` for ``self`` computed
live and filtered by ``partner`` (its commercial partner). Services are
skipped."""
products = self.filtered(lambda p: p.type != "service")
if not products or not partner:
return {}
products_weekly = products.with_context(
weekly_partner_id=partner.id,
)._weekly_sold_delivered()
return {
product: self._format_weekly_string(products_weekly.get(product, False))
for product in products
}

def _get_catalog_weekly_partner(self):
"""Return the partner to filter the weekly sales by when the product
catalog is opened from a sale order, so the hint matches the
recommendation wizard. Empty recordset in any other context."""
if self.env.context.get("product_catalog_order_model") != "sale.order":
return self.env["res.partner"]
order_id = self.env.context.get("product_catalog_order_id")
if not order_id:
return self.env["res.partner"]
order = self.env["sale.order"].browse(order_id).exists()
return order.partner_id.commercial_partner_id

@api.depends_context("product_catalog_order_model", "product_catalog_order_id")
def _compute_weekly_sold_delivered_catalog_shown(self):
"""Live, customer-filtered weekly sales hint that replaces, in the
product catalog opened from a sale order, the globally stored hint added
by ``product_sold_by_delivery_week``."""
self.weekly_sold_delivered_catalog_shown = False
weekly_map = self._weekly_sold_delivered_shown_map(
self._get_catalog_weekly_partner()
)
for product in self:
product.weekly_sold_delivered_catalog_shown = weekly_map.get(product, False)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
- [Tecnativa](https://www.tecnativa.com):
- David Vidal
- David Bañón
- Carlos Roca
- Jairo Llopis ([Moduon](https://www.moduon.team))
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ <h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>David Vidal</li>
<li>David Bañón</li>
<li>Carlos Roca</li>
</ul>
</li>
<li>Jairo Llopis (<a class="reference external" href="https://www.moduon.team">Moduon</a>)</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Tecnativa - Carlos Roca
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="product_view_kanban_catalog" model="ir.ui.view">
<field name="model">product.product</field>
<field
name="inherit_id"
ref="product_sold_by_delivery_week.product_view_kanban_catalog"
/>
<field name="arch" type="xml">
<!-- Replace the globally stored hint with the live, customer-filtered
one so the catalog matches the recommendation wizard. -->
<field name="weekly_sold_delivered_shown" position="after">
<field
groups="product_sold_by_delivery_week.group_weekly_selling_order_line"
name="weekly_sold_delivered_catalog_shown"
/>
</field>
<field name="weekly_sold_delivered_shown" position="attributes">
<attribute name="invisible">1</attribute>
</field>
</field>
</record>
</odoo>
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@ class SaleOrderRecommendationLine(models.TransientModel):
@api.depends("product_id")
def _compute_weekly_sold_delivered_shown(self):
"""Compute dinamically in the view"""
_format_weekly_string = self.env["product.product"]._format_weekly_string
self.weekly_sold_delivered_shown = False
products = self.mapped("product_id").filtered(lambda x: x.type != "service")
common_partner = self.wizard_id.order_id.partner_id.commercial_partner_id
products_weekly = products.with_context(
weekly_partner_id=common_partner.id,
)._weekly_sold_delivered()
for line in self.filtered(lambda x: x.product_id.type != "service"):
line.weekly_sold_delivered_shown = _format_weekly_string(
products_weekly.get(line.product_id, False)
)
weekly_map = self.mapped("product_id")._weekly_sold_delivered_shown_map(
common_partner
)
for line in self:
line.weekly_sold_delivered_shown = weekly_map.get(line.product_id, False)
Loading