678 lines
29 KiB
Python
678 lines
29 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import datetime, timedelta
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import fields
|
|
from odoo.addons.mail.tests.common import mail_new_test_user
|
|
from odoo.addons.stock.tests.common import TestStockCommon
|
|
from odoo.tests import Form
|
|
|
|
|
|
class TestStockLot(TestStockCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
# Creates a tracked product with expiration dates.
|
|
cls.apple_product = cls.ProductObj.create({
|
|
'name': 'Apple',
|
|
'is_storable': True,
|
|
'tracking': 'lot',
|
|
'use_expiration_date': True,
|
|
'expiration_time': 10,
|
|
'use_time': 5,
|
|
'removal_time': 2,
|
|
'alert_time': 6,
|
|
})
|
|
|
|
def test_00_stock_production_lot(self):
|
|
""" Test Scheduled Task on lot with an alert_date in the past creates an activity """
|
|
|
|
# create product
|
|
self.productAAA = self.ProductObj.create({
|
|
'name': 'Product AAA',
|
|
'is_storable': True,
|
|
'tracking':'lot',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
|
|
# create a new lot with with alert date in the past
|
|
self.lot1_productAAA = self.LotObj.create({
|
|
'name': 'Lot 1 ProductAAA',
|
|
'product_id': self.productAAA.id,
|
|
'alert_date': fields.Date.to_string(datetime.today() - relativedelta(days=15)),
|
|
})
|
|
|
|
picking_in = self.PickingObj.create({
|
|
'picking_type_id': self.picking_type_in,
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location,
|
|
'state': 'draft',
|
|
})
|
|
|
|
move_a = self.MoveObj.create({
|
|
'name': self.productAAA.name,
|
|
'product_id': self.productAAA.id,
|
|
'product_uom_qty': 33,
|
|
'product_uom': self.productAAA.uom_id.id,
|
|
'picking_id': picking_in.id,
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location
|
|
})
|
|
|
|
self.assertEqual(picking_in.move_ids.state, 'draft', 'Wrong state of move line.')
|
|
picking_in.action_confirm()
|
|
self.assertEqual(picking_in.move_ids.state, 'assigned', 'Wrong state of move line.')
|
|
|
|
# Replace pack operation of incoming shipments.
|
|
picking_in.action_assign()
|
|
move_a.move_line_ids.quantity = 33
|
|
move_a.move_line_ids.lot_id = self.lot1_productAAA.id
|
|
|
|
# Transfer Incoming Shipment.
|
|
move_a.picked = True
|
|
picking_in._action_done()
|
|
|
|
# run scheduled tasks
|
|
self.env['stock.lot']._alert_date_exceeded()
|
|
|
|
# check a new activity has been created
|
|
activity_id = self.env.ref('product_expiry.mail_activity_type_alert_date_reached').id
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productAAA.id)
|
|
])
|
|
self.assertEqual(activity_count, 1, 'No activity created while there should be one')
|
|
|
|
# run the scheduler a second time
|
|
self.env['stock.lot']._alert_date_exceeded()
|
|
|
|
# check there is still only one activity, no additional activity is created if there is already an existing activity
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productAAA.id)
|
|
])
|
|
self.assertEqual(activity_count, 1, 'There should be one and only one activity')
|
|
|
|
# mark the activity as done
|
|
mail_activity = self.env['mail.activity'].search([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productAAA.id)
|
|
])
|
|
mail_activity.action_done()
|
|
|
|
# check there is no more activity (because it is already done)
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productAAA.id)
|
|
])
|
|
self.assertEqual(activity_count, 0,"As activity is done, there shouldn't be any related activity")
|
|
|
|
# run the scheduler a third time
|
|
self.env['stock.lot']._alert_date_exceeded()
|
|
|
|
# check there is no activity created
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=',self.lot1_productAAA.id)
|
|
])
|
|
self.assertEqual(activity_count, 0, "As there is already an activity marked as done, there shouldn't be any related activity created for this lot")
|
|
|
|
def test_01_stock_production_lot(self):
|
|
""" Test Scheduled Task on lot with an alert_date in future does not create an activity """
|
|
|
|
# create product
|
|
self.productBBB = self.ProductObj.create({
|
|
'name': 'Product BBB',
|
|
'is_storable': True,
|
|
'tracking':'lot'
|
|
})
|
|
|
|
# create a new lot with with alert date in the past
|
|
self.lot1_productBBB = self.LotObj.create({
|
|
'name': 'Lot 1 ProductBBB',
|
|
'product_id': self.productBBB.id,
|
|
'alert_date': fields.Date.to_string(datetime.today() + relativedelta(days=15)),
|
|
})
|
|
|
|
picking_in = self.PickingObj.create({
|
|
'picking_type_id': self.picking_type_in,
|
|
'location_id': self.supplier_location,
|
|
'state': 'draft',
|
|
'location_dest_id': self.stock_location})
|
|
|
|
move_b = self.MoveObj.create({
|
|
'name': self.productBBB.name,
|
|
'product_id': self.productBBB.id,
|
|
'product_uom_qty': 44,
|
|
'product_uom': self.productBBB.uom_id.id,
|
|
'picking_id': picking_in.id,
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location})
|
|
|
|
self.assertEqual(picking_in.move_ids.state, 'draft', 'Wrong state of move line.')
|
|
picking_in.action_confirm()
|
|
self.assertEqual(picking_in.move_ids.state, 'assigned', 'Wrong state of move line.')
|
|
|
|
# Replace pack operation of incoming shipments.
|
|
picking_in.action_assign()
|
|
move_b.move_line_ids.quantity = 44
|
|
move_b.move_line_ids.lot_id = self.lot1_productBBB.id
|
|
|
|
# Transfer Incoming Shipment.
|
|
picking_in._action_done()
|
|
|
|
# run scheduled tasks
|
|
self.env['stock.lot']._alert_date_exceeded()
|
|
|
|
# check a new activity has not been created
|
|
activity_id = self.env.ref('product_expiry.mail_activity_type_alert_date_reached').id
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productBBB.id)
|
|
])
|
|
self.assertEqual(activity_count, 0, "An activity has been created while it shouldn't")
|
|
|
|
def test_02_stock_production_lot(self):
|
|
""" Test Scheduled Task on lot without an alert_date does not create an activity """
|
|
|
|
# create product
|
|
self.productCCC = self.ProductObj.create({'name': 'Product CCC', 'is_storable': True, 'tracking': 'lot'})
|
|
|
|
# create a new lot with with alert date in the past
|
|
self.lot1_productCCC = self.LotObj.create({'name': 'Lot 1 ProductCCC', 'product_id': self.productCCC.id})
|
|
|
|
picking_in = self.PickingObj.create({
|
|
'picking_type_id': self.picking_type_in,
|
|
'location_id': self.supplier_location,
|
|
'state': 'draft',
|
|
'location_dest_id': self.stock_location})
|
|
|
|
move_c = self.MoveObj.create({
|
|
'name': self.productCCC.name,
|
|
'product_id': self.productCCC.id,
|
|
'product_uom_qty': 44,
|
|
'product_uom': self.productCCC.uom_id.id,
|
|
'picking_id': picking_in.id,
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location})
|
|
|
|
self.assertEqual(picking_in.move_ids.state, 'draft', 'Wrong state of move line.')
|
|
picking_in.action_confirm()
|
|
self.assertEqual(picking_in.move_ids.state, 'assigned', 'Wrong state of move line.')
|
|
|
|
# Replace pack operation of incoming shipments.
|
|
picking_in.action_assign()
|
|
move_c.move_line_ids.quantity = 55
|
|
move_c.move_line_ids.lot_id = self.lot1_productCCC.id
|
|
|
|
# Transfer Incoming Shipment.
|
|
picking_in._action_done()
|
|
|
|
# run scheduled tasks
|
|
self.env['stock.lot']._alert_date_exceeded()
|
|
|
|
# check a new activity has not been created
|
|
activity_id = self.env.ref('product_expiry.mail_activity_type_alert_date_reached').id
|
|
activity_count = self.env['mail.activity'].search_count([
|
|
('activity_type_id', '=', activity_id),
|
|
('res_model_id', '=', self.env.ref('stock.model_stock_lot').id),
|
|
('res_id', '=', self.lot1_productCCC.id)
|
|
])
|
|
self.assertEqual(activity_count, 0, "An activity has been created while it shouldn't")
|
|
|
|
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)
|
|
# Creates a new lot number and saves it...
|
|
lot_form = Form(self.LotObj)
|
|
lot_form.name = 'Apple Box #1'
|
|
lot_form.product_id = self.apple_product
|
|
apple_lot = lot_form.save()
|
|
# ...then checks date fields have the expected values.
|
|
check_expiration_dates(self.apple_product, apple_lot, today_date, time_gap)
|
|
|
|
difference = timedelta(days=20)
|
|
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` several times, without saving...
|
|
lot_form = Form(apple_lot)
|
|
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 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
|
|
date will be correctly set. """
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Apple\'s Joe',
|
|
'company_id': self.env.ref('base.main_company').id,
|
|
})
|
|
expiration_date = datetime.today() + timedelta(days=30)
|
|
time_gap = timedelta(seconds=10)
|
|
|
|
# Receives a tracked production using expiration date.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 4
|
|
receipt = picking_form.save()
|
|
receipt.action_confirm()
|
|
|
|
# Defines a date during the receipt.
|
|
move_form = Form(receipt.move_ids_without_package, view="stock.view_stock_move_operations")
|
|
with move_form.move_line_ids.edit(0) as line:
|
|
line.lot_name = 'Apple Box #2'
|
|
line.expiration_date = expiration_date
|
|
move = move_form.save()
|
|
|
|
move.picked = True
|
|
receipt._action_done()
|
|
# Get back the lot created when the picking was done...
|
|
apple_lot = self.env['stock.lot'].search(
|
|
[('product_id', '=', self.apple_product.id)],
|
|
limit=1,
|
|
)
|
|
# ... and checks all date fields are correctly set.
|
|
self.assertAlmostEqual(
|
|
apple_lot.expiration_date, expiration_date, delta=time_gap)
|
|
self.assertAlmostEqual(
|
|
apple_lot.use_date, expiration_date - timedelta(days=self.apple_product.use_time), delta=time_gap)
|
|
self.assertAlmostEqual(
|
|
apple_lot.removal_date, expiration_date - timedelta(days=self.apple_product.removal_time), delta=time_gap)
|
|
self.assertAlmostEqual(
|
|
apple_lot.alert_date, expiration_date - timedelta(days=self.apple_product.alert_time), delta=time_gap)
|
|
|
|
def test_04_2_expiration_date_on_receipt(self):
|
|
""" Test we can set an expiration date on receipt even if all expiration
|
|
date related fields aren't set on product. """
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Apple\'s Joe',
|
|
'company_id': self.env.ref('base.main_company').id,
|
|
})
|
|
# Unset some fields.
|
|
self.apple_product.expiration_time = False
|
|
self.apple_product.removal_time = False
|
|
|
|
expiration_date = datetime.today() + timedelta(days=30)
|
|
time_gap = timedelta(seconds=10)
|
|
|
|
# Receives a tracked production using expiration date.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.quantity = 4
|
|
move.picked = True
|
|
receipt = picking_form.save()
|
|
|
|
# Defines a date during the receipt.
|
|
move = receipt.move_ids_without_package[0]
|
|
line = move.move_line_ids[0]
|
|
self.assertEqual(move.use_expiration_date, True)
|
|
line.lot_name = 'Apple Box #3'
|
|
line.expiration_date = expiration_date
|
|
|
|
receipt._action_done()
|
|
# Get back the lot created when the picking was done...
|
|
apple_lot = self.env['stock.lot'].search(
|
|
[('product_id', '=', self.apple_product.id)],
|
|
limit=1,
|
|
)
|
|
# ... and checks all date fields are correctly set.
|
|
self.assertAlmostEqual(
|
|
apple_lot.expiration_date, expiration_date, delta=time_gap,
|
|
msg="Must be define even if the product's `expiration_time` isn't set.")
|
|
self.assertAlmostEqual(
|
|
apple_lot.use_date, expiration_date - timedelta(days=self.apple_product.use_time), delta=time_gap)
|
|
self.assertEqual(
|
|
apple_lot.removal_date, expiration_date,
|
|
"Must same as expiration_date as the `removal_time` isn't set on product.")
|
|
self.assertAlmostEqual(
|
|
apple_lot.alert_date, expiration_date - timedelta(days=self.apple_product.alert_time), delta=time_gap)
|
|
|
|
def test_05_confirmation_on_delivery(self):
|
|
""" Test when user tries to delivery expired lot, he/she gets a
|
|
confirmation wizard. """
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Cider & Son',
|
|
'company_id': self.env.ref('base.main_company').id,
|
|
})
|
|
# Creates 3 lots (1 non-expired lot, 2 expired lots)
|
|
lot_form = Form(self.LotObj) # Creates the lot.
|
|
lot_form.name = 'good-apple-lot'
|
|
lot_form.product_id = self.apple_product
|
|
good_lot = lot_form.save()
|
|
|
|
lot_form = Form(self.LotObj) # Creates the lot.
|
|
lot_form.name = 'expired-apple-lot-01'
|
|
lot_form.product_id = self.apple_product
|
|
expired_lot_1 = lot_form.save()
|
|
lot_form = Form(expired_lot_1) # Edits the lot to make it expired.
|
|
lot_form.expiration_date = datetime.today() - timedelta(days=10)
|
|
expired_lot_1 = lot_form.save()
|
|
|
|
# Case #1: make a delivery with no expired lot.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_out')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 4
|
|
# Saves and confirms it...
|
|
delivery_1 = picking_form.save()
|
|
delivery_1.action_confirm()
|
|
# ... then create a move line with the non-expired lot and valids the picking.
|
|
delivery_1.move_line_ids_without_package = [(5, 0), (0, 0, {
|
|
'company_id': self.env.company.id,
|
|
'location_id': delivery_1.move_ids.location_id.id,
|
|
'location_dest_id': delivery_1.move_ids.location_dest_id.id,
|
|
'lot_id': good_lot.id,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom_id': self.apple_product.uom_id.id,
|
|
'quantity': 4,
|
|
})]
|
|
delivery_1.move_ids.picked = True
|
|
res = delivery_1.button_validate()
|
|
# Validate a delivery for good products must not raise anything.
|
|
self.assertEqual(res, True)
|
|
|
|
# Case #2: make a delivery with one non-expired lot and one expired lot.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_out')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 8
|
|
# Saves and confirms it...
|
|
delivery_2 = picking_form.save()
|
|
delivery_2.action_confirm()
|
|
# ... then create a move line for the non-expired lot and for an expired
|
|
# lot and valids the picking.
|
|
delivery_2.move_line_ids_without_package = [(5, 0), (0, 0, {
|
|
'company_id': self.env.company.id,
|
|
'location_id': delivery_2.move_ids.location_id.id,
|
|
'location_dest_id': delivery_2.move_ids.location_dest_id.id,
|
|
'lot_id': good_lot.id,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom_id': self.apple_product.uom_id.id,
|
|
'quantity': 4,
|
|
}), (0, 0, {
|
|
'company_id': self.env.company.id,
|
|
'location_id': delivery_2.move_ids.location_id.id,
|
|
'location_dest_id': delivery_2.move_ids.location_dest_id.id,
|
|
'lot_id': expired_lot_1.id,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom_id': self.apple_product.uom_id.id,
|
|
'quantity': 4,
|
|
})]
|
|
delivery_2.move_ids.picked = True
|
|
res = delivery_2.button_validate()
|
|
# Validate a delivery containing expired products must raise a confirmation wizard.
|
|
self.assertNotEqual(res, True)
|
|
self.assertEqual(res['res_model'], 'expiry.picking.confirmation')
|
|
|
|
# Case #3: make a delivery with only on expired lot.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_out')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 4
|
|
# Saves and confirms it...
|
|
delivery_3 = picking_form.save()
|
|
delivery_3.action_confirm()
|
|
# ... then create two move lines with expired lot and valids the picking.
|
|
delivery_3.move_line_ids_without_package = [(5, 0), (0, 0, {
|
|
'company_id': self.env.company.id,
|
|
'location_id': delivery_3.move_ids.location_id.id,
|
|
'location_dest_id': delivery_3.move_ids.location_dest_id.id,
|
|
'lot_id': expired_lot_1.id,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom_id': self.apple_product.uom_id.id,
|
|
'quantity': 4,
|
|
})]
|
|
delivery_3.move_ids.picked = True
|
|
res = delivery_3.button_validate()
|
|
# Validate a delivery containing expired products must raise a confirmation wizard.
|
|
self.assertNotEqual(res, True)
|
|
self.assertEqual(res['res_model'], 'expiry.picking.confirmation')
|
|
|
|
def test_edit_removal_date_in_inventory_mode(self):
|
|
""" Try to edit removal_date with the inventory mode.
|
|
"""
|
|
user_group_stock_manager = self.env.ref('stock.group_stock_manager')
|
|
self.demo_user = mail_new_test_user(
|
|
self.env,
|
|
name='Demo user',
|
|
login='userdemo',
|
|
email='d.d@example.com',
|
|
groups='stock.group_stock_manager',
|
|
)
|
|
lot_form = Form(self.LotObj)
|
|
lot_form.name = 'LOT001'
|
|
lot_form.product_id = self.apple_product
|
|
apple_lot = lot_form.save()
|
|
|
|
quant = self.StockQuantObj.with_context(inventory_mode=True).create({
|
|
'product_id': self.apple_product.id,
|
|
'location_id': self.stock_location,
|
|
'quantity': 10,
|
|
'lot_id': apple_lot.id,
|
|
})
|
|
# Try to write on quant with inventory mode
|
|
new_date = datetime.today() + timedelta(days=15)
|
|
quant.with_user(self.demo_user).with_context(inventory_mode=True).write({'removal_date': new_date})
|
|
self.assertEqual(quant.removal_date, new_date)
|
|
|
|
def test_apply_lot_date_on_sml(self):
|
|
"""
|
|
When assigning a lot to a SML, if the lot has an expiration date,
|
|
the latter should be applied on the SML
|
|
"""
|
|
exp_date = fields.Datetime.today() + relativedelta(days=15)
|
|
sml_exp_date = fields.Datetime.today() + relativedelta(days=10)
|
|
|
|
lot = self.env['stock.lot'].create({
|
|
'name': 'Lot 1',
|
|
'product_id': self.apple_product.id,
|
|
'expiration_date': fields.Datetime.to_string(exp_date),
|
|
})
|
|
|
|
move = self.env['stock.move'].create({
|
|
'name': 'move_test',
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom': self.apple_product.uom_id.id,
|
|
})
|
|
sml = self.env['stock.move.line'].create({
|
|
'location_id': self.supplier_location,
|
|
'location_dest_id': self.stock_location,
|
|
'product_id': self.apple_product.id,
|
|
'quantity': 3,
|
|
'product_uom_id': self.apple_product.uom_id.id,
|
|
'expiration_date': fields.Datetime.to_string(sml_exp_date),
|
|
'company_id': self.env.company.id,
|
|
'move_id': move.id,
|
|
})
|
|
self.assertEqual(sml.expiration_date, sml_exp_date)
|
|
|
|
sml.lot_id = lot
|
|
self.assertEqual(sml.expiration_date, exp_date)
|
|
|
|
def test_apply_same_date_on_expiry_fields(self):
|
|
expiration_time = 10
|
|
self.apple_product.write({
|
|
'expiration_time': expiration_time,
|
|
'use_time': 0,
|
|
'removal_time': 0,
|
|
'alert_time': 0,
|
|
})
|
|
|
|
lot = self.env['stock.lot'].create({
|
|
'product_id': self.apple_product.id,
|
|
})
|
|
|
|
delta = timedelta(seconds=10)
|
|
expiration_date = datetime.today() + timedelta(days=expiration_time)
|
|
err_msg = "The time on the product is set to 0, it means that the corresponding date should be the same as the expiration one"
|
|
self.assertAlmostEqual(lot.expiration_date, expiration_date, delta=delta)
|
|
self.assertAlmostEqual(lot.use_date, expiration_date, delta=delta, msg=err_msg)
|
|
self.assertAlmostEqual(lot.removal_date, expiration_date, delta=delta, msg=err_msg)
|
|
self.assertAlmostEqual(lot.alert_date, expiration_date, delta=delta, msg=err_msg)
|
|
|
|
def test_no_expiration_date(self):
|
|
"""
|
|
When use_expiration_date is set to True on the Product, but the lot have an expiration_date set to False,
|
|
the picking should be able to reserve on it because it is considered as 'non-perishable'
|
|
"""
|
|
lot_form = Form(self.LotObj)
|
|
lot_form.name = 'LOT001'
|
|
lot_form.product_id = self.apple_product
|
|
apple_lot = lot_form.save()
|
|
|
|
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
|
|
apple_lot = lot_form.save()
|
|
|
|
self.StockQuantObj.with_context(inventory_mode=True).create({
|
|
'product_id': self.apple_product.id,
|
|
'location_id': self.stock_location,
|
|
'quantity': 100,
|
|
'lot_id': apple_lot.id,
|
|
})
|
|
|
|
self.assertEqual(self.apple_product.qty_available, 100, 'Wrong quantity.')
|
|
|
|
picking_out = self.PickingObj.create({
|
|
'picking_type_id': self.picking_type_out,
|
|
'location_id': self.stock_location,
|
|
'location_dest_id': self.customer_location,
|
|
'state': 'draft',
|
|
})
|
|
|
|
self.MoveObj.create({
|
|
'name': self.apple_product.name,
|
|
'product_id': self.apple_product.id,
|
|
'product_uom_qty': 10,
|
|
'product_uom': self.apple_product.uom_id.id,
|
|
'picking_id': picking_out.id,
|
|
'location_id': self.stock_location,
|
|
'location_dest_id': self.customer_location
|
|
})
|
|
|
|
self.assertEqual(picking_out.move_ids.state, 'draft', 'Wrong state of move line.')
|
|
picking_out.action_confirm()
|
|
picking_out.action_assign()
|
|
self.assertEqual(picking_out.move_ids.state, 'assigned', 'Wrong state of move line.')
|
|
|
|
def test_no_lot(self):
|
|
"""
|
|
Try to reserve a move that for an expirable product that has both quants with and without lot attached.
|
|
"""
|
|
# Set the removal strategy to 'First Expiry First Out'
|
|
fefo_strategy = self.env['product.removal'].search(
|
|
[('method', '=', 'fefo')])
|
|
self.apple_product.categ_id.removal_strategy_id = fefo_strategy.id
|
|
|
|
apple_lot = self.LotObj.create({
|
|
'name': 'LOT001',
|
|
'product_id': self.apple_product.id,
|
|
})
|
|
|
|
self.StockQuantObj.with_context(inventory_mode=True).create([{
|
|
'product_id': self.apple_product.id,
|
|
'location_id': self.stock_location,
|
|
'quantity': 100,
|
|
}, {
|
|
'product_id': self.apple_product.id,
|
|
'location_id': self.stock_location,
|
|
'quantity': 100,
|
|
'lot_id': apple_lot.id,
|
|
}])
|
|
|
|
with Form(self.PickingObj) as picking_form:
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_out')
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 10
|
|
picking_out = picking_form.save()
|
|
|
|
picking_out.action_assign()
|
|
self.assertEqual(picking_out.move_line_ids.lot_id, apple_lot)
|
|
|
|
def test_compute_expiration_date_from_scheduled_date(self):
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Apple\'s Joe',
|
|
'company_id': self.env.ref('base.main_company').id,
|
|
})
|
|
|
|
delta = timedelta(seconds=10)
|
|
new_date = datetime.today() + timedelta(days=42)
|
|
expiration_date = new_date + timedelta(days=self.apple_product.expiration_time)
|
|
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.partner_id = partner
|
|
picking_form.scheduled_date = new_date
|
|
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
|
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.apple_product
|
|
move.product_uom_qty = 4
|
|
delivery = picking_form.save()
|
|
delivery.action_confirm()
|
|
|
|
self.assertAlmostEqual(delivery.move_line_ids[0].expiration_date, expiration_date, delta=delta)
|