Skip to content

Commit 67e219c

Browse files
committed
[ADD] last_purchased_products: added last order time in product dropdown.
1 parent b68a192 commit 67e219c

File tree

10 files changed

+341
-0
lines changed

10 files changed

+341
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "Last Purchased Products",
3+
"version": "1.0",
4+
"depends": ["sale", "purchase"],
5+
"author": "danal",
6+
"category": "Category",
7+
"license": "LGPL-3",
8+
"data": [
9+
"views/sale_order_view.xml",
10+
"views/account_move_views.xml",
11+
"views/purchase_order_views.xml",
12+
"views/product_views.xml"
13+
],
14+
"assets": {
15+
'web.assets_backend': [
16+
'last_order_date/static/src/product_catalog/**/*.xml',
17+
]
18+
}
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Part of Odoo. See LICENSE file for full copyright and licensing details.
2+
3+
from . import product_template
4+
from . import product
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from dateutil.relativedelta import relativedelta
2+
import datetime
3+
4+
from odoo import models, api, fields
5+
6+
7+
class ProductProduct(models.Model):
8+
_inherit = "product.product"
9+
10+
last_order = fields.Datetime(compute="_compute_last_order")
11+
last_invoice_date = fields.Date(compute="_compute_last_invoice_date")
12+
last_invoice_time = fields.Char(compute="_compute_invoice_time")
13+
14+
@api.depends_context("customer", "formatted_display_name")
15+
def _compute_display_name(self):
16+
res = super()._compute_display_name()
17+
if not self.env.context.get("customer") or not self.env.context.get(
18+
"formatted_display_name"
19+
):
20+
return res
21+
22+
for product in self:
23+
if not product.last_order:
24+
continue
25+
ago = self.compute_agotime(fields.Datetime.now(),product.last_order)
26+
current_product_name = product.display_name or ""
27+
if self.env.context.get("formatted_display_name"):
28+
if ago:
29+
time_postfix = f"\t--{ago}--"
30+
else:
31+
time_postfix = ""
32+
product.display_name = f"{current_product_name}{time_postfix}"
33+
else:
34+
product.display_name = f"{current_product_name}"
35+
36+
def _compute_last_invoice_date(self):
37+
print("invoice", self.env.context.get("customer"))
38+
customer_id = self.env.context.get("customer")
39+
vendor_id = self.env.context.get("vendor")
40+
domain = [
41+
("product_id", "in", self.ids),
42+
("parent_state", "=", "posted"),
43+
]
44+
if customer_id:
45+
domain.append(("partner_id", "=", customer_id))
46+
else:
47+
domain.append(("move_id.move_type", "=", "in_invoice"))
48+
domain.append(("partner_id", "=", vendor_id))
49+
50+
last_invoice_dates = self.env["account.move.line"].search(
51+
domain, order="invoice_date desc"
52+
)
53+
invoice_dates = {}
54+
for invoice in last_invoice_dates:
55+
if invoice.product_id.id not in invoice_dates:
56+
invoice_dates[invoice.product_id.id] = invoice.invoice_date
57+
for product in self:
58+
product.last_invoice_date = invoice_dates.get(product.id, False)
59+
60+
def _compute_last_order(self):
61+
print("order")
62+
last_orders = self.env["sale.order.line"].search(
63+
[
64+
("order_id.partner_id", "=", self.env.context.get("customer")),
65+
("product_id", "in", self.ids),
66+
("state", "=", "sale"),
67+
],
68+
order="order_id desc",
69+
)
70+
order_dates = {}
71+
for order in last_orders:
72+
if order.product_id.id not in order_dates:
73+
order_dates[order.product_id.id] = order.order_id.date_order
74+
for product in self:
75+
product.last_order = order_dates.get(product.id, False)
76+
77+
@api.depends('last_invoice_date')
78+
def _compute_invoice_time(self):
79+
for product in self:
80+
if product.last_invoice_date:
81+
ago = self.compute_agotime(fields.Date.today(),product.last_invoice_date)
82+
if ago == "":
83+
ago = "Today"
84+
product.last_invoice_time = ago
85+
else:
86+
product.last_invoice_time = False
87+
88+
@api.model
89+
def name_search(self, name="", args=None, operator="ilike", limit=100):
90+
print("name search")
91+
if not self.env.context.get("customer") and not self.env.context.get("vendor"):
92+
return super().name_search(name, args, operator, limit)
93+
res = super().name_search(name, args, operator, limit=100)
94+
ids = [r[0] for r in res]
95+
records = self.browse(ids)
96+
records.mapped("last_invoice_date")
97+
sorted_records = records.sorted(
98+
key=(lambda r: r.last_invoice_date or datetime.date.min), reverse=True
99+
)
100+
return [(r.id, r.display_name) for r in sorted_records][:limit]
101+
102+
def compute_agotime(self,current, datetime_field):
103+
rd = relativedelta(current, datetime_field)
104+
if rd.years:
105+
ago = f"{rd.years}y"
106+
elif rd.months:
107+
ago = f"{rd.months}mo"
108+
elif rd.days:
109+
ago = f"{rd.days}d"
110+
elif rd.hours:
111+
ago = f"{rd.hours}h"
112+
elif rd.minutes:
113+
ago = f"{rd.minutes}m"
114+
elif rd.seconds:
115+
ago = f"{rd.seconds}s"
116+
else:
117+
ago = ""
118+
119+
return ago
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from dateutil.relativedelta import relativedelta
2+
import datetime
3+
4+
from odoo import models, api, fields
5+
6+
7+
class ProductTemplate(models.Model):
8+
_inherit = "product.template"
9+
10+
last_order = fields.Datetime(compute="_compute_last_order", default="None")
11+
last_invoice_date = fields.Date(compute="_compute_last_invoice_date")
12+
13+
@api.depends_context("customer", "formatted_display_name")
14+
def _compute_display_name(self):
15+
res = super()._compute_display_name()
16+
if not self.env.context.get("customer") or not self.env.context.get(
17+
"formatted_display_name"
18+
):
19+
return res
20+
21+
for template in self:
22+
if not template.last_order:
23+
continue
24+
ago = self.compute_agotime(template.last_order)
25+
current_product_template_name = template.display_name or ""
26+
if self.env.context.get("formatted_display_name"):
27+
if ago:
28+
time_postfix = f"\t--{ago}--"
29+
else:
30+
time_postfix = ""
31+
template.display_name = f"{current_product_template_name}{time_postfix}"
32+
else:
33+
template.display_name = f"{current_product_template_name}"
34+
35+
def _compute_last_order(self):
36+
last_orders = self.env["sale.order.line"].search(
37+
[
38+
("order_id.partner_id", "=", self.env.context.get("customer")),
39+
("product_id.product_tmpl_id", "in", self.ids),
40+
("state", "=", "sale"),
41+
],
42+
order="order_id desc",
43+
)
44+
order_dates = {}
45+
for order in last_orders:
46+
if order.product_id.id not in order_dates:
47+
order_dates[order.product_id.product_tmpl_id.id] = (
48+
order.order_id.date_order
49+
)
50+
for template in self:
51+
template.last_order = order_dates.get(template.id, False)
52+
53+
def _compute_last_invoice_date(self):
54+
last_invoice_dates = self.env["account.move.line"].search(
55+
[
56+
("partner_id", "=", self.env.context.get("customer")),
57+
("product_id.product_tmpl_id", "in", self.ids),
58+
("parent_state", "=", "posted"),
59+
],
60+
order="invoice_date desc",
61+
)
62+
invoice_dates = {}
63+
for invoice in last_invoice_dates:
64+
if invoice.product_id.id not in invoice_dates:
65+
invoice_dates[invoice.product_id.product_tmpl_id.id] = (
66+
invoice.invoice_date
67+
)
68+
for template in self:
69+
template.last_invoice_date = invoice_dates.get(template.id, False)
70+
71+
@api.model
72+
def name_search(self, name="", args=None, operator="ilike", limit=100):
73+
customer_id = self.env.context.get("customer")
74+
if not customer_id:
75+
return super().name_search(name, args, operator, limit)
76+
res = super().name_search(name, args, operator, limit=100)
77+
ids = [r[0] for r in res]
78+
records = self.browse(ids)
79+
records.mapped("last_invoice_date")
80+
sorted_records = records.sorted(
81+
key=(lambda r: r.last_invoice_date or datetime.date.min), reverse=True
82+
)
83+
return [(r.id, r.display_name) for r in sorted_records][:limit]
84+
85+
def compute_agotime(self, datetime_field):
86+
now = fields.Datetime.now()
87+
rd = relativedelta(now, datetime_field)
88+
if rd.years:
89+
ago = f"{rd.years}y"
90+
elif rd.months:
91+
ago = f"{rd.months}mo"
92+
elif rd.days:
93+
ago = f"{rd.days}d"
94+
elif rd.hours:
95+
ago = f"{rd.hours}h"
96+
elif rd.minutes:
97+
ago = f"{rd.minutes}m"
98+
elif rd.seconds:
99+
ago = f"{rd.seconds}s"
100+
else:
101+
ago = ""
102+
103+
return ago
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
<t t-inherit="product.ProductCatalogOrderLine" t-inherit-mode="extension">
4+
<xpath expr="//span[@t-out=&quot;price&quot;]" position="attributes">
5+
<attribute name="class">o_product_catalog_price fw-bold</attribute>
6+
</xpath>
7+
<xpath expr="//span[@t-out=&quot;price&quot;]" position="after">
8+
<t t-if="this.env.displayUoM">
9+
/ <span class="text-muted text-truncate w-50 me-4" t-out="props.uomDisplayName"/>
10+
</t>
11+
<t t-else="">
12+
<span class="me-4"/>
13+
</t>
14+
</xpath>
15+
</t>
16+
</templates>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<record id="view_move_form_custom_display" model="ir.ui.view">
4+
<field name="name">account.move.form.custom.display</field>
5+
<field name="model">account.move</field>
6+
<field name="inherit_id" ref="account.view_move_form"/>
7+
<field name="arch" type="xml">
8+
<xpath expr="//field[@name='invoice_line_ids']/list/field[@name='product_id']" position="attributes">
9+
<attribute name="context">{'customer': parent.partner_id}</attribute>
10+
</xpath>
11+
<xpath expr="//button[@name='action_add_from_catalog']" position="attributes">
12+
<attribute name="context">{'order_id': parent.id, 'customer': parent.partner_id}</attribute>
13+
</xpath>
14+
</field>
15+
</record>
16+
</odoo>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<record id="product_view_kanban_catalog_inherit" model="ir.ui.view">
4+
<field name="name">product.view.kanban.catalog.inherit</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="//field[@name='id']" position="after">
9+
<field name="virtual_available" invisible="1"/>
10+
<field name="last_invoice_time" invisible="1"/>
11+
</xpath>
12+
<xpath expr="//t[@name='qty_available']" position="inside">
13+
<t t-set="final_qty" t-value="record.virtual_available.raw_value - record.qty_available.raw_value"/>
14+
<t t-if="final_qty"> (
15+
<span t-if="final_qty >= 0" class="fw-bold text-success">
16+
+<t t-out="final_qty"/>
17+
</span>
18+
<span t-else="" class="fw-bold text-danger">
19+
<t t-out="final_qty"/>
20+
</span>
21+
)
22+
</t>
23+
</xpath>
24+
<xpath expr="//div[@name='o_kanban_qty_available_and_on_hand']" position="after">
25+
<t t-if="record.last_invoice_time.raw_value" name="last_invoice_time">
26+
<span class="text-muted">
27+
<t t-out="record.last_invoice_time.raw_value"/>
28+
<t t-if="record.last_invoice_time.raw_value != 'Today'"> ago</t>
29+
</span>
30+
</t>
31+
</xpath>
32+
</field>
33+
</record>
34+
</odoo>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<record id="view_purchase_order_form_custom_display" model="ir.ui.view">
4+
<field name="name">purchase.order.form.custom.display</field>
5+
<field name="model">purchase.order</field>
6+
<field name="inherit_id" ref="purchase.purchase_order_form"/>
7+
<field name="arch" type="xml">
8+
<xpath expr="//field[@name='order_line']/list/field[@name='product_id']" position="attributes">
9+
<attribute name="context">{'vendor': parent.partner_id}</attribute>
10+
</xpath>
11+
</field>
12+
</record>
13+
</odoo>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<record id="view_order_form_custom_display" model="ir.ui.view">
4+
<field name="name">sale.order.form.custom.display</field>
5+
<field name="model">sale.order</field>
6+
<field name="inherit_id" ref="sale.view_order_form"/>
7+
<field name="arch" type="xml">
8+
<xpath expr="//field[@name='order_line']/list/field[@name='product_template_id']" position="attributes">
9+
<attribute name="context">{'customer': parent.partner_id}</attribute>
10+
</xpath>
11+
<xpath expr="//button[@name='action_add_from_catalog']" position="attributes">
12+
<attribute name="context">{'order_id': parent.id,'customer': parent.partner_id}</attribute>
13+
</xpath>
14+
</field>
15+
</record>
16+
</odoo>

0 commit comments

Comments
 (0)