mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-21 07:12:05 +02:00
152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
# 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
|
|
|
|
|
|
class StockMove(models.Model):
|
|
_inherit = "stock.move"
|
|
|
|
buffer_ids = fields.Many2many(
|
|
comodel_name="stock.buffer",
|
|
string="Linked Stock Buffers",
|
|
)
|
|
# Add an index as '_find_buffer_link' method is using it as search criteria
|
|
created_purchase_line_id = fields.Many2one(index=True)
|
|
|
|
def _prepare_procurement_values(self):
|
|
res = super(StockMove, self)._prepare_procurement_values()
|
|
if self.buffer_ids:
|
|
res["buffer_ids"] = self.buffer_ids
|
|
return res
|
|
|
|
def _merge_moves_fields(self):
|
|
res = super(StockMove, self)._merge_moves_fields()
|
|
res["buffer_ids"] = [(4, m.id) for m in self.mapped("buffer_ids")]
|
|
return res
|
|
|
|
def write(self, vals):
|
|
res = super(StockMove, self).write(vals)
|
|
if self and self.env.company.ddmrp_auto_update_nfp:
|
|
# Stock moves changes can be triggered by users without
|
|
# access to write stock buffers, thus we do it with sudo.
|
|
if "state" in vals:
|
|
self.sudo()._update_ddmrp_nfp()
|
|
elif "location_id" in vals or "location_dest_id" in vals:
|
|
self.sudo().filtered(
|
|
lambda m: m.state
|
|
in ("confirmed", "partially_available", "assigned")
|
|
)._update_ddmrp_nfp()
|
|
return res
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
moves = super(StockMove, self).create(vals_list)
|
|
# TODO should we use @api.model_create_single instead?
|
|
moves_to_update_ids = []
|
|
for vals, move in zip(vals_list, moves):
|
|
if (
|
|
"state" in vals
|
|
and move.state not in ("draft", "cancel")
|
|
and self.env.company.ddmrp_auto_update_nfp
|
|
):
|
|
moves_to_update_ids.append(move.id)
|
|
# Stock moves state changes can be triggered by users without
|
|
# access to write stock buffers, thus we do it with sudo.
|
|
if moves_to_update_ids:
|
|
self.browse(moves_to_update_ids).sudo()._update_ddmrp_nfp()
|
|
return moves
|
|
|
|
def _find_buffers_to_update_nfp(self):
|
|
# Find buffers that can be affected. `out_buffers` will see the move as
|
|
# outgoing and `in_buffers` as incoming.
|
|
out_buffers = in_buffers = self.env["stock.buffer"]
|
|
for move in self:
|
|
out_buffers |= move.mapped("product_id.buffer_ids").filtered(
|
|
lambda buffer: (
|
|
move.location_id.is_sublocation_of(buffer.location_id)
|
|
and not move.location_dest_id.is_sublocation_of(buffer.location_id)
|
|
)
|
|
)
|
|
in_buffers |= move.mapped("product_id.buffer_ids").filtered(
|
|
lambda buffer: (
|
|
not move.location_id.is_sublocation_of(buffer.location_id)
|
|
and move.location_dest_id.is_sublocation_of(buffer.location_id)
|
|
)
|
|
)
|
|
return out_buffers, in_buffers
|
|
|
|
def _update_ddmrp_nfp(self):
|
|
if self.env.context.get("no_ddmrp_auto_update_nfp"):
|
|
return True
|
|
out_buffers, in_buffers = self._find_buffers_to_update_nfp()
|
|
for buffer in out_buffers.with_context(no_ddmrp_history=True):
|
|
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)
|
|
while new_moves:
|
|
move_set |= new_moves
|
|
new_moves = new_moves.mapped(attr)
|
|
return move_set
|
|
|
|
all_moves = (
|
|
self | get_moves(self, "move_orig_ids") | get_moves(self, "move_dest_ids")
|
|
)
|
|
return all_moves
|
|
|
|
def _get_source_field_candidates(self):
|
|
"""Extend for more source field candidates."""
|
|
return [
|
|
"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:
|
|
for field in field.split("."):
|
|
current_object = getattr(current_object, field)
|
|
return True
|
|
except AttributeError:
|
|
return False
|
|
|
|
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()
|
|
# 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)
|
|
if record:
|
|
return record
|
|
return False
|
|
|
|
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()
|
|
if record:
|
|
return {
|
|
"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
|