diff --git a/addons/mrp/data/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml
index 68e28abe950c5..c168e4902eaed 100644
--- a/addons/mrp/data/mrp_demo.xml
+++ b/addons/mrp/data/mrp_demo.xml
@@ -696,14 +696,17 @@
-
-
+
+
diff --git a/addons/mrp/models/__init__.py b/addons/mrp/models/__init__.py
index e9fc93bef6ae6..db69451b2fece 100644
--- a/addons/mrp/models/__init__.py
+++ b/addons/mrp/models/__init__.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import mrp_document
+from . import mrp_abstract_workorder
from . import res_config_settings
from . import mrp_bom
from . import mrp_routing
diff --git a/addons/mrp/models/mrp_abstract_workorder.py b/addons/mrp/models/mrp_abstract_workorder.py
new file mode 100644
index 0000000000000..44fb34fdf1ee9
--- /dev/null
+++ b/addons/mrp/models/mrp_abstract_workorder.py
@@ -0,0 +1,354 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.addons import decimal_precision as dp
+from odoo.exceptions import UserError
+from odoo.tools import float_compare, float_round, float_is_zero
+
+
+class MrpAbstractWorkorder(models.AbstractModel):
+ _name = "mrp.abstract.workorder"
+ _description = "Common code between produce wizards and workorders."
+
+ production_id = fields.Many2one('mrp.production', 'Manufacturing Order', required=True)
+ product_id = fields.Many2one(related='production_id.product_id', readonly=True, store=True)
+ qty_producing = fields.Float(string='Currently Produced Quantity', digits=dp.get_precision('Product Unit of Measure'))
+ product_uom_id = fields.Many2one('uom.uom', 'Unit of Measure', required=True, readonly=True)
+ final_lot_id = fields.Many2one('stock.production.lot', string='Lot/Serial Number', domain="[('product_id', '=', product_id)]")
+ product_tracking = fields.Selection(related="product_id.tracking")
+
+ @api.onchange('qty_producing')
+ def _onchange_qty_producing(self):
+ line_values = self._update_workorder_lines()
+ for vals in line_values['to_create']:
+ self.workorder_line_ids |= self.workorder_line_ids.new(vals)
+ if line_values['to_delete']:
+ self.workorder_line_ids -= line_values['to_delete']
+ for line, vals in line_values['to_update'].items():
+ line.update(vals)
+
+ def _update_workorder_lines(self):
+ """ Update workorder lines, according to the new qty currently
+ produced. It returns a dict with line to create, update or delete.
+ It do not directly write or unlink the line because this function is
+ used in onchange and request that write on db (e.g. workorder creation).
+ """
+ line_values = {'to_create': [], 'to_delete': [], 'to_update': {}}
+ for move_raw in self.move_raw_ids.filtered(lambda move: move.state not in ('done', 'cancel')):
+ move_workorder_lines = self.workorder_line_ids.filtered(lambda w: w.move_id == move_raw)
+
+ # Compute the new quantity for the current component
+ rounding = move_raw.product_uom.rounding
+ new_qty = self.product_uom_id._compute_quantity(
+ self.qty_producing * move_raw.unit_factor,
+ self.production_id.product_uom_id,
+ round=False
+ )
+ qty_todo = float_round(new_qty - sum(move_workorder_lines.mapped('qty_done')), precision_rounding=rounding)
+
+ # Remove or lower quantity on exisiting workorder lines
+ if float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0:
+ qty_todo = abs(qty_todo)
+ for workorder_line in move_workorder_lines:
+ if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
+ break
+ if float_compare(workorder_line.qty_to_consume, qty_todo, precision_rounding=rounding) <= 0:
+ # update qty_todo for next wo_line
+ qty_todo = float_round(qty_todo - workorder_line.qty_to_consume, precision_rounding=rounding)
+ if line_values['to_delete']:
+ line_values['to_delete'] |= workorder_line
+ else:
+ line_values['to_delete'] = workorder_line
+ else:
+ new_val = workorder_line.qty_to_consume - qty_todo
+ line_values['to_update'][workorder_line] = {
+ 'qty_to_consume': new_val,
+ 'qty_done': new_val,
+ 'qty_reserved': new_val,
+ }
+ qty_todo = 0
+ else:
+ # Search among wo lines which one could be updated
+ for move_line in move_raw.move_line_ids:
+ # Get workorder lines that match reservation.
+ candidates = move_workorder_lines._find_candidate(move_line)
+ while candidates:
+ if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
+ break
+ candidate = candidates.pop()
+ qty_to_add = move_line.product_uom_qty - candidate.qty_done
+ line_values['to_update'][candidate] = {
+ 'qty_done': candidate.qty_done + qty_to_add,
+ 'qty_to_consume': candidate.qty_to_consume + qty_to_add,
+ 'qty_reserved': candidate.qty_reserved + qty_to_add,
+ }
+ qty_todo -= qty_to_add
+
+ # if there are still qty_todo, create new wo lines
+ if float_compare(qty_todo, 0.0, precision_rounding=rounding) > 0:
+ for vals in self._generate_lines_values(move_raw, qty_todo):
+ line_values['to_create'].append(vals)
+ return line_values
+
+ @api.model
+ def _generate_lines_values(self, move, qty_to_consume):
+ """ Create workorder line. First generate line based on the reservation,
+ in order to match the reservation. If the quantity to consume is greater
+ than the reservation quantity then create line with the correct quantity
+ to consume but without lot or serial number.
+ """
+ lines = []
+ is_tracked = move.product_id.tracking != 'none'
+ for move_line in move.move_line_ids:
+ 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_id or 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(
+ lambda line: line.product_id == move_line.product_id and
+ line.lot_id == move_line.lot_id
+ )
+ if linked_wo_line:
+ if float_compare(sum(linked_wo_line.mapped('qty_to_consume')), move_line.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0:
+ to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty - sum(linked_wo_line.mapped('qty_to_consume')))
+ else:
+ continue
+ else:
+ to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
+ line = {
+ 'move_id': move.id,
+ 'product_id': move.product_id.id,
+ 'product_uom_id': is_tracked and move.product_id.uom_id.id or move.product_uom.id,
+ 'qty_to_consume': to_consume_in_line,
+ 'qty_reserved': move_line.product_uom_qty,
+ 'lot_id': move_line.lot_id.id,
+ 'qty_done': is_tracked and 0 or to_consume_in_line
+ }
+ lines.append(line)
+ qty_to_consume -= to_consume_in_line
+ # The move has not reserved the whole quantity so we create new wo lines
+ if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
+ if move.product_id.tracking == 'serial':
+ while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
+ line = {
+ 'move_id': move.id,
+ 'product_id': move.product_id.id,
+ 'product_uom_id': move.product_id.uom_id.id,
+ 'qty_to_consume': 1,
+ 'qty_done': 1,
+ }
+ lines.append(line)
+ qty_to_consume -= 1
+ else:
+ line = {
+ 'move_id': move.id,
+ 'product_id': move.product_id.id,
+ 'product_uom_id': move.product_uom.id,
+ 'qty_to_consume': qty_to_consume,
+ 'qty_done': is_tracked and 0 or qty_to_consume
+ }
+ lines.append(line)
+ return lines
+
+ def _update_finished_move(self):
+ """ Update the finished move & move lines in order to set the finished
+ product lot on it as well as the produced quantity. This method get the
+ information either from the last workorder or from the Produce wizard."""
+ production_move = self.production_id.move_finished_ids.filtered(
+ lambda move: move.product_id == self.product_id and
+ move.state not in ('done', 'cancel')
+ )
+ if production_move and production_move.product_id.tracking != 'none':
+ if not self.final_lot_id:
+ raise UserError(_('You need to provide a lot for the finished product.'))
+ move_line = production_move.move_line_ids.filtered(
+ lambda line: line.lot_id.id == self.final_lot_id.id
+ )
+ if move_line:
+ if self.product_id.tracking == 'serial':
+ raise UserError(_('You cannot produce the same serial number twice.'))
+ move_line.product_uom_qty += self.qty_producing
+ move_line.qty_done += self.qty_producing
+ else:
+ move_line.create({
+ 'move_id': production_move.id,
+ 'product_id': production_move.product_id.id,
+ 'lot_id': self.final_lot_id.id,
+ 'product_uom_qty': self.qty_producing,
+ 'product_uom_id': self.product_uom_id.id,
+ 'qty_done': self.qty_producing,
+ 'location_id': production_move.location_id.id,
+ 'location_dest_id': production_move.location_dest_id.id,
+ })
+ else:
+ rounding = production_move.product_uom.rounding
+ production_move._set_quantity_done(
+ production_move.quantity_done + float_round(self.qty_producing, precision_rounding=rounding)
+ )
+
+ def _update_raw_moves(self):
+ """ Once the production is done, the lots written on workorder lines
+ are saved on stock move lines"""
+ vals_list = []
+ workorder_lines_to_process = self.workorder_line_ids.filtered(lambda line: line.qty_done > 0)
+ for line in workorder_lines_to_process:
+ line._update_move_lines()
+ if float_compare(line.qty_done, 0, precision_rounding=line.product_uom_id.rounding) > 0:
+ vals_list += line._create_extra_move_lines()
+
+ self.workorder_line_ids.unlink()
+ self.env['stock.move.line'].create(vals_list)
+
+
+class MrpAbstractWorkorderLine(models.AbstractModel):
+ _name = "mrp.abstract.workorder.line"
+ _description = "Abstract model to implement product_produce_line as well as\
+ workorder_line"
+
+ move_id = fields.Many2one('stock.move')
+ product_id = fields.Many2one('product.product', 'Product', required=True)
+ product_tracking = fields.Selection(related="product_id.tracking")
+ lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
+ qty_to_consume = fields.Float('To Consume', digits=dp.get_precision('Product Unit of Measure'))
+ product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure')
+ qty_done = fields.Float('Consumed')
+ qty_reserved = fields.Float('Reserved', digits=dp.get_precision('Product Unit of Measure'))
+
+ @api.onchange('lot_id')
+ def _onchange_lot_id(self):
+ """ When the user is encoding a produce line for a tracked product, we apply some logic to
+ help him. This onchange will automatically switch `qty_done` to 1.0.
+ """
+ if self.product_id.tracking == 'serial':
+ self.qty_done = 1
+
+ @api.onchange('qty_done')
+ def _onchange_qty_done(self):
+ """ When the user is encoding a produce line for a tracked product, we apply some logic to
+ help him. This onchange will warn him if he set `qty_done` to a non-supported value.
+ """
+ res = {}
+ if self.product_id.tracking == 'serial' and not float_is_zero(self.qty_done, self.product_uom_id.rounding):
+ if float_compare(self.qty_done, 1.0, precision_rounding=self.product_uom_id.rounding) != 0:
+ message = _('You can only process 1.0 %s of products with unique serial number.') % self.product_id.uom_id.name
+ res['warning'] = {'title': _('Warning'), 'message': message}
+ return res
+
+ def _update_move_lines(self):
+ """ 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_id)
+ else:
+ move_lines = self.move_id.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_produced_id)
+
+ # Sanity check: if the product is a serial number and `lot` is already present in the other
+ # consumed move lines, raise.
+ if self.product_id.tracking != 'none' and not self.lot_id:
+ raise UserError(_('Please enter a lot or serial number for %s !' % self.product_id.display_name))
+
+ if self.lot_id and self.product_id.tracking == 'serial' and self.lot_id in self.move_id.move_line_ids.filtered(lambda ml: ml.qty_done).mapped('lot_id'):
+ raise UserError(_('You cannot consume the same serial number twice. Please correct the serial numbers encoded.'))
+
+ # Update reservation and quantity done
+ for ml in move_lines:
+ rounding = ml.product_uom_id.rounding
+ if float_compare(self.qty_done, 0, precision_rounding=rounding) <= 0:
+ break
+ quantity_to_process = min(self.qty_done, ml.product_uom_qty - ml.qty_done)
+ self.qty_done -= quantity_to_process
+
+ new_quantity_done = (ml.qty_done + quantity_to_process)
+ # if we produce less than the reserved quantity to produce the finished products
+ # in different lots,
+ # we create different component_move_lines to record which one was used
+ # on which lot of finished product
+ if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0:
+ ml.write({
+ 'qty_done': new_quantity_done,
+ 'lot_produced_id': self._get_final_lot().id
+ })
+ 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_id': self._get_final_lot().id
+ }
+ ml.copy(default=default)
+ ml.with_context(bypass_reservation_update=True).write({
+ 'product_uom_qty': new_qty_reserved,
+ 'qty_done': 0
+ })
+
+ def _create_extra_move_lines(self):
+ """Create new sml if quantity produced is bigger than the reserved one"""
+ vals_list = []
+ quants = self.env['stock.quant']._gather(self.product_id, self.move_id.location_id, lot_id=self.lot_id, strict=False)
+ # Search for a sub-locations where the product is available.
+ # Loop on the quants to get the locations. If there is not enough
+ # quantity into stock, we take the move location. Anyway, no
+ # reservation is made, so it is still possible to change it afterwards.
+ for quant in quants:
+ quantity = quant.reserved_quantity - quant.quantity
+ rounding = quant.product_uom_id.rounding
+ if (float_compare(quant.quantity, 0, precision_rounding=rounding) <= 0 or
+ float_compare(quantity, 0, precision_rounding=rounding) <= 0):
+ continue
+ vals = {
+ 'move_id': self.move_id.id,
+ 'product_id': self.product_id.id,
+ 'location_id': quant.location_id.id,
+ 'location_dest_id': self.move_id.location_dest_id.id,
+ 'product_uom_qty': 0,
+ 'product_uom_id': quant.product_uom_id.id,
+ 'qty_done': min(quantity, self.qty_done),
+ 'lot_produced_id': self._get_final_lot().id,
+ }
+ if self.lot_id:
+ vals.update({'lot_id': self.lot_id.id})
+
+ vals_list.append(vals)
+ self.qty_done -= vals['qty_done']
+ # If all the qty_done is distributed, we can close the loop
+ if float_compare(self.qty_done, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
+ break
+
+ if float_compare(self.qty_done, 0, precision_rounding=self.product_uom_id.rounding) > 0:
+ vals = {
+ 'move_id': self.move_id.id,
+ 'product_id': self.product_id.id,
+ 'location_id': self.move_id.location_id.id,
+ 'location_dest_id': self.move_id.location_dest_id.id,
+ 'product_uom_qty': 0,
+ 'product_uom_id': self.product_uom_id.id,
+ 'qty_done': self.qty_done,
+ 'lot_produced_id': self._get_final_lot().id,
+ }
+ if self.lot_id:
+ vals.update({'lot_id': self.lot_id.id})
+
+ vals_list.append(vals)
+
+ return vals_list
+
+ def _find_candidate(self, move_line):
+ """ Method used in order to return move lines that match reservation.
+ The purpose is to update exisiting workorder line regarding the
+ reservation of the raw moves.
+ """
+ rounding = move_line.product_uom_id.rounding
+ return self.filtered(lambda line:
+ line.lot_id == move_line.lot_id and
+ float_compare(line.qty_done, move_line.product_uom_qty, precision_rounding=rounding) < 0 and
+ line.product_id == move_line.product_id)
+
+ # To be implemented in specific model
+ def _get_final_lot(self):
+ raise NotImplementedError('Method _get_final_lot() undefined on %s' % self)
+
+ def _get_production(self):
+ raise NotImplementedError('Method _get_production() undefined on %s' % self)
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index 4adb325c4d371..b497dca8e6af5 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -509,6 +509,7 @@ def _get_move_raw_values(self, bom_line, line_data):
data = {
'sequence': bom_line.sequence,
'name': self.name,
+ 'reference': self.name,
'date': self.date_planned_start,
'date_expected': self.date_planned_start,
'bom_line_id': bom_line.id,
@@ -715,6 +716,7 @@ def _workorders_create(self, bom, bom_data):
'name': operation.name,
'production_id': self.id,
'workcenter_id': operation.workcenter_id.id,
+ 'product_uom_id': self.product_uom_id.id,
'operation_id': operation.id,
'duration_expected': duration_expected,
'state': len(workorders) == 0 and 'ready' or 'pending',
@@ -733,7 +735,7 @@ def _workorders_create(self, bom, bom_data):
moves_raw.mapped('move_line_ids').write({'workorder_id': workorder.id})
(moves_finished + moves_raw).write({'workorder_id': workorder.id})
- workorder._generate_lot_ids()
+ workorder.generate_wo_lines()
return workorders
def _check_lots(self):
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index cc1be6ab7a83f..a892db6e098ad 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -13,31 +13,17 @@
class MrpWorkorder(models.Model):
_name = 'mrp.workorder'
_description = 'Work Order'
- _inherit = ['mail.thread', 'mail.activity.mixin']
+ _inherit = ['mail.thread', 'mail.activity.mixin', 'mrp.abstract.workorder']
name = fields.Char(
'Work Order', required=True,
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
-
workcenter_id = fields.Many2one(
'mrp.workcenter', 'Work Center', required=True,
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
working_state = fields.Selection(
'Workcenter Status', related='workcenter_id.working_state', readonly=False,
help='Technical: used in views only')
-
- production_id = fields.Many2one(
- 'mrp.production', 'Manufacturing Order',
- index=True, ondelete='cascade', required=True, tracking=True,
- states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
- product_id = fields.Many2one(
- 'product.product', 'Product',
- related='production_id.product_id', readonly=True,
- help='Technical: used in views only.', store=True)
- product_uom_id = fields.Many2one(
- 'uom.uom', 'Unit of Measure',
- related='production_id.product_uom_id', readonly=True,
- help='Technical: used in views only.')
production_availability = fields.Selection(
'Stock Availability', readonly=True,
related='production_id.reservation_state', store=True,
@@ -46,9 +32,6 @@ class MrpWorkorder(models.Model):
'Production State', readonly=True,
related='production_id.state',
help='Technical: used in views only.')
- product_tracking = fields.Selection(
- 'Product Tracking', related='production_id.product_id.tracking', readonly=False,
- help='Technical: used in views only.')
qty_production = fields.Float('Original Production Quantity', readonly=True, related='production_id.product_qty')
qty_remaining = fields.Float('Quantity To Be Produced', compute='_compute_qty_remaining', digits=dp.get_precision('Product Unit of Measure'))
qty_produced = fields.Float(
@@ -56,13 +39,8 @@ class MrpWorkorder(models.Model):
readonly=True,
digits=dp.get_precision('Product Unit of Measure'),
help="The number of products already handled by this work order")
- qty_producing = fields.Float(
- 'Currently Produced Quantity', default=1.0,
- digits=dp.get_precision('Product Unit of Measure'),
- states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
is_produced = fields.Boolean(string="Has Been Produced",
compute='_compute_is_produced')
-
state = fields.Selection([
('pending', 'Waiting for another WO'),
('ready', 'Ready'),
@@ -105,11 +83,7 @@ class MrpWorkorder(models.Model):
'stock.move', 'workorder_id', 'Moves')
move_line_ids = fields.One2many(
'stock.move.line', 'workorder_id', 'Moves to Track',
- domain=[('done_wo', '=', True)],
help="Inventory moves for which you must scan a lot number at this work order")
- active_move_line_ids = fields.One2many(
- 'stock.move.line', 'workorder_id',
- domain=[('done_wo', '=', False)])
final_lot_id = fields.Many2one(
'stock.production.lot', 'Lot/Serial Number', domain="[('product_id', '=', product_id)]",
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
@@ -129,6 +103,7 @@ class MrpWorkorder(models.Model):
capacity = fields.Float(
'Capacity', default=1.0,
help="Number of pieces that can be produced in parallel.")
+ workorder_line_ids = fields.One2many('mrp.workorder.line', 'workorder_id', string='Workorder lines')
@api.multi
def name_get(self):
@@ -179,55 +154,6 @@ def _compute_color(self):
for order in (self - late_orders):
order.color = 2
- @api.onchange('qty_producing')
- def _onchange_qty_producing(self):
- """ Update stock.move.lot records, according to the new qty currently
- produced. """
- moves = self.move_raw_ids.filtered(lambda move: move.state not in ('done', 'cancel') and move.product_id.tracking != 'none' and move.product_id.id != self.production_id.product_id.id)
- for move in moves:
- move_lots = self.active_move_line_ids.filtered(lambda move_lot: move_lot.move_id == move)
- if not move_lots:
- continue
- rounding = move.product_uom.rounding
- new_qty = float_round(move.unit_factor * self.qty_producing, precision_rounding=rounding)
- if move.product_id.tracking == 'lot':
- move_lots[0].product_qty = new_qty
- move_lots[0].qty_done = new_qty
- elif move.product_id.tracking == 'serial':
- # Create extra pseudo record
- qty_todo = float_round(new_qty - sum(move_lots.mapped('qty_done')), precision_rounding=rounding)
- if float_compare(qty_todo, 0.0, precision_rounding=rounding) > 0:
- while float_compare(qty_todo, 0.0, precision_rounding=rounding) > 0:
- self.active_move_line_ids += self.env['stock.move.line'].new({
- 'move_id': move.id,
- 'product_id': move.product_id.id,
- 'lot_id': False,
- 'product_uom_qty': 0.0,
- 'product_uom_id': move.product_uom.id,
- 'qty_done': min(1.0, qty_todo),
- 'workorder_id': self.id,
- 'done_wo': False,
- 'location_id': move.location_id.id,
- 'location_dest_id': move.location_dest_id.id,
- 'date': move.date,
- })
- qty_todo -= 1
- elif float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0:
- qty_todo = abs(qty_todo)
- for move_lot in move_lots:
- if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
- break
- if not move_lot.lot_id and float_compare(qty_todo, move_lot.qty_done, precision_rounding=rounding) >= 0:
- qty_todo = float_round(qty_todo - move_lot.qty_done, precision_rounding=rounding)
- self.active_move_line_ids -= move_lot # Difference operator
- else:
- #move_lot.product_qty = move_lot.product_qty - qty_todo
- if float_compare(move_lot.qty_done - qty_todo, 0, precision_rounding=rounding) == 1:
- move_lot.qty_done = move_lot.qty_done - qty_todo
- else:
- move_lot.qty_done = 0
- qty_todo = 0
-
@api.multi
def write(self, values):
if list(values.keys()) != ['time_ids'] and any(workorder.state == 'done' for workorder in self):
@@ -240,42 +166,20 @@ def write(self, values):
raise UserError(_('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'))
return super(MrpWorkorder, self).write(values)
- def _generate_lot_ids(self):
- """ Generate stock move lines """
+ def generate_wo_lines(self):
+ """ Generate workorder line """
self.ensure_one()
- MoveLine = self.env['stock.move.line']
- tracked_moves = self.move_raw_ids.filtered(
- lambda move: move.state not in ('done', 'cancel') and move.product_id.tracking != 'none' and move.product_id != self.production_id.product_id)
- for move in tracked_moves:
- qty = move.unit_factor * self.qty_producing
- if move.product_id.tracking == 'serial':
- while float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) > 0:
- MoveLine.create({
- 'move_id': move.id,
- 'product_uom_qty': 0,
- 'product_uom_id': move.product_uom.id,
- 'qty_done': min(1, qty),
- 'production_id': self.production_id.id,
- 'workorder_id': self.id,
- 'product_id': move.product_id.id,
- 'done_wo': False,
- 'location_id': move.location_id.id,
- 'location_dest_id': move.location_dest_id.id,
- })
- qty -= 1
- else:
- MoveLine.create({
- 'move_id': move.id,
- 'product_uom_qty': 0,
- 'product_uom_id': move.product_uom.id,
- 'qty_done': qty,
- 'product_id': move.product_id.id,
- 'production_id': self.production_id.id,
- 'workorder_id': self.id,
- 'done_wo': False,
- 'location_id': move.location_id.id,
- 'location_dest_id': move.location_dest_id.id,
- })
+ raw_moves = self.move_raw_ids.filtered(
+ lambda move: move.state not in ('done', 'cancel')
+ )
+ for move in raw_moves:
+ qty_to_consume = move.product_uom._compute_quantity(
+ self.qty_producing * move.unit_factor,
+ move.product_id.uom_id,
+ round=False
+ )
+ line_values = self._generate_lines_values(move, qty_to_consume)
+ self.workorder_line_ids |= self.env['mrp.workorder.line'].create(line_values)
def _assign_default_final_lot_id(self):
self.final_lot_id = self.env['stock.production.lot'].search([('use_next_on_work_order_id', '=', self.id)],
@@ -288,7 +192,6 @@ def _get_byproduct_move_line(self, by_product_move, quantity):
'product_uom_qty': quantity,
'product_uom_id': by_product_move.product_uom.id,
'qty_done': quantity,
- 'workorder_id': self.id,
'location_id': by_product_move.location_id.id,
'location_dest_id': by_product_move.location_dest_id.id,
}
@@ -305,89 +208,22 @@ def record_production(self):
if self.qty_producing <= 0:
raise UserError(_('Please set the quantity you are currently producing. It should be different from zero.'))
- if (self.production_id.product_id.tracking != 'none') and not self.final_lot_id and self.move_raw_ids:
- raise UserError(_('You should provide a lot/serial number for the final product.'))
-
- # Update quantities done on each raw material line
- # For each untracked component without any 'temporary' move lines,
- # (the new workorder tablet view allows registering consumed quantities for untracked components)
- # we assume that only the theoretical quantity was used
- for move in self.move_raw_ids:
- if move.has_tracking == 'none' and (move.state not in ('done', 'cancel'))\
- and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo):
- rounding = move.product_uom.rounding
- if self.product_id.tracking != 'none':
- qty_to_add = float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
- move._generate_consumed_move_line(qty_to_add, self.final_lot_id)
- elif len(move._get_move_lines()) < 2:
- move.quantity_done += float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
- else:
- move._set_quantity_done(move.quantity_done + float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding))
-
- # Transfer quantities from temporary to final move lots or make them final
- for move_line in self.active_move_line_ids:
- # Check if move_line already exists
- if move_line.qty_done <= 0: # rounding...
- move_line.sudo().unlink()
- continue
- if move_line.product_id.tracking != 'none' and not move_line.lot_id:
- raise UserError(_('You should provide a lot/serial number for a component.'))
- # Search other move_line where it could be added:
- lots = self.move_line_ids.filtered(lambda x: (x.lot_id.id == move_line.lot_id.id) and (not x.lot_produced_id) and (not x.done_move) and (x.product_id == move_line.product_id))
- if lots:
- lots[0].qty_done += move_line.qty_done
- lots[0].lot_produced_id = self.final_lot_id.id
- self._link_to_quality_check(move_line, lots[0])
- move_line.sudo().unlink()
- else:
- move_line.lot_produced_id = self.final_lot_id.id
- move_line.done_wo = True
-
# One a piece is produced, you can launch the next work order
if self.next_work_order_id.state == 'pending':
self.next_work_order_id.state = 'ready'
- self.move_line_ids.filtered(
- lambda move_line: not move_line.done_move and not move_line.lot_produced_id and move_line.qty_done > 0
- ).write({
- 'lot_produced_id': self.final_lot_id.id,
- 'lot_produced_qty': self.qty_producing
- })
-
# If last work order, then post lots used
# TODO: should be same as checking if for every workorder something has been done?
if not self.next_work_order_id:
- production_move = self.production_id.move_finished_ids.filtered(
- lambda x: (x.product_id.id == self.production_id.product_id.id) and (x.state not in ('done', 'cancel')))
- if production_move.product_id.tracking != 'none':
- move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
- if move_line:
- move_line.product_uom_qty += self.qty_producing
- move_line.qty_done += self.qty_producing
- else:
- move_line.create({'move_id': production_move.id,
- 'product_id': production_move.product_id.id,
- 'lot_id': self.final_lot_id.id,
- 'product_uom_qty': self.qty_producing,
- 'product_uom_id': production_move.product_uom.id,
- 'qty_done': self.qty_producing,
- 'workorder_id': self.id,
- 'location_id': production_move.location_id.id,
- 'location_dest_id': production_move.location_dest_id.id,
- })
- else:
- production_move.quantity_done += self.qty_producing
+ uom_id = self.production_id.product_uom_id
+ self._update_finished_move()
+ self.production_id.move_finished_ids.filtered(
+ lambda move: move.product_id == self.product_id and
+ move.state not in ('done', 'cancel')
+ ).workorder_id = self.id
- if not self.next_work_order_id:
- for by_product_move in self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))):
- if by_product_move.has_tracking != 'serial':
- values = self._get_byproduct_move_line(by_product_move, self.qty_producing * by_product_move.unit_factor)
- self.env['stock.move.line'].create(values)
- elif by_product_move.has_tracking == 'serial':
- qty_todo = by_product_move.product_uom._compute_quantity(self.qty_producing * by_product_move.unit_factor, by_product_move.product_id.uom_id)
- for i in range(0, int(float_round(qty_todo, precision_digits=0))):
- values = self._get_byproduct_move_line(by_product_move, 1)
- self.env['stock.move.line'].create(values)
+ # Transfer quantities from temporary to final move line or make them final
+ self._update_raw_moves()
# Update workorder quantity produced
self.qty_produced += self.qty_producing
@@ -403,10 +239,10 @@ def record_production(self):
elif self.production_id.product_id.tracking == 'serial':
self._assign_default_final_lot_id()
self.qty_producing = 1.0
- self._generate_lot_ids()
+ self.generate_wo_lines()
else:
self.qty_producing = float_round(self.production_id.product_qty - self.qty_produced, precision_rounding=rounding)
- self._generate_lot_ids()
+ self.generate_wo_lines()
if self.next_work_order_id and self.production_id.product_id.tracking != 'none':
self.next_work_order_id._assign_default_final_lot_id()
@@ -541,3 +377,17 @@ def action_see_move_scrap(self):
def _compute_qty_remaining(self):
for wo in self:
wo.qty_remaining = float_round(wo.qty_production - wo.qty_produced, precision_rounding=wo.production_id.product_uom_id.rounding)
+
+
+class MrpWorkorderLine(models.Model):
+ _name = 'mrp.workorder.line'
+ _inherit = ["mrp.abstract.workorder.line"]
+ _description = "Workorder move line"
+
+ workorder_id = fields.Many2one('mrp.workorder', 'Workorder')
+
+ def _get_final_lot(self):
+ return self.workorder_id.final_lot_id
+
+ def _get_production(self):
+ return self.workorder_id.production_id
diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py
index 7d079afe6fd55..34c3269e7c76f 100644
--- a/addons/mrp/models/stock_move.py
+++ b/addons/mrp/models/stock_move.py
@@ -225,65 +225,6 @@ def _generate_move_phantom(self, bom_line, product_qty, quantity_done):
return move
return self.env['stock.move']
- def _generate_consumed_move_line(self, qty_to_add, final_lot, lot=False):
- if lot:
- move_lines = self.move_line_ids.filtered(lambda ml: ml.lot_id == lot and not ml.lot_produced_id)
- else:
- move_lines = self.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_produced_id)
-
- # Sanity check: if the product is a serial number and `lot` is already present in the other
- # consumed move lines, raise.
- if lot and self.product_id.tracking == 'serial' and lot in self.move_line_ids.filtered(lambda ml: ml.qty_done).mapped('lot_id'):
- raise UserError(_('You cannot consume the same serial number twice. Please correct the serial numbers encoded.'))
-
- for ml in move_lines:
- rounding = ml.product_uom_id.rounding
- if float_compare(qty_to_add, 0, precision_rounding=rounding) <= 0:
- break
- quantity_to_process = min(qty_to_add, ml.product_uom_qty - ml.qty_done)
- qty_to_add -= quantity_to_process
-
- new_quantity_done = (ml.qty_done + quantity_to_process)
- if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0:
- ml.write({'qty_done': new_quantity_done, 'lot_produced_id': final_lot.id})
- 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_id': final_lot.id}
- ml.copy(default=default)
- ml.with_context(bypass_reservation_update=True).write({'product_uom_qty': new_qty_reserved, 'qty_done': 0})
-
- if float_compare(qty_to_add, 0, precision_rounding=self.product_uom.rounding) > 0:
- # Search for a sub-location where the product is available. This might not be perfectly
- # correct if the quantity available is spread in several sub-locations, but at least
- # we should be closer to the reality. Anyway, no reservation is made, so it is still
- # possible to change it afterwards.
- quants = self.env['stock.quant']._gather(self.product_id, self.location_id, lot_id=lot, strict=False)
- available_quantity = self.product_id.uom_id._compute_quantity(
- self.env['stock.quant']._get_available_quantity(
- self.product_id, self.location_id, lot_id=lot, strict=False
- ), self.product_uom
- )
- location_id = False
- if float_compare(qty_to_add, available_quantity, precision_rounding=self.product_uom.rounding) < 0:
- location_id = quants.filtered(lambda r: r.quantity > 0)[-1:].location_id
-
- vals = {
- 'move_id': self.id,
- 'product_id': self.product_id.id,
- 'location_id': location_id.id if location_id else self.location_id.id,
- 'production_id': self.raw_material_production_id.id,
- 'location_dest_id': self.location_dest_id.id,
- 'product_uom_qty': 0,
- 'product_uom_id': self.product_uom.id,
- 'qty_done': qty_to_add,
- 'lot_produced_id': final_lot.id,
- }
- if lot:
- vals.update({'lot_id': lot.id})
- self.env['stock.move.line'].create(vals)
-
def _get_upstream_documents_and_responsibles(self, visited):
if self.created_production_id and self.created_production_id.state not in ('done', 'cancel'):
return [(self.created_production_id, self.created_production_id.user_id, visited)]
diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv
index 0f48edfa18d13..23b010a67b8c4 100644
--- a/addons/mrp/security/ir.model.access.csv
+++ b/addons/mrp/security/ir.model.access.csv
@@ -28,6 +28,8 @@ access_product_supplierinfo_user,product.supplierinfo user,product.model_product
access_res_partner,res.partner,base.model_res_partner,mrp.group_mrp_user,1,0,0,0
access_mrp_workorder_mrp_user,mrp.workorder.user,model_mrp_workorder,mrp.group_mrp_user,1,1,1,1
access_mrp_workorder_mrp_manager,mrp.workorder,model_mrp_workorder,mrp.group_mrp_manager,1,1,1,1
+access_mrp_workorder_line_mrp_user,mrp.workorder.user,model_mrp_workorder_line,mrp.group_mrp_user,1,1,1,1
+access_mrp_workorder_line_mrp_manager,mrp.workorder,model_mrp_workorder_line,mrp.group_mrp_manager,1,1,1,1
access_resource_calendar_leaves_user,mrp.resource.calendar.leaves.user,resource.model_resource_calendar_leaves,mrp.group_mrp_user,1,1,1,1
access_resource_calendar_leaves_manager,mrp.resource.calendar.leaves.manager,resource.model_resource_calendar_leaves,mrp.group_mrp_manager,1,0,0,0
access_resource_calendar_attendance_mrp_user,mrp.resource.calendar.attendance.mrp.user,resource.model_resource_calendar_attendance,mrp.group_mrp_user,1,1,1,1
diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py
index ac4ec19b748a1..2935b6b47f2b5 100644
--- a/addons/mrp/tests/test_order.py
+++ b/addons/mrp/tests/test_order.py
@@ -143,7 +143,7 @@ def test_basic(self):
'active_id': man_order.id,
'active_ids': [man_order.id],
}))
- produce_form.product_qty = 1.0
+ produce_form.qty_producing = 1.0
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -412,7 +412,7 @@ def test_multiple_post_inventory(self):
# produce one item, call `post_inventory`
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
produce_form = Form(self.env['mrp.product.produce'].with_context(context))
- produce_form.product_qty = 1.00
+ produce_form.qty_producing = 1.00
custom_laptop_produce = produce_form.save()
custom_laptop_produce.do_produce()
mo_custom_laptop.post_inventory()
@@ -425,7 +425,7 @@ def test_multiple_post_inventory(self):
# produce the second item, call `post_inventory`
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
produce_form = Form(self.env['mrp.product.produce'].with_context(context))
- produce_form.product_qty = 1.00
+ produce_form.qty_producing = 1.00
custom_laptop_produce = produce_form.save()
custom_laptop_produce.do_produce()
mo_custom_laptop.post_inventory()
@@ -458,7 +458,7 @@ def test_rounding(self):
'active_id': production.id,
'active_ids': [production.id],
}))
- produce_form.product_qty = 8
+ produce_form.qty_producing = 8
produce_wizard = produce_form.save()
produce_wizard.do_produce()
self.assertEqual(production.move_raw_ids[0].quantity_done, 16, 'Should use half-up rounding when producing')
@@ -480,10 +480,9 @@ def test_product_produce_1(self):
'active_ids': [mo.id],
}))
product_produce = produce_form.save()
+ self.assertEqual(len(product_produce.workorder_line_ids), 2, 'You should have produce lines even the consumed products are not tracked.')
product_produce.do_produce()
- self.assertEqual(len(product_produce.produce_line_ids), 2, 'You should have produce lines even the consumed products are not tracked.')
-
def test_product_produce_2(self):
""" Check that line are created when the consumed products are
tracked by serial and the lot proposed are correct. """
@@ -511,9 +510,9 @@ def test_product_produce_2(self):
}))
product_produce = produce_form.save()
- self.assertEqual(len(product_produce.produce_line_ids), 3, 'You should have 3 produce lines. One for each serial to consume')
- product_produce.product_qty = 1
- produce_line_1 = product_produce.produce_line_ids[0]
+ self.assertEqual(len(product_produce.workorder_line_ids), 3, 'You should have 3 produce lines. One for each serial to consume')
+ product_produce.qty_producing = 1
+ produce_line_1 = product_produce.workorder_line_ids[0]
produce_line_1.qty_done = 1
remaining_lot = (lot_p1_1 | lot_p1_2) - produce_line_1.lot_id
product_produce.do_produce()
@@ -523,8 +522,8 @@ def test_product_produce_2(self):
'active_ids': [mo.id],
}))
product_produce = produce_form.save()
- self.assertEqual(len(product_produce.produce_line_ids), 2, 'You should have 2 produce lines since one has already be consumed.')
- for line in product_produce.produce_line_ids.filtered(lambda x: x.lot_id):
+ self.assertEqual(len(product_produce.workorder_line_ids), 2, 'You should have 2 produce lines since one has already be consumed.')
+ for line in product_produce.workorder_line_ids.filtered(lambda x: x.lot_id):
self.assertEqual(line.lot_id, remaining_lot, 'Wrong lot proposed.')
def test_product_produce_3(self):
@@ -560,15 +559,15 @@ def test_product_produce_3(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 1.0
+ produce_form.qty_producing = 1.0
product_produce = produce_form.save()
- product_produce.lot_id = final_product_lot.id
+ product_produce.final_lot_id = final_product_lot.id
# product 1 lot 1 shelf1
# product 1 lot 1 shelf2
# product 1 lot 2
- self.assertEqual(len(product_produce.produce_line_ids), 4, 'You should have 4 produce lines. lot 1 shelf_1, lot 1 shelf_2, lot2 and for product which have tracking None')
+ self.assertEqual(len(product_produce.workorder_line_ids), 4, 'You should have 4 produce lines. lot 1 shelf_1, lot 1 shelf_2, lot2 and for product which have tracking None')
- for produce_line in product_produce.produce_line_ids:
+ for produce_line in product_produce.workorder_line_ids:
produce_line.qty_done = produce_line.qty_to_consume + 1
product_produce.do_produce()
@@ -614,9 +613,10 @@ def test_product_produce_4(self):
'active_id': mo.id,
'active_ids': [mo.id],
}).create({
- 'product_qty': 1.0,
+ 'qty_producing': 1.0,
})
- product_produce._onchange_product_qty()
+ line_values = product_produce._update_workorder_lines()
+ product_produce.workorder_line_ids |= product_produce.workorder_line_ids.create(line_values['to_create'])
product_produce.do_produce()
ml_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1).mapped('move_line_ids')
@@ -658,11 +658,11 @@ def test_product_produce_5(self):
'active_id': mo.id,
'active_ids': [mo.id],
}).create({
- 'product_qty': 5.0,
+ 'qty_producing': 5.0,
})
- produce_wizard._onchange_product_qty()
+ produce_wizard._onchange_qty_producing()
- for produce_line in produce_wizard.produce_line_ids:
+ for produce_line in produce_wizard.workorder_line_ids:
produce_line.qty_done = produce_line.qty_to_consume
produce_wizard.do_produce()
@@ -705,11 +705,11 @@ def test_product_produce_uom(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.lot_id = final_product_lot
+ produce_form.final_lot_id = final_product_lot
product_produce = produce_form.save()
- self.assertEqual(product_produce.product_qty, 1)
+ self.assertEqual(product_produce.qty_producing, 1)
self.assertEqual(product_produce.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
- product_produce.lot_id = final_product_lot.id
+ product_produce.final_lot_id = final_product_lot.id
product_produce.do_produce()
move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done)
diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py
index 522ef94342915..a6a8319d4ea0a 100644
--- a/addons/mrp/tests/test_procurement.py
+++ b/addons/mrp/tests/test_procurement.py
@@ -70,7 +70,7 @@ def test_procurement(self):
'active_id': produce_product_4.id,
'active_ids': [produce_product_4.id],
}))
- produce_form.product_qty = produce_product_4.product_qty
+ produce_form.qty_producing = produce_product_4.product_qty
product_produce = produce_form.save()
product_produce.do_produce()
produce_product_4.post_inventory()
@@ -96,7 +96,7 @@ def test_procurement(self):
'active_id': production_product_6.id,
'active_ids': [production_product_6.id],
}))
- produce_form.product_qty = production_product_6.product_qty
+ produce_form.qty_producing = production_product_6.product_qty
product_produce = produce_form.save()
product_produce.do_produce()
production_product_6.post_inventory()
diff --git a/addons/mrp/tests/test_traceability.py b/addons/mrp/tests/test_traceability.py
index 4b7918bb0ca32..7f94f17ba8c15 100644
--- a/addons/mrp/tests/test_traceability.py
+++ b/addons/mrp/tests/test_traceability.py
@@ -70,10 +70,10 @@ def test_tracking_types_on_mo(self):
}))
if finished_product.tracking != 'serial':
- produce_form.product_qty = 1
+ produce_form.qty_producing = 1
if finished_product.tracking != 'none':
- produce_form.lot_id = self.env['stock.production.lot'].create({'name': 'Serial or Lot finished', 'product_id': finished_product.id})
+ produce_form.final_lot_id = self.env['stock.production.lot'].create({'name': 'Serial or Lot finished', 'product_id': finished_product.id})
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -113,4 +113,3 @@ def test_tracking_types_on_mo(self):
unfoldable,
'Parts with tracking type "%s", should have be unfoldable : %s' % (tracking, unfoldable)
)
-
diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py
index 19dde75622b91..28a436c9869c1 100644
--- a/addons/mrp/tests/test_unbuild.py
+++ b/addons/mrp/tests/test_unbuild.py
@@ -28,7 +28,7 @@ def test_unbuild_standart(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 5.0
+ produce_form.qty_producing = 5.0
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -100,8 +100,8 @@ def test_unbuild_with_final_lot(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 5.0
- produce_form.lot_id = lot
+ produce_form.qty_producing = 5.0
+ produce_form.final_lot_id = lot
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -185,7 +185,7 @@ def test_unbuild_with_comnsumed_lot(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 5.0
+ produce_form.qty_producing = 5.0
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -275,8 +275,8 @@ def test_unbuild_with_everything_tracked(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 5.0
- produce_form.lot_id = lot_final
+ produce_form.qty_producing = 5.0
+ produce_form.final_lot_id = lot_final
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -372,7 +372,7 @@ def test_unbuild_with_duplicate_move(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 5.0
+ produce_form.qty_producing = 5.0
produce_wizard = produce_form.save()
produce_wizard.do_produce()
@@ -398,8 +398,8 @@ def test_unbuild_with_duplicate_move(self):
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_1), 1, 'You should have get your product with lot 1 in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_2), 3, 'You should have the 3 basic product for lot 2 in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_3), 2, 'You should have get one product back for lot 3')
-
-
+
+
def test_production_links_with_non_tracked_lots(self):
""" This test produces an MO in two times and checks that the move lines are linked in a correct way
"""
@@ -408,43 +408,43 @@ def test_production_links_with_non_tracked_lots(self):
'name': 'lot_1',
'product_id': p2.id,
})
-
+
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot_1)
lot_finished_1 = self.env['stock.production.lot'].create({
'name': 'lot_finished_1',
'product_id': p_final.id,
})
-
+
produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 3.0
- produce_form.lot_id = lot_finished_1
+ produce_form.qty_producing = 3.0
+ produce_form.final_lot_id = lot_finished_1
produce_wizard = produce_form.save()
- produce_wizard.produce_line_ids[0].lot_id = lot_1
+ produce_wizard.workorder_line_ids[0].lot_id = lot_1
produce_wizard.do_produce()
-
+
lot_2 = self.env['stock.production.lot'].create({
'name': 'lot_2',
'product_id': p2.id,
})
-
+
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 4, lot_id=lot_2)
lot_finished_2 = self.env['stock.production.lot'].create({
'name': 'lot_finished_2',
'product_id': p_final.id,
})
-
+
produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 2.0
- produce_form.lot_id = lot_finished_2
+ produce_form.qty_producing = 2.0
+ produce_form.final_lot_id = lot_finished_2
produce_wizard = produce_form.save()
- produce_wizard.produce_line_ids[0].lot_id = lot_2
+ produce_wizard.workorder_line_ids[0].lot_id = lot_2
produce_wizard.do_produce()
mo.button_mark_done()
ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and m.lot_produced_id == lot_finished_1)
@@ -453,9 +453,9 @@ def test_production_links_with_non_tracked_lots(self):
self.assertEqual(ml[0].qty_done, 8.0, 'Should have consumed 8 for the second lot')
def test_unbuild_with_routes(self):
- """ This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock
+ """ This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock
is created with Warehouse -> True.
- The unbuild order should revert the consumed components into QC/Unbuild location for quality check
+ The unbuild order should revert the consumed components into QC/Unbuild location for quality check
and then a picking should be generated for transferring components from QC/Unbuild location to stock.
"""
StockQuant = self.env['stock.quant']
@@ -528,7 +528,7 @@ def test_unbuild_with_routes(self):
'active_id': mo.id,
'active_ids': [mo.id],
}))
- produce_form.product_qty = 1.0
+ produce_form.qty_producing = 1.0
produce_wizard = produce_form.save()
produce_wizard.do_produce()
diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
index f02d9bba70f45..a0d78d3cc78d5 100644
--- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
+++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
@@ -187,7 +187,7 @@ def test_manufacturing_flow(self):
'active_id': production_order.id,
'active_ids': [production_order.id],
}))
- produce_form.product_qty = production_order.product_qty
+ produce_form.qty_producing = production_order.product_qty
product_produce = produce_form.save()
product_produce.do_produce()
production_order.button_mark_done()
diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py
index fcfce68733da5..b182ff72c032a 100644
--- a/addons/mrp/tests/test_workorder_operation.py
+++ b/addons/mrp/tests/test_workorder_operation.py
@@ -94,13 +94,13 @@ def test_00_workorder_process(self):
finished_lot =self.env['stock.production.lot'].create({'product_id': production_table.product_id.id})
workorder.write({'final_lot_id': finished_lot.id})
workorder.button_start()
- for active_move_line_id in workorder.active_move_line_ids:
- if active_move_line_id.product_id.id == product_bolt.id:
- active_move_line_id.write({'lot_id': lot_bolt.id, 'qty_done': 1})
- if active_move_line_id.product_id.id == product_table_sheet.id:
- active_move_line_id.write({'lot_id': lot_sheet.id, 'qty_done': 1})
- if active_move_line_id.product_id.id == product_table_leg.id:
- active_move_line_id.write({'lot_id': lot_leg.id, 'qty_done': 1})
+ for workorder_line_id in workorder.workorder_line_ids:
+ if workorder_line_id.product_id.id == product_bolt.id:
+ workorder_line_id.write({'lot_id': lot_bolt.id, 'qty_done': 1})
+ if workorder_line_id.product_id.id == product_table_sheet.id:
+ workorder_line_id.write({'lot_id': lot_sheet.id, 'qty_done': 1})
+ if workorder_line_id.product_id.id == product_table_leg.id:
+ workorder_line_id.write({'lot_id': lot_leg.id, 'qty_done': 1})
self.assertEqual(workorder.state, 'progress')
workorder.record_production()
self.assertEqual(workorder.state, 'done')
@@ -197,7 +197,7 @@ def test_00b_workorder_process(self):
finished_lot = self.env['stock.production.lot'].create({'product_id': production_table.product_id.id})
workorders[0].write({'final_lot_id': finished_lot.id, 'qty_producing': 1.0})
workorders[0].button_start()
- workorders[0].active_move_line_ids[0].write({'lot_id': lot_sheet.id, 'qty_done': 1})
+ workorders[0].workorder_line_ids[0].write({'lot_id': lot_sheet.id, 'qty_done': 1})
self.assertEqual(workorders[0].state, 'progress')
workorders[0].record_production()
@@ -209,7 +209,7 @@ def test_00b_workorder_process(self):
# ---------------------------------------------------------
workorders[1].button_start()
workorders[1].qty_producing = 1.0
- workorders[1].active_move_line_ids[0].write({'lot_id': lot_leg.id, 'qty_done': 4})
+ workorders[1].workorder_line_ids[0].write({'lot_id': lot_leg.id, 'qty_done': 4})
workorders[1].record_production()
move_leg = production_table.move_raw_ids.filtered(lambda p: p.product_id == product_table_leg)
#self.assertEqual(workorders[1].state, 'done')
@@ -220,7 +220,7 @@ def test_00b_workorder_process(self):
# ---------------------------------------------------------
workorders[2].button_start()
workorders[2].qty_producing = 1.0
- move_lot = workorders[2].active_move_line_ids[0]
+ move_lot = workorders[2].workorder_line_ids[0]
move_lot.write({'lot_id': lot_bolt.id, 'qty_done': 4})
move_table_bolt = production_table.move_raw_ids.filtered(lambda p: p.product_id.id == product_bolt.id)
workorders[2].record_production()
@@ -338,11 +338,11 @@ def test_01_without_workorder(self):
# Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger)
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
product_form = Form(self.env['mrp.product.produce'].with_context(context))
- product_form.product_qty = 6.00
+ product_form.qty_producing = 6.00
laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- product_form.lot_id = laptop_lot_001
+ product_form.final_lot_id = laptop_lot_001
product_consume = product_form.save()
- product_consume.produce_line_ids[0].qty_done = 12
+ product_consume.workorder_line_ids[0].qty_done = 12
product_consume.do_produce()
# Check consumed move after produce 6 quantity of customized laptop.
@@ -365,12 +365,12 @@ def test_01_without_workorder(self):
# Produce 4 Unit of custom laptop will consume ( 8 Unit of keybord and 8 Unit of charger).
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
produce_form = Form(self.env['mrp.product.produce'].with_context(context))
- produce_form.product_qty = 4.00
+ produce_form.qty_producing = 4.00
laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- produce_form.lot_id = laptop_lot_002
+ produce_form.final_lot_id = laptop_lot_002
product_consume = produce_form.save()
- self.assertEquals(len(product_consume.produce_line_ids), 2)
- product_consume.produce_line_ids[0].qty_done = 8
+ self.assertEquals(len(product_consume.workorder_line_ids), 2)
+ product_consume.workorder_line_ids[0].qty_done = 8
product_consume.do_produce()
charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done')
keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done')
@@ -504,13 +504,13 @@ def test_02_different_uom_on_bomlines(self):
mo_custom_product.action_assign()
context = {"active_ids": [mo_custom_product.id], "active_id": mo_custom_product.id}
produce_form = Form(self.env['mrp.product.produce'].with_context(context))
- produce_form.product_qty = 10.00
- produce_form.lot_id = lot_a
+ produce_form.qty_producing = 10.00
+ produce_form.final_lot_id = lot_a
product_consume = produce_form.save()
# laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- self.assertEquals(len(product_consume.produce_line_ids), 2)
- product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000})
- product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20})
+ self.assertEquals(len(product_consume.workorder_line_ids), 2)
+ product_consume.workorder_line_ids.filtered(lambda x: x.product_id == product_C).write({'qty_done': 3000})
+ product_consume.workorder_line_ids.filtered(lambda x: x.product_id == product_B).write({'qty_done': 20})
product_consume.do_produce()
mo_custom_product.post_inventory()
diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml
index 570972977656a..be3e4aba4459b 100644
--- a/addons/mrp/views/mrp_workorder_views.xml
+++ b/addons/mrp/views/mrp_workorder_views.xml
@@ -153,17 +153,14 @@
-
+
-
-
-
+
+
+
-
-
-
diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py
index b92616c9a5052..a701ddb01b0a3 100644
--- a/addons/mrp/wizard/change_production_qty.py
+++ b/addons/mrp/wizard/change_production_qty.py
@@ -86,7 +86,7 @@ def change_prod_qty(self):
quantity = quantity if (quantity > 0) else 0
if float_is_zero(quantity, precision_digits=precision):
wo.final_lot_id = False
- wo.active_move_line_ids.unlink()
+ wo.workorder_line_ids.unlink()
wo.qty_producing = quantity
if wo.qty_produced < wo.qty_production and wo.state == 'done':
wo.state = 'progress'
@@ -101,6 +101,11 @@ def change_prod_qty(self):
moves_finished = production.move_finished_ids.filtered(lambda move: move.operation_id == operation) #TODO: code does nothing, unless maybe by_products?
moves_raw.mapped('move_line_ids').write({'workorder_id': wo.id})
(moves_finished + moves_raw).write({'workorder_id': wo.id})
- if quantity > 0 and wo.move_raw_ids.filtered(lambda x: x.product_id.tracking != 'none') and not wo.active_move_line_ids:
- wo._generate_lot_ids()
+ if wo.state not in ('done', 'cancel'):
+ line_values = wo._update_workorder_lines()
+ wo.workorder_line_ids |= wo.workorder_line_ids.create(line_values['to_create'])
+ if line_values['to_delete']:
+ line_values['to_delete'].unlink()
+ for line, vals in line_values['to_update'].items():
+ line.write(vals)
return {}
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index 309f87abc9a68..b1db108d38060 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -4,13 +4,14 @@
from datetime import datetime
from odoo import api, fields, models, _
-from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError
-from odoo.tools import float_compare, float_round
+from odoo.tools import float_compare
+
class MrpProductProduce(models.TransientModel):
_name = "mrp.product.produce"
_description = "Record Production"
+ _inherit = ["mrp.abstract.workorder"]
@api.model
def default_get(self, fields):
@@ -32,21 +33,17 @@ def default_get(self, fields):
res['product_uom_id'] = todo_uom
if 'serial' in fields:
res['serial'] = bool(serial_finished)
- if 'product_qty' in fields:
- res['product_qty'] = todo_quantity
+ if 'qty_producing' in fields:
+ res['qty_producing'] = todo_quantity
return res
serial = fields.Boolean('Requires Serial')
- production_id = fields.Many2one('mrp.production', 'Production')
- product_id = fields.Many2one('product.product', 'Product')
- product_qty = fields.Float(string='Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True)
- product_uom_id = fields.Many2one('uom.uom', 'Unit of Measure')
- lot_id = fields.Many2one('stock.production.lot', string='Lot/Serial Number')
- produce_line_ids = fields.One2many('mrp.product.produce.line', 'product_produce_id', string='Product to Track')
- product_tracking = fields.Selection(related="product_id.tracking", readonly=True)
+ product_tracking = fields.Selection(related="product_id.tracking")
is_pending_production = fields.Boolean(compute='_compute_pending_production')
+ workorder_line_ids = fields.One2many('mrp.product.produce.line', 'product_produce_id')
+ move_raw_ids = fields.One2many(related='production_id.move_raw_ids')
- @api.depends('product_qty')
+ @api.depends('qty_producing')
def _compute_pending_production(self):
""" Compute if it exits remaining quantity once the quantity on the
current wizard will be processed. The purpose is to display or not
@@ -54,7 +51,7 @@ def _compute_pending_production(self):
"""
for product_produce in self:
remaining_qty = product_produce._get_todo(product_produce.production_id)
- product_produce.is_pending_production = remaining_qty - product_produce.product_qty > 0.0
+ product_produce.is_pending_production = remaining_qty - product_produce.qty_producing > 0.0
def continue_production(self):
""" Save current wizard and directly opens a new. """
@@ -67,7 +64,7 @@ def continue_production(self):
def action_generate_serial(self):
self.ensure_one()
product_produce_wiz = self.env.ref('mrp.view_mrp_product_produce_wizard', False)
- self.lot_id = self.env['stock.production.lot'].create({
+ self.final_lot_id = self.env['stock.production.lot'].create({
'product_id': self.product_id.id
})
return {
@@ -88,58 +85,6 @@ def do_produce(self):
self._record_production()
return {'type': 'ir.actions.act_window_close'}
- @api.multi
- def check_finished_move_lots(self):
- produce_move = self.production_id.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel'))
- if produce_move and produce_move.product_id.tracking != 'none':
- if not self.lot_id:
- raise UserError(_('You need to provide a lot for the finished product.'))
- existing_move_line = produce_move.move_line_ids.filtered(lambda x: x.lot_id == self.lot_id)
- if existing_move_line:
- if self.product_id.tracking == 'serial':
- raise UserError(_('You cannot produce the same serial number twice.'))
- produced_qty = self.product_uom_id._compute_quantity(self.product_qty, existing_move_line.product_uom_id)
- existing_move_line.product_uom_qty += produced_qty
- existing_move_line.qty_done += produced_qty
- else:
- vals = {
- 'move_id': produce_move.id,
- 'product_id': produce_move.product_id.id,
- 'production_id': self.production_id.id,
- 'product_uom_qty': self.product_qty,
- 'product_uom_id': self.product_uom_id.id,
- 'qty_done': self.product_qty,
- 'lot_id': self.lot_id.id,
- 'location_id': produce_move.location_id.id,
- 'location_dest_id': produce_move.location_dest_id.id,
- }
- self.env['stock.move.line'].create(vals)
-
- for pl in self.produce_line_ids:
- if pl.qty_done:
- if pl.product_id.tracking != 'none' and not pl.lot_id:
- raise UserError(_('Please enter a lot or serial number for %s !' % pl.product_id.display_name))
- if not pl.move_id:
- # Find move_id that would match
- move_id = self.production_id.move_raw_ids.filtered(lambda m: m.product_id == pl.product_id and m.state not in ('done', 'cancel'))
- if move_id:
- pl.move_id = move_id
- else:
- # create a move and put it in there
- order = self.production_id
- pl.move_id = self.env['stock.move'].create({
- 'name': order.name,
- 'product_id': pl.product_id.id,
- 'product_uom': pl.product_uom_id.id,
- 'location_id': order.location_src_id.id,
- 'location_dest_id': self.product_id.property_stock_production.id,
- 'raw_material_production_id': order.id,
- 'group_id': order.procurement_group_id.id,
- 'origin': order.name,
- 'state': 'confirmed'})
- pl.move_id._generate_consumed_move_line(pl.qty_done, self.lot_id, lot=pl.lot_id)
- return True
-
def _get_todo(self, production):
""" This method will return remaining todo quantity of production. """
main_product_moves = production.move_finished_ids.filtered(lambda x: x.product_id.id == production.product_id.id)
@@ -147,105 +92,54 @@ def _get_todo(self, production):
todo_quantity = todo_quantity if (todo_quantity > 0) else 0
return todo_quantity
+ @api.multi
def _record_production(self):
- # Nothing to do for lots since values are created using default data (stock.move.lots)
- quantity = self.product_qty
+ # Check all the product_produce line have a move id (the user can add product
+ # to consume directly in the wizard)
+ for line in self.workorder_line_ids:
+ if not line.move_id:
+ order = self.production_id
+ # Find move_id that would match
+ move_id = order.move_raw_ids.filtered(
+ lambda m: m.product_id == line.product_id and m.state not in ('done', 'cancel')
+ )
+ if not move_id:
+ # create a move to assign it to the line
+ move_id = self.env['stock.move'].create({
+ 'name': order.name,
+ 'reference': order.name,
+ 'product_id': line.product_id.id,
+ 'product_uom': line.product_uom_id.id,
+ 'location_id': order.location_src_id.id,
+ 'location_dest_id': line.product_id.property_stock_production.id,
+ 'raw_material_production_id': order.id,
+ 'group_id': order.procurement_group_id.id,
+ 'origin': order.name,
+ 'state': 'confirmed'
+ })
+ line.move_id = move_id.id
+
+ # Save product produce lines data into stock moves/move lines
+ quantity = self.qty_producing
if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
raise UserError(_("The production order for '%s' has no quantity specified.") % self.product_id.display_name)
- for move in self.production_id.move_finished_ids:
- if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel'):
- rounding = move.product_uom.rounding
- if move.product_id.id == self.production_id.product_id.id:
- move.quantity_done += float_round(quantity, precision_rounding=rounding)
- elif move.unit_factor:
- # byproducts handling
- move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
- self.check_finished_move_lots()
+ self._update_finished_move()
+ self._update_raw_moves()
if self.production_id.state == 'confirmed':
self.production_id.write({
'date_start': datetime.now(),
})
- @api.onchange('product_qty')
- def _onchange_product_qty(self):
- lines = []
- qty_todo = self.product_uom_id._compute_quantity(self.product_qty, self.production_id.product_uom_id, round=False)
- for move in self.production_id.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel') and (m.bom_line_id or m.product_uom_qty)):
- qty_to_consume = float_round(qty_todo * move.unit_factor, precision_rounding=move.product_uom.rounding)
- for move_line in move.move_line_ids:
- if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
- break
- if move_line.lot_produced_id or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
- continue
- to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': to_consume_in_line,
- 'qty_done': to_consume_in_line,
- 'lot_id': move_line.lot_id.id,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- 'qty_reserved': min(to_consume_in_line, move_line.product_uom_qty),
- })
- qty_to_consume -= to_consume_in_line
- if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
- if move.product_id.tracking == 'serial':
- while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': 1,
- 'qty_done': 1,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- })
- qty_to_consume -= 1
- else:
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': qty_to_consume,
- 'qty_done': qty_to_consume,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- })
-
- self.produce_line_ids = [(0, 0, x) for x in lines]
class MrpProductProduceLine(models.TransientModel):
- _name = "mrp.product.produce.line"
- _description = "Record Production Line"
-
- product_produce_id = fields.Many2one('mrp.product.produce')
- product_id = fields.Many2one('product.product', 'Product')
- product_tracking = fields.Selection(related="product_id.tracking", readonly=False)
- lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
- qty_to_consume = fields.Float('To Consume', digits=dp.get_precision('Product Unit of Measure'))
- product_uom_id = fields.Many2one('uom.uom', 'Unit of Measure')
- qty_done = fields.Float('Consumed')
- move_id = fields.Many2one('stock.move')
- qty_reserved = fields.Float('Reserved')
-
- @api.onchange('lot_id')
- def _onchange_lot_id(self):
- """ When the user is encoding a produce line for a tracked product, we apply some logic to
- help him. This onchange will automatically switch `qty_done` to 1.0.
- """
- res = {}
- if self.product_id.tracking == 'serial':
- self.qty_done = 1
- return res
+ _name = 'mrp.product.produce.line'
+ _inherit = ["mrp.abstract.workorder.line"]
+ _description = "Record production line"
- @api.onchange('qty_done')
- def _onchange_qty_done(self):
- """ When the user is encoding a produce line for a tracked product, we apply some logic to
- help him. This onchange will warn him if he set `qty_done` to a non-supported value.
- """
- res = {}
- if self.product_id.tracking == 'serial' and self.qty_done:
- if float_compare(self.qty_done, 1.0, precision_rounding=self.move_id.product_id.uom_id.rounding) != 0:
- message = _('You can only process 1.0 %s of products with unique serial number.') % self.product_id.uom_id.name
- res['warning'] = {'title': _('Warning'), 'message': message}
- return res
+ product_produce_id = fields.Many2one('mrp.product.produce', 'Produce wizard')
+
+ def _get_final_lot(self):
+ return self.product_produce_id.final_lot_id
- @api.onchange('product_id')
- def _onchange_product_id(self):
- self.product_uom_id = self.product_id.uom_id.id
+ def _get_production(self):
+ return self.product_produce_id.production_id
diff --git a/addons/mrp/wizard/mrp_product_produce_views.xml b/addons/mrp/wizard/mrp_product_produce_views.xml
index 964967ad2a0af..2c7954e1a6ecb 100644
--- a/addons/mrp/wizard/mrp_product_produce_views.xml
+++ b/addons/mrp/wizard/mrp_product_produce_views.xml
@@ -12,22 +12,22 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
diff --git a/addons/mrp_byproduct/models/mrp_production.py b/addons/mrp_byproduct/models/mrp_production.py
index 08902f14b56e3..1ecf0c3484a9e 100644
--- a/addons/mrp_byproduct/models/mrp_production.py
+++ b/addons/mrp_byproduct/models/mrp_production.py
@@ -55,12 +55,12 @@ class MrpProductProduce(models.TransientModel):
_inherit = "mrp.product.produce"
@api.multi
- def check_finished_move_lots(self):
+ def _update_finished_move(self):
""" Handle by product tracked """
- by_product_moves = self.production_id.move_finished_ids.filtered(lambda m: m.product_id != self.product_id and m.product_id.tracking != 'none' and m.state not in ('done', 'cancel'))
+ by_product_moves = self.production_id.move_finished_ids.filtered(lambda m: m.product_id != self.product_id and m.state not in ('done', 'cancel'))
for by_product_move in by_product_moves:
rounding = by_product_move.product_uom.rounding
- quantity = float_round(self.product_qty * by_product_move.unit_factor, precision_rounding=rounding)
+ quantity = float_round(self.qty_producing * by_product_move.unit_factor, precision_rounding=rounding)
values = {
'move_id': by_product_move.id,
'product_id': by_product_move.product_id.id,
@@ -82,4 +82,4 @@ def check_finished_move_lots(self):
})
for i in range(0, int(quantity)):
self.env['stock.move.line'].create(values)
- return super(MrpProductProduce, self).check_finished_move_lots()
+ return super(MrpProductProduce, self)._update_finished_move()
diff --git a/addons/mrp_byproduct/tests/test_mrp_byproduct.py b/addons/mrp_byproduct/tests/test_mrp_byproduct.py
index 2d9628109df27..ee6d566eec109 100644
--- a/addons/mrp_byproduct/tests/test_mrp_byproduct.py
+++ b/addons/mrp_byproduct/tests/test_mrp_byproduct.py
@@ -72,7 +72,7 @@ def test_00_mrp_byproduct(self):
# I consume and produce the production of products.
# I create record for selecting mode and quantity of products to produce.
produce_form = Form(self.env['mrp.product.produce'].with_context(context))
- produce_form.product_qty = 2.00
+ produce_form.qty_producing = 2.00
product_consume = produce_form.save()
# I finish the production order.
self.assertEqual(len(mnf_product_a.move_raw_ids), 1, "Wrong consume move on production order.")
diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py
index 29b85c9fadc6b..2078051754f2d 100644
--- a/addons/sale_mrp/tests/test_sale_mrp_flow.py
+++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py
@@ -423,7 +423,7 @@ def test_00_sale_mrp_flow(self):
'active_id': mnf_product_d.id,
'active_ids': [mnf_product_d.id],
}))
- produce_form.product_qty = 20
+ produce_form.qty_producing = 20
produce_d = produce_form.save()
# produce_d.on_change_qty()
produce_d.do_produce()