diff --git a/custom_sale_purchase_display/__init__.py b/custom_sale_purchase_display/__init__.py
new file mode 100644
index 0000000000..0650744f6b
--- /dev/null
+++ b/custom_sale_purchase_display/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/custom_sale_purchase_display/__manifest__.py b/custom_sale_purchase_display/__manifest__.py
new file mode 100644
index 0000000000..670fa6178c
--- /dev/null
+++ b/custom_sale_purchase_display/__manifest__.py
@@ -0,0 +1,14 @@
+{
+ 'name': 'Sale Order Product History',
+ 'version': '1.0',
+ 'category': 'Sales',
+ 'depends': ['sale_management', 'stock', 'purchase'],
+ 'data': [
+ 'views/purchase_order_form.xml',
+ 'views/sale_order_form_view.xml',
+ 'views/product_template_kanban_catalog.xml',
+ ],
+ 'installable': True,
+ 'application': False,
+ 'license': 'LGPL-3'
+}
diff --git a/custom_sale_purchase_display/models/__init__.py b/custom_sale_purchase_display/models/__init__.py
new file mode 100644
index 0000000000..18b37e8532
--- /dev/null
+++ b/custom_sale_purchase_display/models/__init__.py
@@ -0,0 +1,2 @@
+from . import product_product
+from . import product_template
diff --git a/custom_sale_purchase_display/models/product_product.py b/custom_sale_purchase_display/models/product_product.py
new file mode 100644
index 0000000000..fbde660dcc
--- /dev/null
+++ b/custom_sale_purchase_display/models/product_product.py
@@ -0,0 +1,79 @@
+from odoo import models, fields, api
+
+
+class ProductProduct(models.Model):
+ _inherit = 'product.product'
+
+ last_invoice_date = fields.Date(
+ string="Last Invoice Date",
+ compute="_compute_last_invoice_data",
+ store=False
+ )
+ last_invoice_time_diff = fields.Char(
+ string="Last Invoice Time Diff",
+ compute="_compute_last_invoice_data",
+ store=False
+ )
+
+ def _compute_last_invoice_data(self):
+ for product in self:
+ partner_id = product.env.context.get('sale_order_partner_id') \
+ or product.env.context.get('purchase_order_partner_id')
+
+ is_sale = bool(product.env.context.get('sale_order_partner_id'))
+ move_type = 'out_invoice' if is_sale else 'in_invoice'
+
+ domain = [
+ ('state', '=', 'posted'),
+ ('invoice_date', '!=', False),
+ ('line_ids.product_id', '=', product.id),
+ ('move_type', '=', move_type),
+ ]
+ if partner_id:
+ domain.append(('partner_id', '=', partner_id))
+
+ move = product.env['account.move'].search(
+ domain, order='invoice_date desc', limit=1
+ )
+
+ product.last_invoice_date = move.invoice_date if move else False
+ product.last_invoice_time_diff = (
+ self._format_time_diff(move.invoice_date) if move else False
+ )
+
+ @api.model
+ def _get_recent_invoices(self, partner_id, is_sale=True):
+ if not partner_id:
+ return []
+
+ move_type = 'out_invoice' if is_sale else 'in_invoice'
+ moves = self.env['account.move'].search([
+ ('partner_id', '=', partner_id),
+ ('move_type', '=', move_type),
+ ('state', '=', 'posted'),
+ ('invoice_date', '!=', False)
+ ], order='invoice_date desc')
+
+ recent, seen = [], set()
+ for mv in moves:
+ for line in mv.line_ids.filtered('product_id'):
+ pid = line.product_id.id
+ if pid not in seen:
+ recent.append({'pid': pid, 'date': mv.invoice_date})
+ seen.add(pid)
+ return recent
+
+ @api.model
+ def _format_time_diff(self, invoice_date):
+ if not invoice_date:
+ return ""
+ days = (fields.Date.today() - invoice_date).days
+ if days > 365:
+ return f"{days // 365}y ago"
+ if days > 30:
+ return f"{days // 30}mo ago"
+ if days > 7:
+ return f"{days // 7}w ago"
+ if days > 0:
+ return f"{days}d ago"
+ return "today"
diff --git a/custom_sale_purchase_display/models/product_template.py b/custom_sale_purchase_display/models/product_template.py
new file mode 100644
index 0000000000..1e76394b4a
--- /dev/null
+++ b/custom_sale_purchase_display/models/product_template.py
@@ -0,0 +1,68 @@
+from odoo import models, fields, api
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ last_invoice_date = fields.Date(
+ string="Last Invoice Date",
+ related='product_variant_id.last_invoice_date',
+ store=False
+ )
+ last_invoice_time_diff = fields.Char(
+ string="Last Invoice Time Diff",
+ related='product_variant_id.last_invoice_time_diff',
+ store=False
+ )
+
+ product_variant_id = fields.Many2one(
+ 'product.product',
+ compute='_compute_product_variant_id',
+ store=True,
+ index=True
+ )
+
+ @api.model
+ def name_search(self, name="", args=None, operator="ilike", limit=100):
+ args = args or []
+
+ partner_id = self.env.context.get('sale_order_partner_id') \
+ or self.env.context.get('purchase_order_partner_id')
+ if not partner_id:
+ return super().name_search(name, args, operator, limit)
+
+ is_sale = bool(self.env.context.get('sale_order_partner_id'))
+ recent_lines = self.env['product.product']._get_recent_invoices(
+ partner_id=partner_id,
+ is_sale=is_sale
+ )
+
+ if not recent_lines:
+ return super().name_search(name, args, operator, limit)
+
+ recent_template_ids = list(dict.fromkeys(
+ self.env['product.product'].browse(rl['pid']).product_tmpl_id.id
+ for rl in recent_lines
+ ))
+
+ base_domain = [('name', operator, name)] + args
+
+ recent_templates = self.search(
+ [('id', 'in', recent_template_ids)] + base_domain,
+ limit=limit
+ )
+ other_templates = self.search(
+ [('id', 'not in', recent_template_ids)] + base_domain,
+ limit=max(0, limit - len(recent_templates))
+ )
+
+ results = []
+ for tmpl_id in recent_template_ids:
+ tmpl = recent_templates.filtered(lambda t: t.id == tmpl_id)
+ if tmpl:
+ td = tmpl.last_invoice_time_diff
+ label = f"{tmpl.display_name} ⏱ {td}" if td else tmpl.display_name
+ results.append((tmpl.id, label))
+
+ results.extend((t.id, t.display_name) for t in other_templates)
+ return results
diff --git a/custom_sale_purchase_display/views/product_template_kanban_catalog.xml b/custom_sale_purchase_display/views/product_template_kanban_catalog.xml
new file mode 100644
index 0000000000..7224063bbd
--- /dev/null
+++ b/custom_sale_purchase_display/views/product_template_kanban_catalog.xml
@@ -0,0 +1,32 @@
+
+
+
+ product.product.kanban.inherit.catalog
+ product.product
+
+
+
+
+
+
+
+
+
+ (+ )
+
+
+ ()
+
+
+ (0)
+
+
+ ⏱
+
+
+ 📅
+
+
+
+
+
diff --git a/custom_sale_purchase_display/views/purchase_order_form.xml b/custom_sale_purchase_display/views/purchase_order_form.xml
new file mode 100644
index 0000000000..8ea02c5693
--- /dev/null
+++ b/custom_sale_purchase_display/views/purchase_order_form.xml
@@ -0,0 +1,13 @@
+
+
+
+ purchase.order.form.inherit.custom
+ purchase.order
+
+
+
+ {'purchase_order_partner_id': parent.partner_id}
+
+
+
+
diff --git a/custom_sale_purchase_display/views/sale_order_form_view.xml b/custom_sale_purchase_display/views/sale_order_form_view.xml
new file mode 100644
index 0000000000..ca78d8637e
--- /dev/null
+++ b/custom_sale_purchase_display/views/sale_order_form_view.xml
@@ -0,0 +1,16 @@
+
+
+
+ sale.order.form.inherit.custom
+ sale.order
+
+
+
+ {'sale_order_partner_id': parent.partner_id}
+
+
+ {'sale_order_partner_id': parent.partner_id}
+
+
+
+