[REF] purchase_stock: rescheduling propagation

Implement the propagation minimum delta per rule in purchase_stock. Override
properly the write to handle both RFQ and PO. Take care of not merging po lines
where the expecte date should be different

Add propre test cases.

task-1913401
This commit is contained in:
Haresh Shyara
2019-05-29 13:05:19 +00:00
committed by William Henrotin
parent c514e8ae6a
commit 77b283f307
4 changed files with 220 additions and 10 deletions
+18 -7
View File
@@ -3,7 +3,7 @@
from odoo import api, fields, models, _
from odoo.tools.float_utils import float_compare
from dateutil import relativedelta
from odoo.exceptions import UserError
from odoo.addons.purchase.models.purchase import PurchaseOrder as Purchase
@@ -227,6 +227,8 @@ class PurchaseOrderLine(models.Model):
move_ids = fields.One2many('stock.move', 'purchase_line_id', string='Reservation', readonly=True, ondelete='set null', copy=False)
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Orderpoint')
move_dest_ids = fields.One2many('stock.move', 'created_purchase_line_id', 'Downstream Moves')
propagate_date = fields.Boolean(string="Propagate Rescheduling", help='The rescheduling is propagated to the next move.')
propagate_date_minimum_delta = fields.Integer(string='Reschedule if Higher Than', help='The change must be higher than this value to be propagated')
@api.multi
def _compute_qty_received_method(self):
@@ -266,12 +268,18 @@ class PurchaseOrderLine(models.Model):
@api.multi
def write(self, values):
for line in self:
if values.get('date_planned') and line.propagate_date:
new_date = values['date_planned']
delta_days = (new_date - line.date_planned).total_seconds() / 86400
if abs(delta_days) < line.propagate_date_minimum_delta:
continue
moves_to_update = line.move_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
if not moves_to_update:
moves_to_update = line.move_dest_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
for move in moves_to_update:
move.date_expected = move.date_expected + relativedelta.relativedelta(days=delta_days)
result = super(PurchaseOrderLine, self).write(values)
# Update expected date of corresponding moves
if 'date_planned' in values:
self.env['stock.move'].search([
('purchase_line_id', 'in', self.ids), ('state', '!=', 'done')
]).write({'date_expected': values['date_planned']})
if 'product_qty' in values:
self.filtered(lambda l: l.order_id.state == 'purchase')._create_or_update_picking()
return result
@@ -361,6 +369,8 @@ class PurchaseOrderLine(models.Model):
'picking_type_id': self.order_id.picking_type_id.id,
'group_id': self.order_id.group_id.id,
'origin': self.order_id.name,
'propagate_date': self.propagate_date,
'propagate_date_minimum_delta': self.propagate_date_minimum_delta,
'description_picking': self.product_id._get_description(self.order_id.picking_type_id),
'route_ids': self.order_id.picking_type_id.warehouse_id and [(6, 0, [x.id for x in self.order_id.picking_type_id.warehouse_id.route_ids])] or [],
'warehouse_id': self.order_id.picking_type_id.warehouse_id.id,
@@ -388,4 +398,5 @@ class PurchaseOrderLine(models.Model):
args can be merged. If it returns an empty record then a new line will
be created.
"""
return self and self[0] or self.env['purchase.order.line']
lines = self.filtered(lambda l: l.propagate_date == values['propagate_date'] and l.propagate_date_minimum_delta == values['propagate_date_minimum_delta'])
return lines and lines[0] or self.env['purchase.order.line']
+6 -2
View File
@@ -51,6 +51,8 @@ class StockRule(models.Model):
partner = supplier.name
# we put `supplier_info` in values for extensibility purposes
procurement.values['supplier'] = supplier
procurement.values['propagate_date'] = rule.propagate_date
procurement.values['propagate_date_minimum_delta'] = rule.propagate_date_minimum_delta
domain = rule._make_po_get_domain(procurement.company_id, procurement.values, partner)
procurements_by_po_domain[domain].append((procurement, rule))
@@ -115,11 +117,11 @@ class StockRule(models.Model):
@api.model
def _get_procurements_to_merge_groupby(self, procurement):
return procurement.product_id, procurement.product_uom
return procurement.product_id, procurement.product_uom, procurement.values['propagate_date'], procurement.values['propagate_date_minimum_delta']
@api.model
def _get_procurements_to_merge_sorted(self, procurement):
return procurement.product_id.id, procurement.product_uom.id
return procurement.product_id.id, procurement.product_uom.id, procurement.values['propagate_date'], procurement.values['propagate_date_minimum_delta']
@api.model
def _get_procurements_to_merge(self, procurements):
@@ -231,6 +233,8 @@ class StockRule(models.Model):
'product_uom': product_id.uom_po_id.id,
'price_unit': price_unit,
'date_planned': date_planned,
'propagate_date': values['propagate_date'],
'propagate_date_minimum_delta': values['propagate_date_minimum_delta'],
'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
'taxes_id': [(6, 0, taxes_id.ids)],
'order_id': po.id,
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from datetime import datetime, timedelta
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.addons.product.tests import common
@@ -313,3 +313,95 @@ class TestCreatePicking(common.TestProductCommon):
purchase_order_2.picking_ids.button_validate()
self.assertEqual(sum(customer_picking.move_lines.mapped('reserved_availability')), 100.0, 'The total quantity for the customer move should be available and reserved.')
def create_delivery_order(self, propagate_date, propagate_date_minimum_delta):
stock_location = self.env['ir.model.data'].xmlid_to_object('stock.stock_location_stock')
customer_location = self.env['ir.model.data'].xmlid_to_object('stock.stock_location_customers')
unit = self.ref("uom.product_uom_unit")
picking_type_out = self.env['ir.model.data'].xmlid_to_object('stock.picking_type_out')
partner = self.env['res.partner'].create({'name': 'AAA', 'email': 'from.test@example.com'})
supplier_info1 = self.env['product.supplierinfo'].create({
'name': partner.id,
'price': 50,
})
warehouse1 = self.env.ref('stock.warehouse0')
route_buy = warehouse1.buy_pull_id.route_id
route_mto = warehouse1.mto_pull_id.route_id
# change propagete date and Reschedule if Higher Than on rules
route_buy.rule_ids.write({'propagate_date': propagate_date,
'propagate_date_minimum_delta': propagate_date_minimum_delta})
product = self.env['product.product'].create({
'name': 'Usb Keyboard',
'type': 'product',
'uom_id': unit,
'uom_po_id': unit,
'seller_ids': [(6, 0, [supplier_info1.id])],
'route_ids': [(6, 0, [route_buy.id, route_mto.id])]
})
delivery_order = self.env['stock.picking'].create({
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
'partner_id': partner.id,
'picking_type_id': picking_type_out.id,
})
customer_move = self.env['stock.move'].create({
'name': 'move out',
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
'product_id': product.id,
'product_uom': product.uom_id.id,
'product_uom_qty': 10.0,
'procure_method': 'make_to_order',
'picking_id': delivery_order.id,
})
customer_move._action_confirm()
# find created po the product
purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)])
return delivery_order, purchase_order
def test_05_if_propagate_date(self):
""" In order to check scheduled date of the delivery order is changed based
stock rules with propagate date and minimum delta.
"""
# Create Delivery Order and with propagate date and minimum delta
delivery_order, purchase_order = self.create_delivery_order(True, 5)
# check po is created or not
self.assertTrue(purchase_order, 'No purchase order created.')
purchase_order_line = purchase_order.order_line
# change scheduled date of po line.
purchase_order_line.write({'date_planned': purchase_order_line.date_planned + timedelta(days=5)})
# Now check scheduled date of delivery order is changed or not.
self.assertEquals(purchase_order_line.date_planned, delivery_order.scheduled_date,
'Delivery order schedule date should be changed as we have set date propagate.')
def test_06_no_propagate_date(self):
""" In order to check scheduled date of the delivery order is changed based
stock rules without propagate date and minimum delta.
"""
# Create Delivery Order and without propagate date and minimum delta
delivery_order, purchase_order = self.create_delivery_order(False, 5)
# check po is created or not
self.assertTrue(purchase_order, 'No purchase order created.')
purchase_order_line = purchase_order.order_line
# change scheduled date of po line.
purchase_order_line.write({'date_planned': purchase_order_line.date_planned + timedelta(days=5)})
# Now check scheduled date of delivery order is changed or not.
self.assertNotEquals(purchase_order_line.date_planned, delivery_order.scheduled_date,
'Delivery order schedule date should not changed.')
@@ -136,3 +136,106 @@ class TestPurchaseLeadTime(TestPurchase):
incoming_shipment2 = self.env['stock.picking'].search([('picking_type_id', '=', self.warehouse_1.int_type_id.id), ('location_id', '=', self.warehouse_1.wh_qc_stock_loc_id.id), ('location_dest_id', '=', self.warehouse_1.lot_stock_id.id)])
incoming_shipment2_date = schedule_date - timedelta(days=incoming_shipment2.move_lines[0].rule_id.delay)
self.assertEqual(incoming_shipment2.scheduled_date, incoming_shipment2_date, 'Schedule date of Internal Type shipment for quality control stock location should be equal to: schedule date of Internal type shipment for input stock location + push rule delay..')
def test_merge_po_line(self):
"""Chage that merging po line for same procurement is done depending on
propagate_date and propagate_date_minimum_delta"""
# create a product with manufacture route
product_1 = self.env['product.product'].create({
'name': 'AAA',
'route_ids': [(4, self.route_buy)],
'seller_ids': [(0, 0, {'name': self.partner_1.id, 'delay': 5})]
})
# create a move for product_1 from stock to output and reserve to trigger the
# rule
move_1 = self.env['stock.move'].create({
'name': 'move_1',
'product_id': product_1.id,
'product_uom': self.ref('uom.product_uom_unit'),
'propagate_date': True,
'propagate_date_minimum_delta': 1,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 10,
'procure_method': 'make_to_order'
})
move_1._action_confirm()
po_line = self.env['purchase.order.line'].search([
('product_id', '=', product_1.id),
])
self.assertEqual(len(po_line), 1, 'the purchase order line is not created')
self.assertEqual(po_line.product_qty, 10, 'the purchase order line has a wrong quantity')
move_2 = self.env['stock.move'].create({
'name': 'move_2',
'product_id': product_1.id,
'product_uom': self.ref('uom.product_uom_unit'),
'propagate_date': True,
'propagate_date_minimum_delta': 1,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 5,
'procure_method': 'make_to_order'
})
move_2._action_confirm()
po_line = self.env['purchase.order.line'].search([
('product_id', '=', product_1.id),
])
self.assertEqual(len(po_line), 1, 'the purchase order lines should be merged')
self.assertEqual(po_line.product_qty, 15, 'the purchase order line has a wrong quantity')
def test_merge_po_line_2(self):
"""Chage that merging po line for same procurement is done depending on
propagate_date and propagate_date_minimum_delta"""
# create a product with manufacture route
product_1 = self.env['product.product'].create({
'name': 'AAA',
'route_ids': [(4, self.route_buy)],
'seller_ids': [(0, 0, {'name': self.partner_1.id, 'delay': 5})]
})
# create a move for product_1 from stock to output and reserve to trigger the
# rule
move_1 = self.env['stock.move'].create({
'name': 'move_1',
'product_id': product_1.id,
'product_uom': self.ref('uom.product_uom_unit'),
'propagate_date': True,
'propagate_date_minimum_delta': 1,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 10,
'procure_method': 'make_to_order'
})
move_1._action_confirm()
po_line = self.env['purchase.order.line'].search([
('product_id', '=', product_1.id),
])
self.assertEqual(len(po_line), 1, 'the purchase order line is not created')
self.assertEqual(po_line.product_qty, 10, 'the purchase order line has a wrong quantity')
po_line.propagate_date = not po_line.propagate_date
move_2 = self.env['stock.move'].create({
'name': 'move_2',
'product_id': product_1.id,
'product_uom': self.ref('uom.product_uom_unit'),
'propagate_date': True,
'propagate_date_minimum_delta': 1,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 5,
'procure_method': 'make_to_order'
})
move_2._action_confirm()
po_line = self.env['purchase.order.line'].search([
('product_id', '=', product_1.id),
])
self.assertEqual(len(po_line), 2, 'the purchase order lines are not merged')
self.assertEqual(po_line[0].product_qty, 10, 'the purchase order line has a wrong quantity')
self.assertEqual(po_line[1].product_qty, 5, 'the purchase order line has a wrong quantity')