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:
@@ -68,21 +68,52 @@ class WooProductSearchController(http.Controller):
|
|||||||
if instance.exists() and instance.excluded_category_ids:
|
if instance.exists() and instance.excluded_category_ids:
|
||||||
domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
|
domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
|
||||||
|
|
||||||
total = request.env['product.product'].search_count(domain)
|
# Search product.template to group variants together
|
||||||
products = request.env['product.product'].search(domain, limit=limit, offset=offset)
|
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 {
|
return {
|
||||||
'results': [
|
'results': 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
|
|
||||||
],
|
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -368,10 +368,18 @@
|
|||||||
<div class="woo-split-item"
|
<div class="woo-split-item"
|
||||||
t-att-class="state.selectedOdooId === op.id ? 'selected' : ''"
|
t-att-class="state.selectedOdooId === op.id ? 'selected' : ''"
|
||||||
t-on-click="() => this.selectOdoo(op.id)">
|
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">
|
<div class="woo-split-item-sub">
|
||||||
<t t-if="op.default_code">SKU: <t t-esc="op.default_code"/> · </t>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
Reference in New Issue
Block a user