Skip to content
Draft
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
1 change: 1 addition & 0 deletions last_purchased_products/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
21 changes: 21 additions & 0 deletions last_purchased_products/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "Last Purchased Products",
"version": "1.0",
"depends": ["sale_management", "purchase", "stock"],
"author": "danal",
"category": "Category",
"license": "LGPL-3",
"data": [
"views/sale_order_views.xml",
"views/account_move_views.xml",
"views/purchase_order_views.xml",
"views/product_views.xml"
],
"assets": {
'web.assets_backend': [
'last_purchased_products/static/src/product_catalog/**/*.xml',
]
},
"installable": True,
"application": False,
}
4 changes: 4 additions & 0 deletions last_purchased_products/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import product_template
from . import product
121 changes: 121 additions & 0 deletions last_purchased_products/models/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from dateutil.relativedelta import relativedelta
import datetime

from odoo import models, api, fields


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

last_order = fields.Datetime(compute="_compute_last_order")
last_invoice_date = fields.Datetime(compute="_compute_last_invoice_date")
last_invoice_time = fields.Char(compute="_compute_invoice_time")

@api.depends_context("customer", "formatted_display_name")
def _compute_display_name(self):
res = super()._compute_display_name()
if not self.env.context.get("customer") or not self.env.context.get(
"formatted_display_name"
):
return res
compute_agotime_ref = self.compute_agotime
for product in self:
if not product.last_order:
continue
ago = compute_agotime_ref(product.last_order)
current_product_name = product.display_name or ""
if self.env.context.get("formatted_display_name"):
if ago:
time_postfix = f"\t--{ago}--"
else:
time_postfix = ""
product.display_name = f"{current_product_name}{time_postfix}"
else:
product.display_name = f"{current_product_name}"

@api.depends_context("customer", "vendor")
def _compute_last_invoice_date(self):
customer_id = self.env.context.get("customer")
vendor_id = self.env.context.get("vendor")
domain = [
("product_id", "in", self.ids),
("parent_state", "=", "posted"),
]
if customer_id:
domain.append(("move_id.move_type", "=", "out_invoice"))
domain.append(("partner_id", "=", customer_id))
else:
domain.append(("move_id.move_type", "=", "in_invoice"))
domain.append(("partner_id", "=", vendor_id))

last_invoice_dates = self.env["account.move.line"].search(
domain, order="create_date desc"
)
invoice_dates = {}
for invoice in last_invoice_dates:
if invoice.product_id.id not in invoice_dates:
invoice_dates[invoice.product_id.id] = invoice.create_date
for product in self:
product.last_invoice_date = invoice_dates.get(product.id, False)

@api.depends_context("customer")
def _compute_last_order(self):
last_orders = self.env["sale.order.line"].search(
[
("order_id.partner_id", "=", self.env.context.get("customer")),
("product_id", "in", self.ids),
("state", "=", "sale"),
],
order="order_id desc",
)
order_dates = {}
for order in last_orders:
if order.product_id.id not in order_dates:
order_dates[order.product_id.id] = order.order_id.date_order
for product in self:
product.last_order = order_dates.get(product.id, False)

@api.depends('last_invoice_date')
def _compute_invoice_time(self):
compute_agotime_ref = self.compute_agotime
for product in self:
if product.last_invoice_date:
ago = compute_agotime_ref(product.last_invoice_date)
if not ago or ("s" in ago):
ago = "Just Now"
product.last_invoice_time = ago
else:
product.last_invoice_time = False

@api.model
def name_search(self, name="", args=None, operator="ilike", limit=100):
if not self.env.context.get("customer") and not self.env.context.get("vendor"):
return super().name_search(name, args, operator, limit)
res = super().name_search(name, args, operator, limit=100)
ids = [r[0] for r in res]
records = self.browse(ids)
records.mapped("last_invoice_date")
sorted_records = records.sorted(
key=(lambda r: r.last_invoice_date or datetime.datetime.min), reverse=True
)
return [(r.id, r.display_name) for r in sorted_records][:limit]

def compute_agotime(self, datetime_field):
now = fields.Datetime.now()
rd = relativedelta(now, datetime_field)
if rd.years:
ago = f"{rd.years}y"
elif rd.months:
ago = f"{rd.months}mo"
elif rd.days:
ago = f"{rd.days}d"
elif rd.hours:
ago = f"{rd.hours}h"
elif rd.minutes:
ago = f"{rd.minutes}m"
elif rd.seconds:
ago = f"{rd.seconds}s"
else:
ago = ""

return ago
104 changes: 104 additions & 0 deletions last_purchased_products/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from dateutil.relativedelta import relativedelta
import datetime

from odoo import models, api, fields


class ProductTemplate(models.Model):
_inherit = "product.template"

last_order = fields.Datetime(compute="_compute_last_order")
last_invoice_date = fields.Datetime(compute="_compute_last_invoice_date")

@api.depends_context("customer", "formatted_display_name")
def _compute_display_name(self):
res = super()._compute_display_name()
if not self.env.context.get("customer") or not self.env.context.get(
"formatted_display_name"
):
return res

compute_agotime_ref = self.compute_agotime
for template in self:
if not template.last_order:
continue
ago = compute_agotime_ref(template.last_order)
current_product_template_name = template.display_name or ""
if self.env.context.get("formatted_display_name"):
if ago:
time_postfix = f"\t--{ago}--"
else:
time_postfix = ""
template.display_name = f"{current_product_template_name}{time_postfix}"
else:
template.display_name = f"{current_product_template_name}"

@api.depends_context("customer")
def _compute_last_order(self):
last_orders = self.env["sale.order.line"].search(
[
("order_id.partner_id", "=", self.env.context.get("customer")),
("product_id.product_tmpl_id", "in", self.ids),
("state", "=", "sale"),
],
order="order_id desc",
)
order_dates = {}
for order in last_orders:
if order.product_id.id not in order_dates:
order_dates[order.product_id.product_tmpl_id.id] = (
order.order_id.date_order
)
for template in self:
template.last_order = order_dates.get(template.id, False)

@api.depends_context("customer")
def _compute_last_invoice_date(self):
last_invoice_dates = self.env["account.move.line"].search(
[
("partner_id", "=", self.env.context.get("customer")),
("product_id.product_tmpl_id", "in", self.ids),
("parent_state", "=", "posted"),
],
order="create_date desc",
)
invoice_dates = {}
for invoice in last_invoice_dates:
if invoice.product_id.id not in invoice_dates:
invoice_dates[invoice.product_id.product_tmpl_id.id] = invoice.create_date
for template in self:
template.last_invoice_date = invoice_dates.get(template.id, False)

@api.model
def name_search(self, name="", args=None, operator="ilike", limit=100):
customer_id = self.env.context.get("customer")
if not customer_id:
return super().name_search(name, args, operator, limit)
res = super().name_search(name, args, operator, limit=100)
ids = [r[0] for r in res]
records = self.browse(ids)
records.mapped("last_invoice_date")
sorted_records = records.sorted(
key=(lambda r: r.last_invoice_date or datetime.datetime.min), reverse=True
)
return [(r.id, r.display_name) for r in sorted_records][:limit]

def compute_agotime(self, datetime_field):
now = fields.Datetime.now()
rd = relativedelta(now, datetime_field)
if rd.years:
ago = f"{rd.years}y"
elif rd.months:
ago = f"{rd.months}mo"
elif rd.days:
ago = f"{rd.days}d"
elif rd.hours:
ago = f"{rd.hours}h"
elif rd.minutes:
ago = f"{rd.minutes}m"
elif rd.seconds:
ago = f"{rd.seconds}s"
else:
ago = ""

return ago
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-inherit="product.ProductCatalogOrderLine" t-inherit-mode="extension">
<xpath expr="//span[@t-out=&quot;price&quot;]" position="attributes">
<attribute name="class">o_product_catalog_price fw-bold</attribute>
</xpath>
<xpath expr="//span[@t-out=&quot;price&quot;]" position="after">
<t t-if="this.env.displayUoM">
/ <span class="text-muted text-truncate w-50 me-4" t-out="props.uomDisplayName"/>
</t>
<t t-else="">
<span class="me-4"/>
</t>
</xpath>
</t>
</templates>
1 change: 1 addition & 0 deletions last_purchased_products/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_last_order_product
Loading