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 @@