Skip to content

Commit

Permalink
[IMP] ddmrp: Add 'total_outside_dlt_qty' to split incoming moves than…
Browse files Browse the repository at this point in the history
… rfqs.

Maintain two buttons to alert for supplies out of DLT.
The first one shows 'Stock Pickings' and the second one shows 'RFQs'.
  • Loading branch information
BernatPForgeFlow committed Sep 15, 2023
1 parent 16e18ff commit 199557c
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 14 deletions.
42 changes: 37 additions & 5 deletions ddmrp/models/stock_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,13 @@ def _compute_order_spike_threshold(self):
for rec in self:
rec.order_spike_threshold = 0.5 * rec.red_zone_qty

@api.depends("incoming_outside_dlt_qty", "rfq_outside_dlt_qty")
def _compute_total_incoming_outside_dlt_qty(self):
for rec in self:
rec.total_incoming_outside_dlt_qty = (

Check warning on line 930 in ddmrp/models/stock_buffer.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L930

Added line #L930 was not covered by tests
rec.incoming_outside_dlt_qty + rec.rfq_outside_dlt_qty
)

def _get_manufactured_bom(self, limit=1):
locations = self.env["stock.location"].search(
[("id", "child_of", [self.location_id.id])]
Expand Down Expand Up @@ -1168,12 +1175,21 @@ def _compute_product_vendor_code(self):
string="Incoming (Outside DLT)",
readonly=True,
)
rfq_inside_dlt_qty = fields.Float(
string="RFQ Qty (Inside DLT)",
readonly=True,
help="Request for Quotation total quantity that is planned inside of "
"the DLT horizon.",
)
rfq_outside_dlt_qty = fields.Float(
string="RFQ Qty (Outside DLT)",
readonly=True,
help="Request for Quotation total quantity that is planned outside of "
"the DLT horizon.",
)
total_incoming_outside_dlt_qty = fields.Float(
compute="_compute_total_incoming_outside_dlt_qty"
)
net_flow_position = fields.Float(
string="Net flow position",
digits="Product Unit of Measure",
Expand Down Expand Up @@ -1670,12 +1686,18 @@ def _calc_incoming_dlt_qty(self):
# FIXME: filter using order_id.state while
# https://github.com/odoo/odoo/pull/58966 is not merged.
# Can be changed in v14.
pols = rec.purchase_line_ids.filtered(
lambda l: l.date_planned <= fields.Datetime.to_datetime(cut_date)
and l.order_id.state in ("draft", "sent")
)
rec.rfq_inside_dlt_qty = sum(pols.mapped("product_qty"))
pols = rec.purchase_line_ids.filtered(
lambda l: l.date_planned > fields.Datetime.to_datetime(cut_date)
and l.order_id.state in ("draft", "sent")
)
rec.rfq_outside_dlt_qty = sum(pols.mapped("product_qty"))
else:
rec.rfq_inside_dlt_qty = 0.0
rec.rfq_outside_dlt_qty = 0.0
return True

Expand Down Expand Up @@ -1817,19 +1839,23 @@ def _search_purchase_order_lines_incoming(self, outside_dlt=False):
)
return pols

def action_view_supply(self, outside_dlt=False):
def action_view_supply(self, outside_dlt=False, view_rfq=False):
if self.item_type == "purchased":
pols = self._search_purchase_order_lines_incoming(outside_dlt)
moves = self._search_stock_moves_incoming(outside_dlt)
while moves.mapped("move_orig_ids"):
moves = moves.mapped("move_orig_ids")
pos = pols.mapped("order_id") + moves.mapped("purchase_line_id.order_id")
pos = pols.mapped("order_id")
if not view_rfq:
moves = self._search_stock_moves_incoming(outside_dlt)
while moves.mapped("move_orig_ids"):
moves = moves.mapped("move_orig_ids")
pos = moves.mapped("purchase_line_id.order_id")
result = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_rfq")
# Remove the context since the action display RFQ and not PO.
result["context"] = {}
result["domain"] = [("id", "in", pos.ids)]
elif self.item_type == "manufactured":
moves = self._search_stock_moves_incoming(outside_dlt)
while moves.mapped("move_orig_ids"):
moves = moves.mapped("move_orig_ids")
mos = moves.mapped("production_id")
result = self.env["ir.actions.actions"]._for_xml_id(
"mrp.mrp_production_action"
Expand All @@ -1852,6 +1878,12 @@ def action_view_supply_inside_dlt_window(self):
def action_view_supply_outside_dlt_window(self):
return self.action_view_supply(outside_dlt=True)

def action_view_rfq_inside_dlt_window(self):
return self.action_view_supply(view_rfq=True)

Check warning on line 1882 in ddmrp/models/stock_buffer.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1882

Added line #L1882 was not covered by tests

def action_view_rfq_outside_dlt_window(self):
return self.action_view_supply(outside_dlt=True, view_rfq=True)

Check warning on line 1885 in ddmrp/models/stock_buffer.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1885

Added line #L1885 was not covered by tests

def action_view_qualified_demand_pickings(self):
moves = self.qualified_demand_stock_move_ids
picks = moves.mapped("picking_id")
Expand Down
162 changes: 162 additions & 0 deletions ddmrp/tests/test_ddmrp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1104,3 +1104,165 @@ def test_44_resupply_from_another_warehouse(self):
buffer_distributed.distributed_source_location_id,
self.warehouse.lot_stock_id,
)

def test_45_action_view_supply_buffer_purchase(self):
"""
Verify that the view incoming quantities action for a purchased buffer
displays the correct results.
"""
self.buffer_purchase.auto_procure = True
self.buffer_purchase.auto_procure_option = "standard"
self.buffer_purchase.cron_actions()
pol = self.pol_model.search([("product_id", "=", self.product_purchased.id)])
# Check that RFQs are correctly computed
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=True
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=True
)["domain"][0][2]
self.assertEqual(len(po_inside_dlt_ids), 0)
self.assertEqual(po_outside_dlt_ids, pol.order_id.ids)
pol.date_planned -= timedelta(days=1)
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=True
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=True
)["domain"][0][2]
self.assertEqual(po_inside_dlt_ids, pol.order_id.ids)
self.assertEqual(len(po_outside_dlt_ids), 0)
# Check that incoming quantities are correctly computed
pol.order_id.button_confirm()
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=False
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=False
)["domain"][0][2]
self.assertEqual(po_inside_dlt_ids, pol.order_id.ids)
self.assertEqual(len(po_outside_dlt_ids), 0)
pol.mapped("move_ids.picking_id").scheduled_date += timedelta(days=1)
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=False
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=False
)["domain"][0][2]
self.assertEqual(len(po_inside_dlt_ids), 0)
self.assertEqual(po_outside_dlt_ids, pol.order_id.ids)

def test_46_action_view_supply_buffer_manufacture(self):
"""
Verify that the view incoming quantities action for a manufactured buffer
displays the correct results.
"""
self.quant.quantity = 0
self.buffer_a.buffer_profile_id = self.buffer_profile_mmm.id
self.buffer_a.auto_procure = True
self.buffer_a.auto_procure_option = "standard"
self.buffer_a.cron_actions()
mo = self.buffer_a.mrp_production_ids.filtered(
lambda x: x.product_id == self.productA
)
# Check that MOs are correctly computed
mo_inside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=False)[
"domain"
][0][2]
mo_outside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=True)[
"domain"
][0][2]
self.assertEqual(mo_inside_dlt_ids, mo.ids)
self.assertEqual(len(mo_outside_dlt_ids), 0)
mo.date_planned_finished += timedelta(days=1)
mo_inside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=False)[
"domain"
][0][2]
mo_outside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=True)[
"domain"
][0][2]
self.assertEqual(len(mo_inside_dlt_ids), 0)
self.assertEqual(mo_outside_dlt_ids, mo.ids)

def test_47_action_view_supply_buffer_purchase_3_steps(self):
"""
Verify that the view incoming quantities action for a purchased buffer displays
the correct results with a 3-step configuration.
"""
self.warehouse.reception_steps = "three_steps"
self.buffer_purchase.auto_procure = True
self.buffer_purchase.auto_procure_option = "standard"
self.buffer_purchase.cron_actions()
pol = self.pol_model.search([("product_id", "=", self.product_purchased.id)])
# Check that RFQs are correctly computed
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=True
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=True
)["domain"][0][2]
self.assertEqual(len(po_inside_dlt_ids), 0)
self.assertEqual(po_outside_dlt_ids, pol.order_id.ids)
pol.date_planned -= timedelta(days=1)
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=True
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=True
)["domain"][0][2]
self.assertEqual(po_inside_dlt_ids, pol.order_id.ids)
self.assertEqual(len(po_outside_dlt_ids), 0)
# Check that incoming quantities are correctly computed
pol.order_id.button_confirm()
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=False
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=False
)["domain"][0][2]
self.assertEqual(po_inside_dlt_ids, pol.order_id.ids)
self.assertEqual(len(po_outside_dlt_ids), 0)
moves = pol.mapped("move_ids")
while moves.mapped("move_dest_ids"):
moves = moves.mapped("move_dest_ids")
moves.mapped("picking_id").scheduled_date += timedelta(days=1)
po_inside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=False, view_rfq=False
)["domain"][0][2]
po_outside_dlt_ids = self.buffer_purchase.action_view_supply(
outside_dlt=True, view_rfq=False
)["domain"][0][2]
self.assertEqual(len(po_inside_dlt_ids), 0)
self.assertEqual(po_outside_dlt_ids, pol.order_id.ids)

def test_48_action_view_supply_buffer_manufacture_3_steps(self):
"""
Verify that the view incoming quantities action for a manufactured buffer displays
the correct results with a 3-step configuration.
"""
self.warehouse.manufacture_steps = "pbm_sam"
self.quant.quantity = 0
self.buffer_a.buffer_profile_id = self.buffer_profile_mmm.id
self.buffer_a.auto_procure = True
self.buffer_a.auto_procure_option = "standard"
self.buffer_a.cron_actions()
mo = self.env["mrp.production"].search([("product_id", "=", self.productA.id)])
# Check that MOs are correctly computed
mo_inside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=False)[
"domain"
][0][2]
mo_outside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=True)[
"domain"
][0][2]
self.assertEqual(mo_inside_dlt_ids, mo.ids)
self.assertEqual(len(mo_outside_dlt_ids), 0)
move = self.buffer_a._search_stock_moves_incoming()
move.picking_id.scheduled_date += timedelta(days=1)
mo_inside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=False)[
"domain"
][0][2]
mo_outside_dlt_ids = self.buffer_a.action_view_supply(outside_dlt=True)[
"domain"
][0][2]
self.assertEqual(len(mo_inside_dlt_ids), 0)
self.assertEqual(mo_outside_dlt_ids, mo.ids)
57 changes: 48 additions & 9 deletions ddmrp/views/stock_buffer_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@
string="Incoming Outside DLT"
optional="hide"
/>
<field
name="rfq_outside_dlt_qty"
string="RFQ Qty Outside DLT"
optional="hide"
/>
<button
title="Open Non-completed Moves"
name="open_moves"
Expand All @@ -74,10 +69,28 @@
name="action_view_supply_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0), ('rfq_outside_dlt_qty', '=', 0)]}"
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0)]}"
/>
<field name="rfq_inside_dlt_qty" invisible="1" />
<field name="rfq_outside_dlt_qty" invisible="1" />
<button
title="Some RFQ quantities are inside the DLT Horizon and may require confirming.
Press this button to display the involved RFQs"
name="action_view_rfq_inside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('rfq_inside_dlt_qty', '=', 0)]}"
/>
<button
title="No stock available on source location for distributed buffer"
title="Some RFQ quantities are outside of the DLT Horizon and may require rescheduling.
Press this button to display the involved RFQs"
name="action_view_rfq_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('rfq_outside_dlt_qty', '=', 0)]}"
/>
<button
title="No stock available in source location for distributed buffer"
name="action_dummy"
icon="fa-warning"
type="object"
Expand Down Expand Up @@ -389,21 +402,47 @@
type="object"
attrs="{'invisible': [('incoming_dlt_qty', '=', 0)]}"
/>
<field
name="incoming_outside_dlt_qty"
invisible="1"
/>
<field name="rfq_inside_dlt_qty" invisible="1" />
<div
class="o_row"
attrs="{'invisible':[('rfq_inside_dlt_qty', '=', 0)]}"
>
<button
title="Some RFQ quantities are inside the DLT Horizon and may require confirming.
Press this button to display the involved RFQs"
name="action_view_rfq_inside_dlt_window"
icon="fa-warning"
type="object"
/>
</div>
<field name="rfq_outside_dlt_qty" invisible="1" />
<div
class="o_row"
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0), ('rfq_outside_dlt_qty', '=', 0)]}"
attrs="{'invisible':[('total_incoming_outside_dlt_qty', '=', 0)]}"
>
<button
title="Some incoming quantities are outside of the DLT Horizon and may require rescheduling.
Press this button to display the involved supply orders"
name="action_view_supply_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0)]}"
/>
<button
title="Some RFQ quantities are outside of the DLT Horizon and may require rescheduling.
Press this button to display the involved RFQs"
name="action_view_rfq_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('rfq_outside_dlt_qty', '=', 0)]}"
/>
(Outside DLT:
<field
name="incoming_outside_dlt_qty"
name="total_incoming_outside_dlt_qty"
invisible="0"
/>
)
Expand Down

0 comments on commit 199557c

Please sign in to comment.