[FIX] product_expiry: update expiry dates correctly

Steps to reproduce:

- Install product_expiry
- Create Product P:
	- Product type: Storable
	- Inventory tab > Tracking: By Lots
	- Check Expiration Date
	- Expiration Date: 100 days after receipt
	- Best Before Date, Removal Date, Alert Date: 10 days before
	expiration date
	- Save
- Products > Lots/Serial Numbers > New
	- Product: P
	- open Dates tab

There are two different usecases and issues starting from this point.

Usecase 1:
	- Dates tab, delete all dates, save
	- set the Expiration Date, all other dates are set
	- without saving, update the Expiration Date again

Issue 1: The other dates don't update according to the new Expiration Date. This happens because neither if conditions in `_compute_dates` match the case where the product has an expiration date set, but it has not been saved to the lot.

Solution 1: Add an additional condition in the `_compute_dates` method where `lot.expiration_date` is set, but `lot._origin.expiration_date` is not set. This will ensure the other dates are updated correctly when setting the Expiration Date without saving.

Usecase 2:
	- Dates tab, set an Expiration Date, save
	- Without saving the record, update the Expiration Date several times, with dates in the future

Issue 2: The other dates (Best Before Date, Removal Date, Alert Date) will drift off further in the future, while they should always be 10 days apart from the expiration date. This happens because the new dates are being set relative to the actual value of the date, which changes with each successive update.

Solution 2: set the new dates relative to the value of the `lot._origin` dates, which remain constant between successive (saveless) updates. This will ensure that the other dates maintain a 10-day difference from the Expiration Date consistently.

opw-3374135

closes odoo/odoo#130599

X-original-commit: a275cbcd2d8a87bbd8cadf85c094625ebd8358c4
Signed-off-by: Tiffany Chang <tic@odoo.com>
This commit is contained in:
stcc-odoo
2023-08-03 10:43:23 +02:00
parent 2c11e734f8
commit 94cc077e21
2 changed files with 44 additions and 30 deletions
@@ -47,8 +47,9 @@ class StockLot(models.Model):
lot.alert_date = False
elif lot.expiration_date:
# when create
if lot.product_id != lot._origin.product_id or\
(not lot.use_date and not lot.removal_date and not lot.alert_date):
if lot.product_id != lot._origin.product_id or \
(not lot.use_date and not lot.removal_date and not lot.alert_date) or \
(lot.expiration_date and not lot._origin.expiration_date):
product_tmpl = lot.product_id.product_tmpl_id
lot.use_date = lot.expiration_date - datetime.timedelta(days=product_tmpl.use_time)
lot.removal_date = lot.expiration_date - datetime.timedelta(days=product_tmpl.removal_time)
@@ -56,9 +57,9 @@ class StockLot(models.Model):
# when change
elif lot._origin.expiration_date:
time_delta = lot.expiration_date - lot._origin.expiration_date
lot.use_date = lot.use_date and lot.use_date + time_delta
lot.removal_date = lot.removal_date and lot.removal_date + time_delta
lot.alert_date = lot.alert_date and lot.alert_date + time_delta
lot.use_date = lot._origin.use_date and lot._origin.use_date + time_delta
lot.removal_date = lot._origin.removal_date and lot._origin.removal_date + time_delta
lot.alert_date = lot._origin.alert_date and lot._origin.alert_date + time_delta
@api.model
def _alert_date_exceeded(self):
+38 -25
View File
@@ -236,6 +236,20 @@ class TestStockLot(TestStockCommon):
def test_03_onchange_expiration_date(self):
""" Updates the `expiration_date` of the lot production and checks other date
fields are updated as well. """
def check_expiration_dates(product, lot, start_date, delta):
self.assertAlmostEqual(
start_date + timedelta(days=product.expiration_time),
lot.expiration_date, delta=delta)
self.assertAlmostEqual(
lot.expiration_date - timedelta(days=product.use_time),
lot.use_date, delta=delta)
self.assertAlmostEqual(
lot.expiration_date - timedelta(days=product.removal_time),
lot.removal_date, delta=delta)
self.assertAlmostEqual(
lot.expiration_date - timedelta(days=product.alert_time),
lot.alert_date, delta=delta)
# Keeps track of the current datetime and set a delta for the compares.
today_date = datetime.today()
time_gap = timedelta(seconds=10)
@@ -246,37 +260,36 @@ class TestStockLot(TestStockCommon):
lot_form.company_id = self.env.company
apple_lot = lot_form.save()
# ...then checks date fields have the expected values.
self.assertAlmostEqual(
today_date + timedelta(days=self.apple_product.expiration_time),
apple_lot.expiration_date, delta=time_gap)
self.assertAlmostEqual(
apple_lot.expiration_date - timedelta(days=self.apple_product.use_time),
apple_lot.use_date, delta=time_gap)
self.assertAlmostEqual(
apple_lot.expiration_date - timedelta(days=self.apple_product.removal_time),
apple_lot.removal_date, delta=time_gap)
self.assertAlmostEqual(
apple_lot.expiration_date - timedelta(days=self.apple_product.alert_time),
apple_lot.alert_date, delta=time_gap)
check_expiration_dates(self.apple_product, apple_lot, today_date, time_gap)
difference = timedelta(days=20)
new_date = apple_lot.expiration_date + difference
old_use_date = apple_lot.use_date
old_removal_date = apple_lot.removal_date
old_alert_date = apple_lot.alert_date
new_expiration_date = apple_lot.expiration_date + difference
new_start_date = new_expiration_date - timedelta(days=self.apple_product.expiration_time)
random_date = new_expiration_date + difference
# Modifies the lot `expiration_date`...
# Modifies the lot `expiration_date` several times, without saving...
lot_form = Form(apple_lot)
lot_form.expiration_date = new_date
lot_form.expiration_date = new_expiration_date
lot_form.expiration_date = random_date
lot_form.expiration_date = new_expiration_date
apple_lot = lot_form.save()
# ...then checks all other date fields were correclty updated.
self.assertAlmostEqual(
apple_lot.use_date, old_use_date + difference, delta=time_gap)
self.assertAlmostEqual(
apple_lot.removal_date, old_removal_date + difference, delta=time_gap)
self.assertAlmostEqual(
apple_lot.alert_date, old_alert_date + difference, delta=time_gap)
# ...then checks all other date fields were correctly updated.
check_expiration_dates(self.apple_product, apple_lot, new_start_date, time_gap)
# Remove all dates, save, update expiration date twice, then save again
lot_form = Form(apple_lot)
lot_form.expiration_date = False
lot_form.use_date = False
lot_form.removal_date = False
lot_form.alert_date = False
lot_form.save()
lot_form.expiration_date = random_date
lot_form.expiration_date = new_expiration_date
apple_lot = lot_form.save()
# ...then check all other date fields were correctly updated.
check_expiration_dates(self.apple_product, apple_lot, new_start_date, time_gap)
def test_04_expiration_date_on_receipt(self):
""" Test we can set an expiration date on receipt and all expiration