summaryrefslogtreecommitdiff
path: root/addons/mrp/models
diff options
context:
space:
mode:
authorWilliam Henrotin <whe@odoo.com>2020-04-27 11:56:02 +0200
committerSimon Lejeune <sle@openerp.com>2020-06-15 16:59:41 +0200
commit0e2765f5fd0ca0079f1f024ec3a1448b6d2ea3db (patch)
tree90ea03d1aca1bf87a080775e7db07977b645c5cc /addons/mrp/models
parent1b6421285c6bc4c61be3242cea30c2813251cbc7 (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.py10
-rw-r--r--addons/mrp/models/mrp_bom.py2
-rw-r--r--addons/mrp/models/mrp_production.py203
-rw-r--r--addons/mrp/models/mrp_unbuild.py2
-rw-r--r--addons/mrp/models/stock_move.py94
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