Skip to content

Commit 5d57513

Browse files
committed
[IMP] products_orderby_invoice: Imp search and catalog based on invoice history
Purpose: Make it easier to find and show products based on past invoices or bills. Approach: Changed name_search to list first the products already invoiced to the customer, sorted by latest date. Updated product catalog view to show last invoice date, moved info around, and added forecasted qty with color. Impact: Improves product search and helps users quickly spot stock levels and recent purchases.
1 parent db900fb commit 5d57513

File tree

4 files changed

+61
-48
lines changed

4 files changed

+61
-48
lines changed

products_orderby_invoice/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
'views/product_views.xml',
99
],
1010
'installable': True,
11+
'license': 'LGPL-3',
1112
}

products_orderby_invoice/models/product_product.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1+
# Part of Odoo. See LICENSE file for full copyright and licensing details.
2+
13
from odoo import api, fields, models
24

5+
36
class ProductProduct(models.Model):
47
_inherit = 'product.product'
58

6-
79
surplus_qty = fields.Float(string='Surplus Quantity', compute='_compute_surplus_qty')
10+
invoice_date = fields.Datetime(string='Last Invoice Date', compute='_compute_invoice_date')
11+
12+
@api.depends('invoice_date')
13+
def _compute_invoice_date(self):
14+
for rec in self:
15+
lines = self.env['account.move.line'].search([
16+
('product_id', '=', rec.id),
17+
('move_id.move_type', '=', 'out_invoice'),
18+
('move_id.state', '=', 'posted'),
19+
], limit=1, order='date desc')
20+
rec.invoice_date = lines.move_id.date if lines else False
821

922
@api.depends('virtual_available', 'qty_available')
1023
def _compute_surplus_qty(self):
@@ -13,7 +26,6 @@ def _compute_surplus_qty(self):
1326

1427
@api.model
1528
def name_search(self, name, args=None, operator='ilike', limit=100):
16-
print("===============This is triggered====================")
1729
args = list(args) if args else []
1830
partner_id = self.env.context.get('partner_id')
1931
results = []
@@ -29,15 +41,12 @@ def name_search(self, name, args=None, operator='ilike', limit=100):
2941

3042
lines = sorted(lines, key=lambda l: l.move_id.invoice_date or l.create_date, reverse=True)
3143

32-
name_lower = name.lower() if name else ''
33-
3444
for line in lines:
3545
product = line.product_id
3646
if product.id in matched_ids:
3747
continue
38-
if not name or (operator == 'ilike' and name_lower in product.name.lower()):
39-
results.append((product.id, product.display_name))
40-
matched_ids.add(product.id)
48+
results.append((product.id, product.display_name))
49+
matched_ids.add(product.id)
4150
if len(results) >= limit:
4251
break
4352

@@ -47,7 +56,7 @@ def name_search(self, name, args=None, operator='ilike', limit=100):
4756
if name:
4857
domain.append(('name', operator, name))
4958
if matched_ids:
50-
domain.append(('id', 'not in', list(matched_ids))) # needs to be a list here
59+
domain.append(('id', 'not in', list(matched_ids)))
5160
others = super().name_search(name, domain, operator=operator, limit=remaining)
5261
results.extend(others)
5362

products_orderby_invoice/models/product_template.py

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Part of Odoo. See LICENSE file for full copyright and licensing details.
2+
13
from odoo import models, fields, api
24
from datetime import date
35

@@ -10,48 +12,35 @@ def name_search(self, name, args=None, operator="ilike", limit=100):
1012
args = list(args) if args else []
1113
partner_id = self.env.context.get("partner_id")
1214
results = []
13-
matched_ids = []
15+
seen_ids = set()
1416

15-
lines = self.env["account.move.line"].search([
17+
if partner_id:
18+
lines = self.env["account.move.line"].search([
1619
("move_id.move_type", "=", "out_invoice"),
1720
("move_id.partner_id", "=", partner_id),
1821
("move_id.state", "=", "posted"),
1922
("product_id.product_tmpl_id", "!=", False),
2023
])
21-
22-
lines = sorted(
23-
lines, key=lambda l: l.move_id.invoice_date or fields.Date.today(),
24-
reverse=True)
25-
26-
tmpl_map = {}
27-
for line in lines:
28-
tmpl = line.product_id.product_tmpl_id
29-
if tmpl.id not in tmpl_map:
30-
tmpl_map[tmpl.id] = {"tmpl": tmpl, "date": line.move_id.invoice_date}
31-
if len(tmpl_map) >= limit:
32-
break
33-
34-
name_lower = name.lower() if name else ""
35-
today = date.today()
36-
37-
for info in tmpl_map.values():
38-
tmpl = info["tmpl"]
39-
invoice_date = info["date"]
40-
if not name or (operator == "ilike" and name_lower in tmpl.name.lower()):
41-
days = (today - invoice_date).days if invoice_date else "?"
42-
display = f"{tmpl.display_name} (Last ordered {days} days ago)"
24+
lines = sorted(lines, key=lambda l: l.move_id.invoice_date or fields.Date.today(), reverse=True)
25+
26+
for line in lines:
27+
tmpl = line.product_id.product_tmpl_id
28+
if tmpl.id in seen_ids:
29+
continue
30+
days = line.product_id.product_tmpl_id.sale_delay
31+
display = f"{tmpl.display_name} (Order Lead time {days} days)"
4332
results.append((tmpl.id, display))
44-
matched_ids.append(tmpl.id)
45-
if len(results) >= limit:
46-
break
33+
seen_ids.add(tmpl.id)
34+
if len(results) >= limit:
35+
break
4736

4837
remaining = limit - len(results)
4938
if remaining > 0:
5039
domain = args[:]
5140
if name:
5241
domain.append(("name", operator, name))
53-
if matched_ids:
54-
domain.append(("id", "not in", matched_ids))
42+
if seen_ids:
43+
domain.append(("id", "not in", list(seen_ids)))
5544
others = super().name_search(name, args=domain, operator=operator, limit=remaining)
5645
results.extend(others)
5746

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<odoo>
33
<record id="product_view_kanban_catalog_surplus_qty" model="ir.ui.view">
4-
<field name="name">product.view.kanban.catalog.inherit.surplus.qty</field>
5-
<field name="model">product.product</field>
6-
<field name="inherit_id" ref="product.product_view_kanban_catalog"/>
7-
<field name="arch" type="xml">
8-
<xpath expr="//div[@name='o_kanban_qty_available']/field[@name='uom_id']" position="after">
9-
<span> (</span>
10-
<field name="surplus_qty"></field>
11-
<span>)</span>
12-
</xpath>
13-
</field>
14-
</record>
4+
<field name="name">product.view.kanban.catalog.inherit.surplus.qty</field>
5+
<field name="model">product.product</field>
6+
<field name="inherit_id" ref="product.product_view_kanban_catalog" />
7+
<field name="arch" type="xml">
8+
<xpath expr="//div[@name='o_kanban_qty_available']/field[@name='uom_id']"
9+
position="after">
10+
<span> (</span>
11+
<t t-if="record.surplus_qty.raw_value &gt; 0">
12+
<span class="text-success">+<field name="surplus_qty" /></span>
13+
</t>
14+
<t t-elif="record.surplus_qty.raw_value &lt; 0">
15+
<span class="text-danger">
16+
<field name="surplus_qty" />
17+
</span>
18+
</t>
19+
<t t-else="">
20+
<span class="text-muted">0.00</span>
21+
</t>
22+
<span>)</span>
23+
<div>
24+
Last invoice date :<field name="invoice_date" widget="remaining_days"/>
25+
</div>
26+
</xpath>
27+
</field>
28+
</record>
1529
</odoo>

0 commit comments

Comments
 (0)