diff options
| author | Rémy Voet (ryv) <ryv@odoo.com> | 2020-11-10 16:30:28 +0000 |
|---|---|---|
| committer | Rémy Voet (ryv) <ryv@odoo.com> | 2020-11-18 09:16:08 +0000 |
| commit | 1a34e46eaca14aec981dd0f162e8014c7cb0abea (patch) | |
| tree | f8ba8ec3169f43f1fb164914ba29452d02498951 /addons/mrp_subcontracting/models | |
| parent | 9e244ae3053f46fc22b8e9a75015d57cd70053b8 (diff) | |
[FIX] mrp_subcontracting: fix flow with only tracked finished product
Step to reproduce:
- Create a subcontracting BoM by partner A,
for a tracked product B (by serial or lots)
- Create a incoming transfer coming from A and mark as todo.
- Fill the move with move lines with lot + quantity.
- It is impossible to validate the transfer, "You need to supply lot..."
Also at the creation of the picking a warning popover indicate that
the previous operation (the hidden MO) is set after the transfer.
These issues comes from the refactor of mrp for v14 and the part of
subcontracting wasn't complete obviously.
Fixes done:
- Rewrite the code of the `_action_done` for subcontracting. It is for
the case of tracked finished product without any tracked component,
there wasn't any code to manage that. Now manage it by backorder MO
feature. Fix the main bug
- Set finished_date_planned before the transfer to avoid the alert
popover.
- Avoid to write activity in channel about the hidden MO in case of
cancelling.
- Fix `action_record_components` to manage multiple subcontracting
products with tracked component.
- Remove the `priority` field from the wizard MO (in case of tracked
product. Also the wizard is not really a wizard, just a weird hybrid
MO form)
PR: odoo/odoo#61606
task-2357115
Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com>
Diffstat (limited to 'addons/mrp_subcontracting/models')
| -rw-r--r-- | addons/mrp_subcontracting/models/mrp_production.py | 5 | ||||
| -rw-r--r-- | addons/mrp_subcontracting/models/stock_move.py | 23 | ||||
| -rw-r--r-- | addons/mrp_subcontracting/models/stock_picking.py | 86 |
3 files changed, 69 insertions, 45 deletions
diff --git a/addons/mrp_subcontracting/models/mrp_production.py b/addons/mrp_subcontracting/models/mrp_production.py index fcc77b32e63..e546bf1b034 100644 --- a/addons/mrp_subcontracting/models/mrp_production.py +++ b/addons/mrp_subcontracting/models/mrp_production.py @@ -125,3 +125,8 @@ class MrpProduction(models.Model): for ml in subcontract_move_id.move_line_ids: ml.product_uom_qty = ml.qty_done subcontract_move_id._recompute_state() + + def _subcontracting_filter_to_done(self): + """ Filter subcontracting production where composant is already recorded and should be consider to be validate """ + mos = self.filtered(lambda mo: mo.state not in ('done', 'cancel')) + return mos.filtered(lambda pro: all(line.lot_id for line in pro.move_raw_ids.filtered(lambda sm: sm.has_tracking != 'none').move_line_ids)) diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py index 32817a4ca8b..45f5bcce5eb 100644 --- a/addons/mrp_subcontracting/models/stock_move.py +++ b/addons/mrp_subcontracting/models/stock_move.py @@ -51,11 +51,18 @@ class StockMove(models.Model): """ If the initial demand is updated then also update the linked subcontract order to the new quantity. """ - if 'product_uom_qty' in values: - if self.env.context.get('cancel_backorder') is False: - return super(StockMove, self).write(values) + if 'product_uom_qty' in values and self.env.context.get('cancel_backorder') is not False: self.filtered(lambda m: m.is_subcontract and m.state not in ['draft', 'cancel', 'done'])._update_subcontract_order_qty(values['product_uom_qty']) - return super(StockMove, self).write(values) + res = super().write(values) + if 'date' in values: + for move in self: + if move.state in ('done', 'cancel') or not move.is_subcontract: + continue + move.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel')).write({ + 'date_planned_finished': move.date, + 'date_planned_start': move.date, + }) + return res def action_show_details(self): """ Open the produce wizard in order to register tracked components for @@ -97,10 +104,10 @@ class StockMove(models.Model): def _action_cancel(self): for move in self: if move.is_subcontract: - production = move.move_orig_ids.production_id + active_production = move.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel')) moves = self.env.context.get('moves_todo') - if not moves or production not in moves.move_orig_ids.production_id: - production._action_cancel() + if not moves or active_production not in moves.move_orig_ids.production_id: + active_production.with_context(skip_activity=True).action_cancel() return super()._action_cancel() def _action_confirm(self, merge=True, merge_into=False): @@ -192,7 +199,7 @@ class StockMove(models.Model): break if quantity_to_remove >= production.product_qty: quantity_to_remove -= production.product_qty - production.action_cancel() + production.with_context(skip_activity=True).action_cancel() else: self.env['change.production.qty'].with_context(skip_activity=True).create({ 'mo_id': production.id, diff --git a/addons/mrp_subcontracting/models/stock_picking.py b/addons/mrp_subcontracting/models/stock_picking.py index 6faebd8a809..23949e6cac9 100644 --- a/addons/mrp_subcontracting/models/stock_picking.py +++ b/addons/mrp_subcontracting/models/stock_picking.py @@ -4,7 +4,7 @@ from datetime import timedelta from odoo import api, fields, models -from odoo.tools.float_utils import float_is_zero +from odoo.tools.float_utils import float_compare, float_is_zero class StockPicking(models.Model): @@ -41,39 +41,48 @@ class StockPicking(models.Model): # ------------------------------------------------------------------------- def _action_done(self): res = super(StockPicking, self)._action_done() + + for move in self.move_lines.filtered(lambda move: move.is_subcontract): + # Auto set qty_producing/lot_producing_id of MO if there isn't tracked component + # If there is tracked component, the flow use subcontracting_record_component instead + if move._has_tracked_subcontract_components(): + continue + production = move.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel'))[-1:] + if not production: + continue + # Manage additional quantities + quantity_done_move = move.product_uom._compute_quantity(move.quantity_done, production.product_uom_id) + if float_compare(production.product_qty, quantity_done_move, precision_rounding=production.product_uom_id.rounding) == -1: + change_qty = self.env['change.production.qty'].create({ + 'mo_id': production.id, + 'product_qty': quantity_done_move + }) + change_qty.with_context(skip_activity=True).change_prod_qty() + # Create backorder MO for each move lines + for move_line in move.move_line_ids: + if move_line.lot_id: + production.lot_producing_id = move_line.lot_id + production.qty_producing = move_line.product_uom_id._compute_quantity(move_line.qty_done, production.product_uom_id) + production._set_qty_producing() + if move_line != move.move_line_ids[-1]: + backorder = production._generate_backorder_productions(close_mo=False) + # The move_dest_ids won't be set because the _split filter out done move + backorder.move_finished_ids.filtered(lambda mo: mo.product_id == move.product_id).move_dest_ids = production.move_finished_ids.filtered(lambda mo: mo.product_id == move.product_id).move_dest_ids + production.product_qty = production.qty_producing + production = backorder + for picking in self: - # Auto set qty_producing if not tracked component - for move in picking.move_lines.filtered(lambda move: move.is_subcontract): - if not move._has_tracked_subcontract_components(): - production = move.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel')) - if production: - qty_done_production_uom = move.product_uom._compute_quantity(move.quantity_done, production.product_uom_id) - production.qty_producing += qty_done_production_uom - for move in (production.move_raw_ids | production.move_finished_ids.filtered(lambda m: m.product_id != production.product_id)): - if move.state in ('done', 'cancel'): - continue - if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): - continue - new_qty = production.product_uom_id._compute_quantity( - (production.qty_producing - production.qty_produced) * move.unit_factor, production.product_uom_id, - rounding_method='HALF-UP') - move.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0 - move.move_line_ids = move._set_quantity_done_prepare_vals(new_qty) - - productions_to_done = picking.move_lines.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel')) - # TODO : to change in master with field on mrp - productions_to_done = productions_to_done.filtered(lambda pro: all(line.lot_id for line in pro.move_raw_ids.filtered(lambda sm: sm.has_tracking != 'none').move_line_ids)) - for subcontracted_production in productions_to_done: - if subcontracted_production.state == 'progress': - subcontracted_production._post_inventory() - else: - subcontracted_production.with_context(subcontract_move_id=True).button_mark_done() - # For concistency, set the date on production move before the date - # on picking. (Tracability report + Product Moves menu item) - minimum_date = min(picking.move_line_ids.mapped('date')) - production_moves = subcontracted_production.move_raw_ids | subcontracted_production.move_finished_ids - production_moves.write({'date': minimum_date - timedelta(seconds=1)}) - production_moves.move_line_ids.write({'date': minimum_date - timedelta(seconds=1)}) + productions_to_done = picking._get_subcontracted_productions()._subcontracting_filter_to_done() + production_ids_backorder = [] + if not self.env.context.get('cancel_backorder'): + production_ids_backorder = productions_to_done.filtered(lambda mo: mo.state == "progress").ids + productions_to_done.with_context(subcontract_move_id=True, mo_ids_to_backorder=production_ids_backorder).button_mark_done() + # For concistency, set the date on production move before the date + # on picking. (Traceability report + Product Moves menu item) + minimum_date = min(picking.move_line_ids.mapped('date')) + production_moves = productions_to_done.move_raw_ids | productions_to_done.move_finished_ids + production_moves.write({'date': minimum_date - timedelta(seconds=1)}) + production_moves.move_line_ids.write({'date': minimum_date - timedelta(seconds=1)}) return res def action_record_components(self): @@ -81,7 +90,9 @@ class StockPicking(models.Model): for move in self.move_lines: if not move._has_tracked_subcontract_components(): continue - production = move.move_orig_ids.production_id[-1:] + productions = move.move_orig_ids.production_id + productions_to_done = productions._subcontracting_filter_to_done() + production = (productions - productions_to_done)[-1:] if not production: continue return move._action_record_components() @@ -94,8 +105,7 @@ class StockPicking(models.Model): return self.picking_type_id.code == 'incoming' and any(m.is_subcontract for m in self.move_lines) def _get_subcontracted_productions(self): - self.ensure_one() - return self.move_lines.mapped('move_orig_ids.production_id') + return self.move_lines.move_orig_ids.production_id def _get_warehouse(self, subcontract_move): return subcontract_move.warehouse_id or self.picking_type_id.warehouse_id @@ -117,7 +127,8 @@ class StockPicking(models.Model): 'location_src_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id, 'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id, 'product_qty': subcontract_move.product_uom_qty, - 'picking_type_id': warehouse.subcontracting_type_id.id + 'picking_type_id': warehouse.subcontracting_type_id.id, + 'date_planned_start': subcontract_move.date } return vals @@ -127,6 +138,7 @@ class StockPicking(models.Model): mo = self.env['mrp.production'].with_company(move.company_id).create(self._prepare_subcontract_mo_vals(move, bom)) self.env['stock.move'].create(mo._get_moves_raw_values()) self.env['stock.move'].create(mo._get_moves_finished_values()) + mo.date_planned_finished = move.date # Avoid to have the picking late depending of the MO mo.action_confirm() # Link the finished to the receipt move. |
