fix: group Odoo products by template — show variant count instead of duplicates

Search endpoint now queries product.template instead of product.product,
so a product with 20 variants shows as 1 row with a "20 variants" badge
instead of 20 duplicate rows. Category name shown in sub-text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-01 17:42:01 -04:00
parent 9d483fb474
commit a3fa1ced16
2 changed files with 54 additions and 15 deletions

View File

@@ -68,21 +68,52 @@ class WooProductSearchController(http.Controller):
if instance.exists() and instance.excluded_category_ids:
domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
total = request.env['product.product'].search_count(domain)
products = request.env['product.product'].search(domain, limit=limit, offset=offset)
# Search product.template to group variants together
tmpl_domain = []
if query:
tmpl_domain = [
'|',
('name', 'ilike', query),
('default_code', 'ilike', query),
]
if category_id:
tmpl_domain.append(('categ_id', '=', int(category_id)))
if exclude_category_ids:
if isinstance(exclude_category_ids, str):
import json as _json2
try:
exclude_category_ids = _json2.loads(exclude_category_ids)
except (ValueError, TypeError):
exclude_category_ids = []
if exclude_category_ids:
tmpl_domain.append(('categ_id', 'not in', [int(x) for x in exclude_category_ids]))
if apply_excluded and instance_id:
instance = request.env['woo.instance'].browse(int(instance_id))
if instance.exists() and instance.excluded_category_ids:
tmpl_domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
total = request.env['product.template'].search_count(tmpl_domain)
templates = request.env['product.template'].search(tmpl_domain, limit=limit, offset=offset)
results = []
for tmpl in templates:
variant_count = len(tmpl.product_variant_ids)
# Use first variant as representative
first_variant = tmpl.product_variant_ids[:1]
results.append({
'id': first_variant.id if first_variant else tmpl.id,
'template_id': tmpl.id,
'name': tmpl.name,
'default_code': tmpl.default_code or '',
'list_price': tmpl.list_price,
'qty_available': sum(tmpl.product_variant_ids.mapped('qty_available')),
'categ_name': tmpl.categ_id.name if tmpl.categ_id else '',
'variant_count': variant_count,
'has_variants': variant_count > 1,
})
return {
'results': [
{
'id': p.id,
'name': p.name,
'default_code': p.default_code or '',
'list_price': p.list_price,
'qty_available': p.qty_available,
'categ_name': p.categ_id.name if p.categ_id else '',
}
for p in products
],
'results': results,
'total': total,
}

View File

@@ -368,10 +368,18 @@
<div class="woo-split-item"
t-att-class="state.selectedOdooId === op.id ? 'selected' : ''"
t-on-click="() => this.selectOdoo(op.id)">
<div class="woo-split-item-name"><t t-esc="op.name"/></div>
<div class="woo-split-item-name">
<t t-esc="op.name"/>
<t t-if="op.has_variants">
<span class="woo-badge woo-badge-unmapped ms-2">
<t t-esc="op.variant_count"/> variants
</span>
</t>
</div>
<div class="woo-split-item-sub">
<t t-if="op.default_code">SKU: <t t-esc="op.default_code"/> · </t>
$<t t-esc="op.list_price.toFixed(2)"/>
<t t-esc="this.formatPrice(op.list_price)"/>
<t t-if="op.categ_name"> · <t t-esc="op.categ_name"/></t>
</div>
</div>
</t>