Skip to content

Commit

Permalink
[IMP] ddmrp: Add customized Stock Moves view
Browse files Browse the repository at this point in the history
When checking incoming quantities or qualified demand moves, we will use a customized view that allows to redirect to the source document of that move.

A stock move can be chained with many other moves and can have two sources, like a dropship. Then, to calculate the source of that stock move, a list of fields ordered by priority is followed, in which if any record is found in any of them, it is returned as a source.
For example, if we have a stock move that belongs to a dropship with related sale and purchase, it will always return the sale as source because its the first field in the list. The sources field method is extensible to add any others that are needed.
  • Loading branch information
BernatPForgeFlow committed May 14, 2024
1 parent 89237c5 commit 176b5fd
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 78 deletions.
97 changes: 43 additions & 54 deletions ddmrp/models/stock_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1370,17 +1370,6 @@ def _stock_move_tree_view(self, lines):
"domain": str([("id", "in", lines.ids)]),
}

def open_moves(self):
self.ensure_one()
# Utility method used to add an "Open Moves" button in the buffer
# planning view
domain = self._search_open_stock_moves_domain()
moves = self.env["stock.move"].search(domain)
moves = moves.filtered(
lambda move: move.location_dest_id.is_sublocation_of(self.location_id)
)
return self._stock_move_tree_view(moves)

def _get_horizon_adu_past_demand(self):
return self.adu_calculation_method.horizon_past or 0

Expand Down Expand Up @@ -1828,72 +1817,72 @@ def do_auto_procure(self):
wizard.make_procurement()
return True

def _search_purchase_order_lines_incoming(self, outside_dlt=False):
def action_view_supply_moves(self):
result = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
result["context"] = {}
moves = self._search_stock_moves_incoming() + self._search_stock_moves_incoming(

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1821-L1823

Added lines #L1821 - L1823 were not covered by tests
outside_dlt=True
)
result["domain"] = [("id", "in", moves.ids)]
return result

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1826-L1827

Added lines #L1826 - L1827 were not covered by tests

def _get_rfq_dlt(self, outside_dlt=False):
self.ensure_one()

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1830

Added line #L1830 was not covered by tests
cut_date = self._get_incoming_supply_date_limit()
if not outside_dlt:
pols = self.purchase_line_ids.filtered(
lambda l: l.date_planned <= fields.Datetime.to_datetime(cut_date)
and l.order_id.state in ("draft", "sent")
and l.state in ("draft", "sent")
)
else:
pols = self.purchase_line_ids.filtered(
lambda l: l.date_planned > fields.Datetime.to_datetime(cut_date)
and l.order_id.state in ("draft", "sent")
and l.state in ("draft", "sent")
)
return pols

def action_view_supply(self, outside_dlt=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")
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)
mos = moves.mapped("production_id")
result = self.env["ir.actions.actions"]._for_xml_id(
"mrp.mrp_production_action"
)
result["context"] = {}
result["domain"] = [("id", "in", mos.ids)]
else:
moves = self._search_stock_moves_incoming(outside_dlt)
picks = moves.mapped("picking_id")
result = self.env["ir.actions.actions"]._for_xml_id(
"stock.action_picking_tree_all"
)
result["context"] = {}
result["domain"] = [("id", "in", picks.ids)]
def action_view_supply_moves_inside_dlt_window(self):
result = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
moves = self._search_stock_moves_incoming()
result["context"] = {}
result["domain"] = [("id", "in", moves.ids)]

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1845-L1848

Added lines #L1845 - L1848 were not covered by tests
return result

def action_view_supply_inside_dlt_window(self):
return self.action_view_supply()
def action_view_supply_moves_outside_dlt_window(self):
result = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
moves = self._search_stock_moves_incoming(outside_dlt=True)
result["context"] = {}
result["domain"] = [("id", "in", moves.ids)]
return result

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1852-L1856

Added lines #L1852 - L1856 were not covered by tests

def action_view_supply_outside_dlt_window(self):
return self.action_view_supply(outside_dlt=True)
def action_view_supply_rfq_inside_dlt_window(self):
result = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_rfq")
pols = self._get_rfq_dlt()
pos = pols.mapped("order_id")
result["context"] = {}
result["domain"] = [("id", "in", pos.ids)]
return result

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1859-L1864

Added lines #L1859 - L1864 were not covered by tests

def action_view_qualified_demand_pickings(self):
moves = self.qualified_demand_stock_move_ids
picks = moves.mapped("picking_id")
result = self.env["ir.actions.actions"]._for_xml_id(
"stock.action_picking_tree_all"
)
def action_view_supply_rfq_outside_dlt_window(self):
result = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_rfq")
pols = self._get_rfq_dlt(outside_dlt=True)
pos = pols.mapped("order_id")

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1867-L1869

Added lines #L1867 - L1869 were not covered by tests
result["context"] = {}
result["domain"] = [("id", "in", picks.ids)]
result["domain"] = [("id", "in", pos.ids)]
return result

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1871-L1872

Added lines #L1871 - L1872 were not covered by tests

def action_view_qualified_demand_moves(self):
result = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
result["context"] = {}
result["domain"] = [("id", "in", self.qualified_demand_stock_move_ids.ids)]

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

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_buffer.py#L1875-L1877

Added lines #L1875 - L1877 were not covered by tests
return result

def action_view_qualified_demand_mrp(self):
mrp_moves = self.qualified_demand_mrp_move_ids
result = self.env["ir.actions.actions"]._for_xml_id(
"mrp_multi_level.mrp_move_action"
)
result["context"] = {}
result["domain"] = [("id", "in", mrp_moves.ids)]
result["domain"] = [("id", "in", self.qualified_demand_mrp_move_ids.ids)]

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
return result

def action_view_past_adu_direct_demand(self):
Expand Down
68 changes: 67 additions & 1 deletion ddmrp/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2019-20 ForgeFlow S.L. (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import api, fields, models
from odoo import _, api, fields, models


class StockMove(models.Model):
Expand Down Expand Up @@ -84,3 +84,69 @@ def _update_ddmrp_nfp(self):
buffer.cron_actions(only_nfp="out")
for buffer in in_buffers.with_context(no_ddmrp_history=True):
buffer.cron_actions(only_nfp="in")

def _get_all_linked_moves(self):
"""Retrieve all linked moves both origin and destination recursively."""

def get_moves(move_set, attr):
new_moves = move_set.mapped(attr)

Check warning on line 92 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L91-L92

Added lines #L91 - L92 were not covered by tests
while new_moves:
move_set |= new_moves
new_moves = new_moves.mapped(attr)
return move_set

Check warning on line 96 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L94-L96

Added lines #L94 - L96 were not covered by tests

all_moves = (

Check warning on line 98 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L98

Added line #L98 was not covered by tests
self | get_moves(self, "move_orig_ids") | get_moves(self, "move_dest_ids")
)
return all_moves

Check warning on line 101 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L101

Added line #L101 was not covered by tests

def _get_source_field_candidates(self):
"""Extend for more source field candidates."""
return [

Check warning on line 105 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L105

Added line #L105 was not covered by tests
"sale_line_id.order_id",
"purchase_line_id.order_id",
"production_id",
"raw_material_production_id",
"unbuild_id",
"repair_id",
"rma_line_id",
"picking_id",
]

def _has_nested_field(self, field):
"""Check if an object has a nested chain of fields."""
current_object = self
try:

Check warning on line 119 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L118-L119

Added lines #L118 - L119 were not covered by tests
for field in field.split("."):
current_object = getattr(current_object, field)
return True
except AttributeError:
return False

Check warning on line 124 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L121-L124

Added lines #L121 - L124 were not covered by tests

def _get_source_record(self):
"""Find the first source record in the field candidates linked with the moves,
prioritizing the order of field candidates."""
moves = self._get_all_linked_moves()
field_candidates = self._get_source_field_candidates()

Check warning on line 130 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L129-L130

Added lines #L129 - L130 were not covered by tests
# Iterate over the prioritized list of candidate fields
for field in field_candidates:
if self._has_nested_field(field):
for move in moves:
record = move.mapped(field)

Check warning on line 135 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L135

Added line #L135 was not covered by tests
if record:
return record
return False

Check warning on line 138 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L137-L138

Added lines #L137 - L138 were not covered by tests

def action_open_stock_move_source(self):
"""Open the source record of the stock move, if it exists."""
self.ensure_one()
record = self._get_source_record()

Check warning on line 143 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L142-L143

Added lines #L142 - L143 were not covered by tests
if record:
return {

Check warning on line 145 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L145

Added line #L145 was not covered by tests
"name": getattr(record, "name", _("Stock Move Source")),
"view_mode": "form",
"res_model": record._name,
"type": "ir.actions.act_window",
"res_id": record.id,
}
return False

Check warning on line 152 in ddmrp/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

ddmrp/models/stock_move.py#L152

Added line #L152 was not covered by tests
55 changes: 36 additions & 19 deletions ddmrp/views/stock_buffer_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,19 @@
type="object"
/>
<field name="incoming_dlt_qty" string="Incoming Within DLT" />
<field
name="incoming_total_qty"
string="Total Incoming"
optional="show"
/>
<field
name="incoming_outside_dlt_qty"
string="Incoming Outside DLT"
optional="hide"
/>
<field
name="rfq_outside_dlt_qty"
string="RFQ Qty Outside DLT"
optional="hide"
name="incoming_total_qty"
string="Total Incoming"
optional="show"
/>
<button
title="Open Non-completed Moves"
name="open_moves"
title="Total Incoming"
name="action_view_supply_moves"
icon="fa-exchange"
type="object"
/>
Expand All @@ -71,13 +66,22 @@
/>
<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"
name="action_view_supply_moves_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_outside_dlt_qty" invisible="1" />
<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_supply_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 @@ -397,8 +401,8 @@
<div name="incoming_dlt_qty" class="o_row">
<field name="incoming_dlt_qty" />
<button
title="View Incoming (Within DLT)"
name="action_view_supply_inside_dlt_window"
title="View Incoming Moves (Within DLT)"
name="action_view_supply_moves_inside_dlt_window"
icon="fa-search"
type="object"
attrs="{'invisible': [('incoming_dlt_qty', '=', 0)]}"
Expand All @@ -409,11 +413,24 @@
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0), ('rfq_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"
title="Some incoming qty is outside of the DLT Horizon and may require rescheduling.
Press this button to display the involved supply orders"
name="action_view_supply_moves_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('incoming_outside_dlt_qty', '=', 0)]}"
/>
<field
name="rfq_outside_dlt_qty"
invisible="1"
/>
<button
title="Some incoming RFQs are outside of the DLT Horizon and may require rescheduling.
Press this button to display the involved supply orders"
name="action_view_supply_rfq_outside_dlt_window"
icon="fa-warning"
type="object"
attrs="{'invisible':[('rfq_outside_dlt_qty', '=', 0)]}"
/>
(Outside DLT:
<field
Expand All @@ -436,7 +453,7 @@
/>
<button
title="View Qualified Demand from Pickings"
name="action_view_qualified_demand_pickings"
name="action_view_qualified_demand_moves"
icon="fa-search"
type="object"
attrs="{'invisible': ['|', ('qualified_demand', '=', 0), ('qualified_demand_stock_move_ids', '=', [])]}"
Expand Down
11 changes: 7 additions & 4 deletions ddmrp/views/stock_move_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
<!-- Copyright 2018-20 ForgeFlow S.L. (http://www.forgeflow.com)
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
<odoo>
<!--Fixes to show date on stock move report-->
<!--If someday Odoo corrects this, we could remove this views-->
<record id="view_move_tree" model="ir.ui.view">
<field name="name">stock.move.tree</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_tree" />
<field name="arch" type="xml">
<field name="date" position="after">
<field name="date" />
<field name="state" position="after">
<button
title="Go to Source"
name="action_open_stock_move_source"
icon="fa-arrow-right"
type="object"
/>
</field>
</field>
</record>
Expand Down

0 comments on commit 176b5fd

Please sign in to comment.