Skip to content

Commit c8e3426

Browse files
committed
[IMP] last_purchased_products: add test suite for computation and sorting logic
This commit introduces the `TestLastOrderProduct` test suite to ensure the stability of product sorting and time computation based on sales and purchase history. Key scenarios covered: Last Order Computation: - Verifies that confirming a Sale Order updates the `last_order` field. - Checks that the 'Ago' time suffix (e.g., '--1h--') is correctly appended to `display_name` when the context is active. Last Invoice Date: - Verifies that posting a Customer Invoice updates `last_invoice_date`. - Ensures valid date comparisons between fields. Vendor Bill Sorting: - Verifies that posting a Vendor Bill updates `last_invoice_date` when in a vendor context. - Ensures products from recent bills appear at the top of search results. Name Search Sorting: - Verifies that recently invoiced products appear at the top of the list in the `name_search` results. - Confirms that sorting falls back to the default (alphabetical) when no customer/vendor context is provided. Time Display Logic: - Validates `last_invoice_time` computation. - Specifically verifies that invoices created less than a minute ago return 'Just Now'. Context Handling: - Ensures logic only triggers when `customer`, `vendor`, or `formatted_display_name` keys are present in the context, preventing unwanted side effects in standard views.
1 parent b3720e4 commit c8e3426

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

last_purchased_products/models/product.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def _compute_invoice_time(self):
8181
for product in self:
8282
if product.last_invoice_date:
8383
ago = compute_agotime_ref(product.last_invoice_date)
84-
if not ago:
84+
if not ago or ("s" in ago):
8585
ago = "Just Now"
8686
product.last_invoice_time = ago
8787
else:
@@ -113,6 +113,8 @@ def compute_agotime(self, datetime_field):
113113
ago = f"{rd.hours}h"
114114
elif rd.minutes:
115115
ago = f"{rd.minutes}m"
116+
elif rd.seconds:
117+
ago = f"{rd.seconds}s"
116118
else:
117119
ago = ""
118120

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import test_last_order_product
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
from odoo.tests.common import TransactionCase
2+
from odoo.exceptions import UserError
3+
from odoo import fields, Command
4+
from datetime import timedelta
5+
6+
7+
class TestLastOrderProduct(TransactionCase):
8+
9+
@classmethod
10+
def setUpClass(self):
11+
super().setUpClass()
12+
13+
self.partner = self.env['res.users'].create({
14+
'name': 'Salesman Test User',
15+
'login': 'sales_test_user',
16+
'email': 'sales_test@example.com',
17+
})
18+
19+
# Product 1: Will have an Order
20+
self.product = self.env['product.product'].create({
21+
'name': 'A Ordered Product',
22+
'type': 'consu',
23+
'list_price': 100.0,
24+
})
25+
26+
# Product 2: Will have an Invoice
27+
self.product_second = self.env['product.product'].create({
28+
'name': 'B Invoiced Product',
29+
'type': 'consu',
30+
'list_price': 200.0,
31+
})
32+
33+
# Create Order for Product 1
34+
self.order = self.env['sale.order'].create({
35+
'partner_id': self.partner.partner_id.id,
36+
'order_line': [
37+
Command.create({
38+
'product_id': self.product.id,
39+
'product_uom_qty': 1,
40+
'price_unit': 100.0,
41+
})
42+
]
43+
})
44+
45+
# Create Invoice for Product 2
46+
self.invoice = self.env['account.move'].create({
47+
'move_type': 'out_invoice',
48+
'partner_id': self.partner.partner_id.id,
49+
'invoice_date': fields.Date.today(),
50+
'invoice_line_ids': [
51+
Command.create({
52+
'product_id': self.product_second.id,
53+
'quantity': 1,
54+
'price_unit': 200.0,
55+
})
56+
]
57+
})
58+
59+
# Create Bill for Product
60+
self.bill = self.env['account.move'].create({
61+
'move_type': 'in_invoice',
62+
'partner_id': self.partner.partner_id.id,
63+
'invoice_date': fields.Date.today(),
64+
'invoice_line_ids': [
65+
Command.create({
66+
'product_id': self.product.id,
67+
'quantity': 1,
68+
'price_unit': 200.0,
69+
})
70+
]
71+
})
72+
73+
self.past_time = fields.Datetime.now() - timedelta(hours=1)
74+
75+
def test_last_order_computation_name_search(self):
76+
""" Test that confirming a sale order updates the last_order and display_name has order time """
77+
self.order.action_confirm()
78+
self.assertEqual(self.order.state, 'sale')
79+
self.order.date_order = self.past_time
80+
ctx = {
81+
'customer': self.partner.partner_id.id,
82+
'formatted_display_name': True
83+
}
84+
product_with_ctx = self.product.with_context(ctx)
85+
product_with_ctx._compute_last_order()
86+
self.assertEqual(
87+
product_with_ctx.last_order,
88+
self.order.date_order,
89+
"Last order date should match the sale order date"
90+
)
91+
results = self.env['product.product'].with_context(ctx).name_search(name=self.product.name)
92+
ago = self.product.with_context(ctx).compute_agotime(self.order.date_order)
93+
self.assertEqual(ago, "1h", "Time computed should be 1h")
94+
result_name = results[0][1]
95+
self.assertIn("--1h--", result_name, "Name should contain the time suffix")
96+
97+
def test_last_invoice_date_computation_name_search(self):
98+
""" Test that confirming a create date updates the last_invoice_date and product appear on top """
99+
self.invoice.action_post()
100+
self.assertEqual(self.invoice.state, 'posted')
101+
ctx = {
102+
'customer': self.partner.partner_id.id,
103+
'formatted_display_name': True
104+
}
105+
product_with_ctx = self.product_second.with_context(ctx)
106+
product_with_ctx._compute_last_invoice_date()
107+
self.assertEqual(
108+
product_with_ctx.last_invoice_date,
109+
self.invoice.create_date,
110+
"Last invoice date should match the invoice creation date"
111+
)
112+
results = self.env['product.product'].with_context(ctx).name_search(name="")
113+
self.assertEqual(results[0][1], self.product_second.name, "Recently invoiced product should be on top.")
114+
115+
def test_no_customer_name_search(self):
116+
""" Test that if no customer is selected then should ive default display_name and sorting """
117+
self.invoice.action_post()
118+
self.order.action_confirm()
119+
ctx = {
120+
'customer': None,
121+
'formatted_display_name': True
122+
}
123+
results = self.env['product.product'].with_context(ctx).name_search(name="")
124+
result_ids = [r[0] for r in results]
125+
index_product_a = result_ids.index(self.product.id)
126+
index_product_b = result_ids.index(self.product_second.id)
127+
self.assertLess(index_product_a, index_product_b,
128+
"Without customer context, sorting should default.")
129+
product_a_result_name = next(r[1] for r in results if r[0] == self.product.id)
130+
self.assertNotIn("--", product_a_result_name, "Suffix should not be present without customer context")
131+
132+
def test_last_invoice_time_compute(self):
133+
""" Test to compute last_invoice_time which depends on last_invoice_date """
134+
self.invoice.action_post()
135+
ctx = {
136+
'customer': self.partner.partner_id.id,
137+
'order_id': self.order.id
138+
}
139+
product_with_ctx = self.product_second.with_context(ctx)
140+
product_with_ctx.with_context(ctx)._compute_invoice_time()
141+
self.assertEqual(
142+
product_with_ctx.last_invoice_time,
143+
"Just Now",
144+
"Last invoice time should be 'Just Now' for less then 1 minute older invoices"
145+
)
146+
147+
def test_purchase_order_product_sorting(self):
148+
self.bill.action_post()
149+
self.assertEqual(self.bill.state, 'posted')
150+
ctx = {
151+
'vendor': self.partner.partner_id.id,
152+
'formatted_display_name': True
153+
}
154+
product_with_ctx = self.product.with_context(ctx)
155+
product_with_ctx._compute_last_invoice_date()
156+
self.assertEqual(
157+
product_with_ctx.last_invoice_date,
158+
self.bill.create_date,
159+
"Last invoice date should match the bill creation date"
160+
)
161+
results = self.env['product.product'].with_context(ctx).name_search(name="")
162+
self.assertEqual(results[0][1], self.product.name, "Billed product should be at the top for Vendor context")

0 commit comments

Comments
 (0)