From 94b673dd293c3889c41b026e98dcab5033a72250 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 19 Sep 2017 15:56:28 +0200 Subject: [PATCH] [IMP] mrp: lock/unlock mode and show final lot We apply a lock/unlock mode for the mo as was done for the picking. In locked mode, the user can do all the regular things, but not change the quantities done given by the work order or produce wizard. In unlocked mode, only allowed by the mrp manager, the user can change those quantities. We opted to remove the 4 lines and just use a check box to indicate there are lots behind a line. For the finished products tab, we opted to show the move lines instead of the moves as this way, you don't need to click further. If nothing has been produced yet, a message indicates to produce something first. --- addons/mrp/models/mrp_production.py | 42 +++++++++++++--- addons/mrp/models/stock_move.py | 60 ++++++++++------------- addons/mrp/views/mrp_production_views.xml | 56 +++++++++++---------- addons/mrp/views/stock_move_views.xml | 30 +++++++----- addons/stock/models/stock_move.py | 9 +++- 5 files changed, 117 insertions(+), 80 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 9d2076596a0..34878e9cdb6 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -106,6 +106,9 @@ class MrpProduction(models.Model): 'stock.move', 'production_id', 'Finished Products', copy=False, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('scrapped', '=', False)]) + finished_move_line_ids = fields.One2many( + 'stock.move.line', compute='_compute_lines', inverse='_inverse_lines', + ) workorder_ids = fields.One2many( 'mrp.workorder', 'production_id', 'Work Orders', copy=False, oldname='workcenter_lines', readonly=True) @@ -157,6 +160,23 @@ class MrpProduction(models.Model): scrap_count = fields.Integer(compute='_compute_scrap_move_count', string='Scrap Move') priority = fields.Selection([('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')], 'Priority', readonly=True, states={'confirmed': [('readonly', False)]}, default='1') + is_locked = fields.Boolean('Is Locked', default=True) + show_final_lots = fields.Boolean('Show Final Lots', compute='_compute_show_lots') + production_location_id = fields.Many2one('stock.location', "Production Location", related='product_id.property_stock_production') + + @api.depends('product_id.tracking') + def _compute_show_lots(self): + for production in self: + production.show_final_lots = production.product_id.tracking != 'none' + + def _inverse_lines(self): + """ Little hack to make sure that when you change something on these objects, it gets saved""" + pass + + @api.depends('move_finished_ids.move_line_ids') + def _compute_lines(self): + for production in self: + production.finished_move_line_ids = production.move_finished_ids.mapped('move_line_ids') @api.multi @api.depends('bom_id.routing_id', 'bom_id.routing_id.operation_ids') @@ -199,18 +219,21 @@ class MrpProduction(models.Model): assigned_list = [x.state in ('assigned', 'done', 'cancel') for x in order.move_raw_ids] order.availability = (all(assigned_list) and 'assigned') or (any(partial_list) and 'partially_available') or 'waiting' + @api.depends('move_raw_ids', 'is_locked', 'state', 'move_raw_ids.quantity_done') def _compute_unreserve_visible(self): - return True + for order in self: + already_reserved = order.is_locked and order.state not in ('done', 'cancel') and order.mapped('move_raw_ids.move_line_ids') + any_quantity_done = any([m.quantity_done > 0 for m in order.move_raw_ids]) + order.unreserve_visible = not any_quantity_done and already_reserved @api.multi - @api.depends('move_raw_ids.quantity_done', 'move_finished_ids.quantity_done') + @api.depends('move_raw_ids.quantity_done', 'move_finished_ids.quantity_done', 'is_locked') def _compute_post_visible(self): for order in self: if order.product_tmpl_id._is_cost_method_standard(): - order.post_visible = any((x.quantity_done > 0 and x.state not in ['done', 'cancel']) for x in order.move_raw_ids) or \ - any((x.quantity_done > 0 and x.state not in ['done' 'cancel']) for x in order.move_finished_ids) + order.post_visible = order.is_locked and any((x.quantity_done > 0 and x.state not in ['done', 'cancel']) for x in order.move_raw_ids | order.move_finished_ids) else: - order.post_visible = any((x.quantity_done > 0 and x.state not in ['done', 'cancel']) for x in order.move_finished_ids) + order.post_visible = order.is_locked and any((x.quantity_done > 0 and x.state not in ['done', 'cancel']) for x in order.move_finished_ids) @api.multi @api.depends('move_raw_ids.quantity_done', 'move_raw_ids.product_uom_qty') @@ -223,7 +246,7 @@ class MrpProduction(models.Model): ) @api.multi - @api.depends('workorder_ids.state', 'move_finished_ids') + @api.depends('workorder_ids.state', 'move_finished_ids', 'is_locked') def _get_produced_qty(self): for production in self: done_moves = production.move_finished_ids.filtered(lambda x: x.state != 'cancel' and x.product_id.id == production.product_id.id) @@ -231,7 +254,7 @@ class MrpProduction(models.Model): wo_done = True if any([x.state not in ('done', 'cancel') for x in production.workorder_ids]): wo_done = False - production.check_to_done = done_moves and (qty_produced >= production.product_qty) and (production.state not in ('done', 'cancel')) and wo_done + production.check_to_done = production.is_locked and done_moves and (qty_produced >= production.product_qty) and (production.state not in ('done', 'cancel')) and wo_done production.qty_produced = qty_produced return True @@ -293,6 +316,11 @@ class MrpProduction(models.Model): raise UserError(_('Cannot delete a manufacturing order not in cancel state')) return super(MrpProduction, self).unlink() + def action_toggle_is_locked(self): + self.ensure_one() + self.is_locked = not self.is_locked + return True + @api.multi def _generate_moves(self): for production in self: diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index c7d7111fd1b..6fddb2f1f92 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -66,7 +66,31 @@ class StockMove(models.Model): 'Done', compute='_compute_is_done', store=True, help='Technical Field to order moves') - + needs_lots = fields.Boolean('Tracking', compute='_compute_needs_lots') + order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids') + + @api.depends('active_move_line_ids.qty_done', 'active_move_line_ids.product_uom_id') + def _compute_done_quantity(self): + super(StockMove, self)._compute_done_quantity() + + @api.depends('raw_material_production_id.move_finished_ids.move_line_ids.lot_id') + def _compute_order_finished_lot_ids(self): + for move in self: + if move.product_id.tracking != 'none' and move.raw_material_production_id: + move.order_finished_lot_ids = move.raw_material_production_id.move_finished_ids.mapped('move_line_ids.lot_id').ids + + @api.depends('product_id.tracking') + def _compute_needs_lots(self): + for move in self: + move.needs_lots = move.product_id.tracking != 'none' + + @api.depends('raw_material_production_id.is_locked', 'picking_id.is_locked') + def _compute_is_locked(self): + super(StockMove, self)._compute_is_locked() + for move in self: + if move.raw_material_production_id: + move.is_locked = move.raw_material_production_id.is_locked + def _get_move_lines(self): self.ensure_one() if self.raw_material_production_id: @@ -93,40 +117,6 @@ class StockMove(models.Model): If you want to cancel this MO, please change the consumed quantities to 0.')) return super(StockMove, self)._action_cancel() - @api.multi - # Could use split_move_operation from stock here - def split_move_lot(self): - ctx = dict(self.env.context) - self.ensure_one() - view = self.env.ref('mrp.view_stock_move_lots') - serial = (self.has_tracking == 'serial') - only_create = False # Check operation type in theory - show_reserved = any([x for x in self.move_line_ids if x.product_qty > 0.0]) - ctx.update({ - 'serial': serial, - 'only_create': only_create, - 'create_lots': True, - 'state_done': self.is_done, - 'show_reserved': show_reserved, - }) - if ctx.get('w_production'): - action = self.env.ref('mrp.act_mrp_product_produce').read()[0] - action['context'] = ctx - return action - result = { - 'name': _('Register Lots'), - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'stock.move', - 'views': [(view.id, 'form')], - 'view_id': view.id, - 'target': 'new', - 'res_id': self.id, - 'context': ctx, - } - return result - def _action_confirm(self, merge=True): moves = self for move in self.filtered(lambda m: m.production_id): diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index d0415dc2fc3..9e27498caaa 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -38,18 +38,21 @@