diff options
| author | William Henrotin <whe@odoo.com> | 2020-04-27 11:56:02 +0200 |
|---|---|---|
| committer | Simon Lejeune <sle@openerp.com> | 2020-06-15 16:59:41 +0200 |
| commit | 0e2765f5fd0ca0079f1f024ec3a1448b6d2ea3db (patch) | |
| tree | 90ea03d1aca1bf87a080775e7db07977b645c5cc /addons/mrp/models | |
| parent | 1b6421285c6bc4c61be3242cea30c2813251cbc7 (diff) | |
[REF] mrp: no more produce wizard
Use a view similar as the pickings one.
task-2241471
Diffstat (limited to 'addons/mrp/models')
| -rw-r--r-- | addons/mrp/models/mrp_abstract_workorder.py | 10 | ||||
| -rw-r--r-- | addons/mrp/models/mrp_bom.py | 2 | ||||
| -rw-r--r-- | addons/mrp/models/mrp_production.py | 203 | ||||
| -rw-r--r-- | addons/mrp/models/mrp_unbuild.py | 2 | ||||
| -rw-r--r-- | addons/mrp/models/stock_move.py | 94 |
5 files changed, 255 insertions, 56 deletions
diff --git a/addons/mrp/models/mrp_abstract_workorder.py b/addons/mrp/models/mrp_abstract_workorder.py index 13d560f2320..54edf3e8ffb 100644 --- a/addons/mrp/models/mrp_abstract_workorder.py +++ b/addons/mrp/models/mrp_abstract_workorder.py @@ -186,7 +186,7 @@ class MrpAbstractWorkorder(models.AbstractModel): if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0: break # move line already 'used' in workorder (from its lot for instance) - if move_line.lot_produced_ids or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0: + if float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0: continue # search wo line on which the lot is not fully consumed or other reserved lot linked_wo_line = self._workorder_line_ids().filtered( @@ -371,9 +371,9 @@ class MrpAbstractWorkorderLine(models.AbstractModel): """ update a move line to save the workorder line data""" self.ensure_one() if self.lot_id: - move_lines = self.move_id.move_line_ids.filtered(lambda ml: ml.lot_id == self.lot_id and not ml.lot_produced_ids) + move_lines = self.move_id.move_line_ids.filtered(lambda ml: ml.lot_id == self.lot_id) else: - move_lines = self.move_id.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_produced_ids) + move_lines = self.move_id.move_line_ids.filtered(lambda ml: not ml.lot_id) # Sanity check: if the product is a serial number and `lot` is already present in the other # consumed move lines, raise. @@ -396,14 +396,12 @@ class MrpAbstractWorkorderLine(models.AbstractModel): if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0: ml.write({ 'qty_done': new_quantity_done, - 'lot_produced_ids': self._get_produced_lots(), }) else: new_qty_reserved = ml.product_uom_qty - new_quantity_done default = { 'product_uom_qty': new_quantity_done, 'qty_done': new_quantity_done, - 'lot_produced_ids': self._get_produced_lots(), } ml.copy(default=default) ml.with_context(bypass_reservation_update=True).write({ @@ -436,7 +434,6 @@ class MrpAbstractWorkorderLine(models.AbstractModel): 'product_uom_qty': 0, 'product_uom_id': self.product_uom_id.id, 'qty_done': min(quantity, self.qty_done), - 'lot_produced_ids': self._get_produced_lots(), } if self.lot_id: vals.update({'lot_id': self.lot_id.id}) @@ -456,7 +453,6 @@ class MrpAbstractWorkorderLine(models.AbstractModel): 'product_uom_qty': 0, 'product_uom_id': self.product_uom_id.id, 'qty_done': self.qty_done, - 'lot_produced_ids': self._get_produced_lots(), } if self.lot_id: vals.update({'lot_id': self.lot_id.id}) diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 127f07af4b4..c999122c578 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -66,7 +66,7 @@ class MrpBom(models.Model): ('strict', 'Strict'), ('flexible', 'Flexible')], help="Defines if you can consume more or less components than the quantity defined on the BoM.", - default='strict', + default='flexible', string='Consumption', required=True ) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 06bf4fdee40..1398a6f1916 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -72,6 +72,7 @@ class MrpProduction(models.Model): domain="[('id', 'in', allowed_product_ids)]", readonly=True, required=True, check_company=True, states={'draft': [('readonly', False)]}) + product_tracking = fields.Selection(related='product_id.tracking') allowed_product_ids = fields.Many2many('product.product', compute='_compute_allowed_product_ids') product_tmpl_id = fields.Many2one('product.template', 'Product Template', related='product_id.product_tmpl_id') product_qty = fields.Float( @@ -83,6 +84,10 @@ class MrpProduction(models.Model): 'uom.uom', 'Product Unit of Measure', readonly=True, required=True, states={'draft': [('readonly', False)]}, domain="[('category_id', '=', product_uom_category_id)]") + lot_producing_id = fields.Many2one( + 'stock.production.lot', string='Lot/Serial Number', copy=False, + domain="[('product_id', '=', product_id), ('company_id', '=', company_id)]", check_company=True) + qty_producing = fields.Float(string="Quantity Producing", copy=False) product_uom_category_id = fields.Many2one(related='product_id.uom_id.category_id') product_uom_qty = fields.Float(string='Total Quantity', compute='_compute_product_uom_qty', store=True) picking_type_id = fields.Many2one( @@ -175,6 +180,7 @@ class MrpProduction(models.Model): 'stock.move', 'production_id', 'Finished Products', copy=False, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('scrapped', '=', False)]) + move_byproduct_ids = fields.One2many('stock.move', compute='_compute_move_byproduct_ids', inverse='_set_move_byproduct_ids') finished_move_line_ids = fields.One2many( 'stock.move.line', compute='_compute_lines', inverse='_inverse_lines', string="Finished Product" ) @@ -454,8 +460,9 @@ class MrpProduction(models.Model): 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 - order.reserve_visible = order.state in ('confirmed', 'planned') and any(move.state in ['confirmed', 'partially_available'] for move in order.move_raw_ids) + order.reserve_visible = order.state in ('confirmed', 'progress', 'to_close') and any(move.state in ['confirmed', 'partially_available'] for move in order.move_raw_ids.filtered(lambda m: m.product_uom_qty)) @api.depends('move_finished_ids.quantity_done', 'move_finished_ids.state', 'is_locked') def _compute_post_visible(self): @@ -476,6 +483,14 @@ class MrpProduction(models.Model): for production in self: production.scrap_count = count_data.get(production.id, 0) + @api.depends('move_finished_ids') + def _compute_move_byproduct_ids(self): + for order in self: + order.move_byproduct_ids = order.move_finished_ids.filtered(lambda m: m.product_id != order.product_id) + + def _set_move_byproduct_ids(self): + self.move_finished_ids |= self.move_byproduct_ids + _sql_constraints = [ ('name_uniq', 'unique(name, company_id)', 'Reference must be unique per Company!'), ('qty_positive', 'check (product_qty > 0)', 'The quantity to produce must be positive!'), @@ -502,7 +517,7 @@ class MrpProduction(models.Model): """ Finds UoM of changed product. """ if not self.product_id: self.bom_id = False - else: + elif not self.bom_id or self.bom_id.product_tmpl_id != self.product_tmpl_id or (self.bom_id.product_id and self.bom_id.product_id != self.product_id): bom = self.env['mrp.bom']._bom_find(product=self.product_id, picking_type=self.picking_type_id, company_id=self.company_id.id, bom_type='normal') if bom: self.bom_id = bom.id @@ -563,6 +578,28 @@ class MrpProduction(models.Model): self.location_src_id = self.picking_type_id.default_location_src_id.id or location.id self.location_dest_id = self.picking_type_id.default_location_dest_id.id or location.id + @api.onchange('qty_producing', 'lot_producing_id') + def _onchange_producing(self): + if self.product_id.tracking == 'serial': + qty_producing_uom = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') + if qty_producing_uom != 1: + self.qty_producing = self.product_id.uom_id._compute_quantity(1, self.product_uom_id, rounding_method='HALF-UP') + for move in (self.move_raw_ids | self.move_finished_ids.filtered(lambda m: m.product_id != self.product_id)): + if move.state in ('done', 'cancel'): + continue + # Do not update extra product quantities + if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): + continue + if move.has_tracking != 'none' or move.state == 'done': + continue + vals = move._update_quantity_done(self) + if vals.get('to_create'): + for res in vals['to_create']: + move.move_line_ids.new(res) + if vals.get('to_write'): + for move_line, res in vals['to_write']: + move_line.update(res) + def write(self, vals): res = super(MrpProduction, self).write(vals) @@ -572,7 +609,7 @@ class MrpProduction(models.Model): raise UserError(_('You cannot move a manufacturing order once it is cancelled or done.')) if production.workorder_ids and not self.env.context.get('force_date', False): raise UserError(_('You cannot move a planned manufacturing order.')) - if 'move_raw_ids' in vals and production.state != 'draft': + if ('move_raw_ids' in vals or 'move_finished_ids' in vals) and production.state != 'draft': production._autoconfirm_production() if not production.bom_id.operation_ids and vals.get('date_planned_start') and not vals.get('date_planned_finished'): new_date_planned_start = fields.Datetime.to_datetime(vals.get('date_planned_start')) @@ -595,7 +632,6 @@ class MrpProduction(models.Model): production = super(MrpProduction, self).create(values) production.move_raw_ids.write({ 'group_id': production.procurement_group_id.id, - 'reference': production.name, # set reference when MO name is different than 'New' }) # Trigger move_raw creation when importing a file if 'import_file' in self.env.context: @@ -692,7 +728,6 @@ class MrpProduction(models.Model): data = { 'sequence': bom_line.sequence if bom_line else 10, 'name': self.name, - 'reference': self.name, 'date': self.date_planned_start, 'date_expected': self.date_planned_start, 'bom_line_id': bom_line.id if bom_line else False, @@ -782,13 +817,14 @@ class MrpProduction(models.Model): ) additional_moves.write({ 'group_id': production.procurement_group_id.id, - 'reference': production.name, # set reference when MO name is different than 'New' }) additional_moves._adjust_procure_method() moves_to_confirm |= additional_moves - moves_to_confirm |= production.move_finished_ids.filtered( + additional_byproducts = production.move_finished_ids.filtered( lambda move: move.state == 'draft' and move.additional ) + moves_to_confirm |= additional_byproducts + if moves_to_confirm: moves_to_confirm._action_confirm() @@ -826,7 +862,7 @@ class MrpProduction(models.Model): }) else: action.update({ - 'name': _("MO Generated by %s" % self.name), + 'name': _("MO Generated by %s") % self.name, 'domain': [('id', 'in', mrp_production_ids)], 'view_mode': 'tree,form', }) @@ -842,6 +878,34 @@ class MrpProduction(models.Model): 'view_mode': 'tree,form', } + def action_generate_serial(self): + self.ensure_one() + self.lot_producing_id = self.env['stock.production.lot'].create({ + 'product_id': self.product_id.id, + 'company_id': self.company_id.id + }) + if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids: + self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id + if self.product_id.tracking == 'serial': + # copy/paste from _onchange_producing + qty_producing_uom = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') + if qty_producing_uom != 1: + self.qty_producing = self.product_id.uom_id._compute_quantity(1, self.product_uom_id, rounding_method='HALF-UP') + for move in (self.move_raw_ids | self.move_finished_ids.filtered(lambda m: m.product_id != self.product_id)): + if move.state in ('done', 'cancel'): + continue + if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): + continue + if move.has_tracking != 'none': + continue + vals = move._update_quantity_done(self) + if vals.get('to_create'): + for res in vals['to_create']: + move.move_line_ids.create(res) + if vals.get('to_write'): + for move_line, res in vals['to_write']: + move_line.write(res) + def action_confirm(self): self._check_company() for production in self: @@ -862,13 +926,6 @@ class MrpProduction(models.Model): production.workorder_ids._refresh_wo_lines() return True - def open_produce_product(self): - self.ensure_one() - if self.bom_id.type == 'phantom': - raise UserError(_('You cannot produce a MO with a bom kit product.')) - action = self.env.ref('mrp.act_mrp_product_produce').read()[0] - return action - def button_plan(self): """ Create work orders. And probably do stuff, like things. """ orders_to_plan = self.filtered(lambda order: order.bom_id.operation_ids and order.state == 'confirmed') @@ -1122,7 +1179,7 @@ class MrpProduction(models.Model): self.ensure_one() return True - def post_inventory(self): + def _post_inventory(self, cancel_backorder=False): for order in self: moves_not_to_do = order.move_raw_ids.filtered(lambda x: x.state == 'done') moves_to_do = order.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) @@ -1132,23 +1189,26 @@ class MrpProduction(models.Model): # to get extra moves. moves_to_do = moves_to_do._action_done() moves_to_do = order.move_raw_ids.filtered(lambda x: x.state == 'done') - moves_not_to_do + + finish_moves = order.move_finished_ids.filtered(lambda m: m.product_id == order.product_id and m.state not in ('done', 'cancel')) + # the finish move can already be completed by the workorder. + if not finish_moves.quantity_done: + if order.product_tracking == 'serial': + uom = order.product_id.uom_id + finish_moves.quantity_done = order.product_uom_id._compute_quantity(order.qty_producing, uom, round='HALF-UP') + finish_moves.move_line_ids.product_uom_id = uom + else: + finish_moves.quantity_done = float_round(self.qty_producing - self.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP') + finish_moves.move_line_ids.lot_id = self.lot_producing_id order._cal_price(moves_to_do) + moves_to_finish = order.move_finished_ids.filtered(lambda x: x.state not in ('done', 'cancel')) - moves_to_finish = moves_to_finish._action_done() + moves_to_finish = moves_to_finish._action_done(cancel_backorder=cancel_backorder) order.workorder_ids.mapped('raw_workorder_line_ids').unlink() order.workorder_ids.mapped('finished_workorder_line_ids').unlink() order.action_assign() consume_move_lines = moves_to_do.mapped('move_line_ids') - for moveline in moves_to_finish.mapped('move_line_ids'): - if moveline.move_id.has_tracking != 'none' and moveline.product_id == order.product_id or moveline.lot_id in consume_move_lines.mapped('lot_produced_ids'): - if any([not ml.lot_produced_ids for ml in consume_move_lines]): - raise UserError(_('You can not consume without telling for which lot you consumed it')) - # Link all movelines in the consumed with same lot_produced_ids false or the correct lot_produced_ids - filtered_lines = consume_move_lines.filtered(lambda ml: moveline.lot_id in ml.lot_produced_ids) - moveline.write({'consume_line_ids': [(6, 0, [x for x in filtered_lines.ids])]}) - else: - # Link with everything - moveline.write({'consume_line_ids': [(6, 0, [x for x in consume_move_lines.ids])]}) + order.move_finished_ids.move_line_ids.consume_line_ids = [(6, 0, consume_move_lines.ids)] return True @api.model @@ -1189,13 +1249,10 @@ class MrpProduction(models.Model): production.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel')).write({ 'raw_material_production_id': backorder_mo.id, - 'reference': backorder_mo.name, }) backorders |= backorder_mo production.name = self._get_name_backorder(production.name, production.backorder_sequence) - # update moves references - (production.move_raw_ids | production.move_finished_ids).reference = production.name for wo in backorder_mo.workorder_ids: wo.duration_expected = wo._get_duration_expected(wo.workcenter_id) @@ -1236,8 +1293,9 @@ class MrpProduction(models.Model): productions_to_backorder = self.env['mrp.production'] self.workorder_ids.button_finish() - self._post_inventory() + productions_not_to_backorder._post_inventory(cancel_backorder=False) + productions_to_backorder._post_inventory(cancel_backorder=True) backorders = productions_to_backorder._generate_backorder_productions() # Moves without quantity done are not posted => set them as done instead of canceling. In @@ -1383,7 +1441,7 @@ class MrpProduction(models.Model): 'res_model': 'mrp.unbuild', 'view_id': self.env.ref('mrp.mrp_unbuild_form_view_simplified').id, 'type': 'ir.actions.act_window', - 'context': {'default_mo_id': self.id, + 'context': {'default_mo_id': self.id, 'default_company_id': self.company_id.id, 'default_location_id': self.location_dest_id.id, 'default_location_dest_id': self.location_src_id.id, @@ -1398,3 +1456,84 @@ class MrpProduction(models.Model): def _get_quantity_to_backorder(self): self.ensure_one() return max(self.product_qty - self.qty_produced, 0) + + def _check_sn_uniqueness(self): + """ Alert the user if the serial number as already been consumed/produced """ + if self.product_tracking == 'serial' and self.lot_producing_id: + sml = self.env['stock.move.line'].search_count([ + ('lot_id', '=', self.lot_producing_id.id), + ('location_id.usage', '=', 'production'), + ('qty_done', '=', 1), + ('state', '=', 'done') + ]) + if sml: + raise UserError(_('This serial number for product %s has already been produced') % self.product_id.name) + + for move in self.move_finished_ids: + if move.has_tracking != 'serial' or move.product_id == self.product_id: + continue + for move_line in move.move_line_ids: + domain = [ + ('lot_id', '=', move_line.lot_id.id), + ('qty_done', '=', 1), + ('state', '=', 'done') + ] + message = _('The serial number %s used for byproduct %s has already been produced') % (move_line.lot_id.name, move_line.product_id.name) + co_prod_move_lines = self.move_finished_ids.move_line_ids.filtered(lambda ml: ml.product_id != self.product_id) + domain_unbuild = domain + [ + ('production_id', '=', False), + ('location_dest_id.usage', '=', 'production') + ] + + # Check presence of same sn in previous productions + duplicates = self.env['stock.move.line'].search_count(domain + [ + ('location_id.usage', '=', 'production') + ]) + if duplicates: + # Maybe some move lines have been compensated by unbuild + duplicates_unbuild = self.env['stock.move.line'].search_count(domain_unbuild) + if not (duplicates_unbuild and duplicates - duplicates_unbuild == 0): + raise UserError(message) + # Check presence of same sn in current production + duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == move_line.lot_id) - move_line + if duplicates: + raise UserError(message) + + for move in self.move_raw_ids: + if move.has_tracking != 'serial': + continue + for move_line in move.move_line_ids: + if float_is_zero(move_line.qty_done, precision_rounding=move_line.product_uom_id.rounding): + continue + domain = [ + ('lot_id', '=', move_line.lot_id.id), + ('qty_done', '=', 1), + ('state', '=', 'done') + ] + message = _('The serial number %s used for component %s has already been consumed') % (move_line.lot_id.name, move_line.product_id.name) + co_prod_move_lines = self.move_raw_ids.move_line_ids + domain_unbuild = domain + [ + ('production_id', '=', False), + ('location_id.usage', '=', 'production') + ] + + # Check presence of same sn in previous productions + duplicates = self.env['stock.move.line'].search_count(domain + [ + ('location_dest_id.usage', '=', 'production') + ]) + if duplicates: + # Maybe some move lines have been compensated by unbuild + duplicates_unbuild = self.env['stock.move.line'].search_count(domain_unbuild) + if not (duplicates_unbuild and duplicates - duplicates_unbuild == 0): + raise UserError(message) + # Check presence of same sn in current production + duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == move_line.lot_id) - move_line + if duplicates: + raise UserError(message) + + def _strict_consumption_check(self): + if self.consumption == 'strict': + for move in self.move_raw_ids: + rounding = move.product_uom.rounding + if float_compare(move.quantity_done, move.product_uom_qty, precision_rounding=rounding) != 0: + raise UserError(_('You should consume the quantity of %s defined in the BoM. If you want to consume more or less components, change the consumption setting on the BoM.') % move.product_id.name) diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py index 40856e8c141..6d345e75763 100644 --- a/addons/mrp/models/mrp_unbuild.py +++ b/addons/mrp/models/mrp_unbuild.py @@ -184,7 +184,7 @@ class MrpUnbuild(models.Model): needed_quantity = move.product_qty moves_lines = original_move.mapped('move_line_ids') if move in produce_moves and self.lot_id: - moves_lines = moves_lines.filtered(lambda ml: self.lot_id in ml.lot_produced_ids) + moves_lines = moves_lines.filtered(lambda ml: self.lot_id in ml.produce_line_ids.lot_id) # FIXME sle: double check with arm for move_line in moves_lines: # Iterate over all move_lines until we unbuilded the correct quantity. taken_quantity = min(needed_quantity, move_line.qty_done) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 8702a292155..cf0ec783e6e 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -3,7 +3,7 @@ from odoo import api, exceptions, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import float_compare, float_round, float_is_zero +from odoo.tools import float_compare, float_round, float_repr, float_is_zero class StockMoveLine(models.Model): @@ -11,7 +11,6 @@ class StockMoveLine(models.Model): workorder_id = fields.Many2one('mrp.workorder', 'Work Order', check_company=True) production_id = fields.Many2one('mrp.production', 'Production Order', check_company=True) - lot_produced_ids = fields.Many2many('stock.production.lot', string='Finished Lot/Serial Number', check_company=True) done_move = fields.Boolean('Move Done', related='move_id.is_done', readonly=False, store=True) # TDE FIXME: naming @api.model_create_multi @@ -23,8 +22,10 @@ class StockMoveLine(models.Model): # traceability report if line.move_id.raw_material_production_id and line.state == 'done': mo = line.move_id.raw_material_production_id - if line.lot_produced_ids: - produced_move_lines = mo.move_finished_ids.move_line_ids.filtered(lambda sml: sml.lot_id in line.lot_produced_ids) + finished_lots = mo.lot_producing_id + finished_lots |= mo.move_finished_ids.filtered(lambda m: m.product_id != mo.product_id).move_line_ids.lot_id + if finished_lots: + produced_move_lines = mo.move_finished_ids.move_line_ids.filtered(lambda sml: sml.lot_id in finished_lots) line.produce_line_ids = [(6, 0, produced_move_lines.ids)] else: produced_move_lines = mo.move_finished_ids.move_line_ids @@ -45,7 +46,7 @@ class StockMoveLine(models.Model): def _reservation_is_updatable(self, quantity, reserved_quant): self.ensure_one() - if self.lot_produced_ids: + if self.produce_line_ids.lot_id: ml_remaining_qty = self.qty_done - self.product_uom_qty ml_remaining_qty = self.product_uom_id._compute_quantity(ml_remaining_qty, self.product_id.uom_id, rounding_method="HALF-UP") if float_compare(ml_remaining_qty, quantity, precision_rounding=self.product_id.uom_id.rounding) < 0: @@ -54,10 +55,6 @@ class StockMoveLine(models.Model): def write(self, vals): for move_line in self: - if move_line.move_id.production_id and 'lot_id' in vals: - move_line.production_id.move_raw_ids.mapped('move_line_ids')\ - .filtered(lambda r: not r.done_move and move_line.lot_id in r.lot_produced_ids)\ - .write({'lot_produced_ids': [(4, vals['lot_id'])]}) production = move_line.move_id.production_id or move_line.move_id.raw_material_production_id if production and move_line.state == 'done' and any(field in vals for field in ('lot_id', 'location_id', 'qty_done')): move_line._log_message(production, move_line, 'mrp.track_production_move_template', vals) @@ -95,6 +92,7 @@ class StockMove(models.Model): needs_lots = fields.Boolean('Tracking', compute='_compute_needs_lots') order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids') finished_lots_exist = fields.Boolean('Finished Lots Exist', compute='_compute_order_finished_lot_ids') + should_consume_qty = fields.Float('Quantity To Consume', compute='_compute_should_consume_qty') def _unreserve_initial_demand(self, new_move): # If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move @@ -153,22 +151,55 @@ class StockMove(models.Model): for move in self: move.is_done = (move.state in ('done', 'cancel')) - @api.depends('product_uom_qty') + @api.depends('product_uom_qty', 'raw_material_production_id', 'raw_material_production_id.product_qty', 'raw_material_production_id.qty_produced') def _compute_unit_factor(self): for move in self: mo = move.raw_material_production_id or move.production_id if mo: - move.unit_factor = (move.product_uom_qty - move.quantity_done) / ((mo.product_qty - mo.qty_produced) or 1) + move.unit_factor = move.product_uom_qty / ((mo.product_qty - mo.qty_produced) or 1) else: move.unit_factor = 1.0 + @api.depends('raw_material_production_id', 'raw_material_production_id.name', 'production_id', 'production_id.name') + def _compute_reference(self): + moves_with_reference = self.env['stock.move'] + for move in self: + if move.raw_material_production_id and move.raw_material_production_id.name: + move.reference = move.raw_material_production_id.name + moves_with_reference |= move + if move.production_id and move.production_id.name: + move.reference = move.production_id.name + moves_with_reference |= move + super(StockMove, self - moves_with_reference)._compute_reference() + + @api.depends('raw_material_production_id.qty_producing', 'product_uom_qty') + def _compute_should_consume_qty(self): + for move in self: + mo = move.raw_material_production_id + if not mo: + move.qty_summary = 0 + continue + move.should_consume_qty = mo.product_uom_id._compute_quantity((mo.qty_producing - mo.qty_produced) * move.unit_factor, mo.product_uom_id, rounding_method='HALF-UP') + + @api.onchange('product_uom_qty') + def _onchange_product_uom_qty(self): + if self.raw_material_production_id and self.has_tracking == 'none': + mo = self.raw_material_production_id + vals = self._update_quantity_done(mo) + if vals.get('to_create'): + for res in vals['to_create']: + self.move_line_ids.new(res) + if vals.get('to_write'): + for move_line, res in vals['to_write']: + move_line.update(res) + @api.model def default_get(self, fields_list): defaults = super(StockMove, self).default_get(fields_list) - if self.env.context.get('default_raw_material_production_id'): - production_id = self.env['mrp.production'].browse(self.env.context['default_raw_material_production_id']) - if production_id.state in ('confirmed', 'done'): - if production_id.state == 'confirmed': + if self.env.context.get('default_raw_material_production_id') or self.env.context.get('default_production_id'): + production_id = self.env['mrp.production'].browse(self.env.context.get('default_raw_material_production_id') or self.env.context.get('default_production_id')) + if production_id.state not in ('draft', 'cancel'): + if production_id.state != 'done': defaults['state'] = 'draft' else: defaults['state'] = 'done' @@ -225,6 +256,17 @@ class StockMove(models.Model): moves_to_return |= phantom_moves.action_explode() return moves_to_return + def action_show_details(self): + self.ensure_one() + action = super().action_show_details() + if self.raw_material_production_id: + action['views'] = [(self.env.ref('mrp.view_stock_move_operations_raw').id, 'form')] + action['context']['show_destination_location'] = False + elif self.production_id: + action['views'] = [(self.env.ref('mrp.view_stock_move_operations_finished').id, 'form')] + action['context']['show_source_location'] = False + return action + def _action_cancel(self): res = super(StockMove, self)._action_cancel() for production in self.mapped('raw_material_production_id'): @@ -328,3 +370,25 @@ class StockMove(models.Model): return min(qty_ratios) // 1 else: return 0.0 + + @api.depends('raw_material_production_id') + def _compute_is_quantity_done_editable(self): + return super()._compute_is_quantity_done_editable() + + def _show_details_in_draft(self): + self.ensure_one() + if self.raw_material_production_id and self.state == 'draft': + return True + else: + return super()._show_details_in_draft() + + def _update_quantity_done(self, mo): + self.ensure_one() + ml_values = {} + new_qty = mo.product_uom_id._compute_quantity((mo.qty_producing - mo.qty_produced) * self.unit_factor, mo.product_uom_id, rounding_method='HALF-UP') + if not self.is_quantity_done_editable: + self.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0 + ml_values = self._set_quantity_done_prepare_vals(new_qty) + else: + self.quantity_done = new_qty + return ml_values |
