[IMP] product_expiry: Schedule an activity on lots when expiration date is reached
This commit is contained in:
committed by
Yannick Tivisse
parent
86448be319
commit
edbf129372
@@ -4,5 +4,12 @@
|
||||
<field name="name">First Expiry First Out (FEFO)</field>
|
||||
<field name="method">fefo</field>
|
||||
</record>
|
||||
<record id="mail_activity_type_alert_date_reached" model="mail.activity.type">
|
||||
<field name="name">Alert Date Reached</field>
|
||||
<field name="category">default</field>
|
||||
<field name="res_model_id" ref="stock.model_stock_production_lot"/>
|
||||
<field name="icon">fa-tasks</field>
|
||||
<field name="days">0</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import datetime
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class StockProductionLot(models.Model):
|
||||
@@ -55,3 +55,41 @@ class StockProductionLot(models.Model):
|
||||
dates_dict = self._get_dates()
|
||||
for field, value in dates_dict.items():
|
||||
setattr(self, field, value)
|
||||
|
||||
@api.model
|
||||
def _alert_date_exceeded(self):
|
||||
# if the alert_date is in the past and the lot is linked to an internal quant
|
||||
# log a next activity on the next.production.lot
|
||||
alert_lot_ids = self.env['stock.production.lot'].search([('alert_date', '<=', fields.Date.today())])
|
||||
mail_activity_type = self.env.ref('product_expiry.mail_activity_type_alert_date_reached').id
|
||||
stock_quants = self.env['stock.quant'].search([
|
||||
('lot_id', 'in', alert_lot_ids.ids),
|
||||
('quantity', '>', 0)]).filtered(lambda quant: quant.location_id.usage == 'internal' )
|
||||
lots = stock_quants.mapped('lot_id')
|
||||
|
||||
# only for lots that do not have already an activity
|
||||
# or that do not have a done alert activity, i.e. a mail.message
|
||||
lots = lots.filtered(lambda lot:
|
||||
not self.env['mail.activity'].search_count([
|
||||
('res_model', '=', 'stock.production.lot'),
|
||||
('res_id', '=', lot.id),
|
||||
('activity_type_id','=', mail_activity_type)])
|
||||
and not self.env['mail.message'].search_count([
|
||||
('model', '=', 'stock.production.lot'),
|
||||
('res_id', '=', lot.id),
|
||||
('subtype_id', '=', self.env.ref('mail.mt_activities').id),
|
||||
('mail_activity_type_id','=', mail_activity_type)]))
|
||||
for lot in lots:
|
||||
lot.activity_schedule('product_expiry.mail_activity_type_alert_date_reached',
|
||||
user_id=lot.product_id.responsible_id.id, note=_("The alert date has been reached for this lot/serial number")
|
||||
)
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
|
||||
@api.model
|
||||
def _run_scheduler_tasks(self, use_new_cursor=False, company_id=False):
|
||||
super(ProcurementGroup, self)._run_scheduler_tasks(use_new_cursor=use_new_cursor, company_id=company_id)
|
||||
self.env['stock.production.lot']._alert_date_exceeded()
|
||||
if use_new_cursor:
|
||||
self.env.cr.commit()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_stock_production_lot
|
||||
@@ -0,0 +1,213 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import fields
|
||||
from odoo.addons.stock.tests.common import TestStockCommon
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class TestStockProductionLot(TestStockCommon):
|
||||
|
||||
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',
|
||||
'type': 'product',
|
||||
'tracking':'lot'
|
||||
})
|
||||
|
||||
# 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({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location
|
||||
})
|
||||
|
||||
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_lines.state, 'draft', 'Wrong state of move line.')
|
||||
picking_in.action_confirm()
|
||||
self.assertEqual(picking_in.move_lines.state, 'assigned', 'Wrong state of move line.')
|
||||
|
||||
# Replace pack operation of incoming shipments.
|
||||
picking_in.action_assign()
|
||||
move_a.move_line_ids.qty_done = 33
|
||||
move_a.move_line_ids.lot_id = self.lot1_productAAA.id
|
||||
|
||||
# Transfer Incoming Shipment.
|
||||
picking_in.action_done()
|
||||
|
||||
# run scheduled tasks
|
||||
self.env['stock.production.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_production_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.production.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_production_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_production_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_production_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.production.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_production_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',
|
||||
'type': 'product',
|
||||
'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({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'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_lines.state, 'draft', 'Wrong state of move line.')
|
||||
picking_in.action_confirm()
|
||||
self.assertEqual(picking_in.move_lines.state, 'assigned', 'Wrong state of move line.')
|
||||
|
||||
# Replace pack operation of incoming shipments.
|
||||
picking_in.action_assign()
|
||||
move_b.move_line_ids.qty_done = 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.production.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_production_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', 'type': 'product', '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({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'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_lines.state, 'draft', 'Wrong state of move line.')
|
||||
picking_in.action_confirm()
|
||||
self.assertEqual(picking_in.move_lines.state, 'assigned', 'Wrong state of move line.')
|
||||
|
||||
# Replace pack operation of incoming shipments.
|
||||
picking_in.action_assign()
|
||||
move_c.move_line_ids.qty_done = 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.production.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_production_lot').id),
|
||||
('res_id', '=', self.lot1_productCCC.id)
|
||||
])
|
||||
self.assertEqual(activity_count, 0, "An activity has been created while it shouldn't")
|
||||
@@ -7,7 +7,7 @@ from odoo.exceptions import UserError
|
||||
|
||||
class ProductionLot(models.Model):
|
||||
_name = 'stock.production.lot'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread','mail.activity.mixin']
|
||||
_description = 'Lot/Serial'
|
||||
|
||||
name = fields.Char(
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -61,6 +62,11 @@
|
||||
<field name="ref"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user