[FIX] mrp_byproduct: correctly handle fixed quantities

If the fixed quantity was higher than the produced quantity, the quantity of the
manufacturing order was used for the byproducts instead of the one specified on
the BoM.

This bug was due to the computation of the subfactor (`_get_subproduct_factor`
in mrp, because I love the modularity) that was computing remaining quantity,
only considering the variable quantities cases.
In fixed quantity, the MO quantity being lower than the byproduct quantity, it
was never fully produced.

Corrected the test to correctly check the quantities and handle the usecase with
an higher quantity in fixed byproduct.

Added a field subproduct_id on the stock.move object that allows to easily find
the attributes of the mrp.subproduct item without making hacky (and probably
wrong in case of more than one byproduct) search to find it back.

Closes #8031
This commit is contained in:
Mitali Patel
2016-03-17 15:36:54 +01:00
committed by Martin Trigaux
parent f4f6e6c5dd
commit 7b1d655d8c
5 changed files with 59 additions and 36 deletions
+11 -14
View File
@@ -855,17 +855,6 @@ class mrp_production(osv.osv):
res = False
return res
def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None):
""" Compute the factor to compute the qty of procucts to produce for the given production_id. By default,
it's always equal to the quantity encoded in the production order or the production wizard, but if the
module mrp_subproduct is installed, then we must use the move_id to identify the product to produce
and its quantity.
:param production_id: ID of the mrp.order
:param move_id: ID of the stock move that needs to be produced. Will be used in mrp_subproduct.
:return: The factor to apply to the quantity that we should produce for the given production order.
"""
return 1
def _get_produced_qty(self, cr, uid, production, context=None):
''' returns the produced quantity of product 'production.product_id' for the given production, in the product UoM
'''
@@ -968,6 +957,16 @@ class mrp_production(osv.osv):
consume_lines.append({'product_id': prod, 'product_qty': qty, 'lot_id': lot})
return consume_lines
def _calculate_produce_line_qty(self, cr, uid, move, quantity, context=None):
""" Compute the quantity and remainig quantity of products to produce.
:param move: Record set of stock move that needs to be produced, identify the product to produce.
:param quantity: specify quantity to produce in the uom of the production order.
:return: The quantity and remaining quantity of product produce.
"""
qty = min(quantity, move.product_qty)
remaining_qty = quantity - qty
return qty, remaining_qty
def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None):
""" To produce final product based on production mode (consume/consume&produce).
If Production mode is consume, all stock move lines of raw materials will be done/consumed.
@@ -996,15 +995,13 @@ class mrp_production(osv.osv):
produced_products[produced_product.product_id.id] = 0
produced_products[produced_product.product_id.id] += produced_product.product_qty
for produce_product in production.move_created_ids:
subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
lot_id = False
if wiz:
lot_id = wiz.lot_id.id
qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity
qty, remaining_qty = self._calculate_produce_line_qty(cr, uid, produce_product, production_qty_uom, context=context)
new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], qty,
location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context)
stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context)
remaining_qty = subproduct_factor * production_qty_uom - qty
if not float_is_zero(remaining_qty, precision_digits=precision):
# In case you need to make more than planned
#consumed more in wizard than previously planned
+1
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import mrp_byproduct
import stock_move
+28 -18
View File
@@ -88,41 +88,51 @@ class mrp_production(osv.osv):
'location_id': source,
'location_dest_id': production.location_dest_id.id,
'move_dest_id': production.move_prod_id.id,
'production_id': production.id
'production_id': production.id,
'origin': production.name,
'subproduct_id': sub_product.id
}
move_id = move_obj.create(cr, uid, data, context=context)
move_obj.action_confirm(cr, uid, [move_id], context=context)
return picking_id
def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None):
"""Compute the factor to compute the qty of procucts to produce for the given production_id. By default,
it's always equal to the quantity encoded in the production order or the production wizard, but with
def _get_subproduct_factor(self, cr, uid, move, context=None):
"""Compute the factor to compute the quantity of products to produce. By default,
it's always equal to the quantity encoded in the production order or the production wizard, but with
the module mrp_byproduct installed it can differ for byproducts having type 'variable'.
:param production_id: ID of the mrp.order
:param move_id: ID of the stock move that needs to be produced. Identify the product to produce.
:param move: Record set of stock move that needs to be produced, identify the product to produce.
:return: The factor to apply to the quantity that we should produce for the given production order and stock move.
"""
sub_obj = self.pool.get('mrp.subproduct')
move_obj = self.pool.get('stock.move')
production_obj = self.pool.get('mrp.production')
production_browse = production_obj.browse(cr, uid, production_id, context=context)
move_browse = move_obj.browse(cr, uid, move_id, context=context)
subproduct_factor = 1
sub_id = sub_obj.search(cr, uid,[('product_id', '=', move_browse.product_id.id),('bom_id', '=', production_browse.bom_id.id), ('subproduct_type', '=', 'variable')], context=context)
if sub_id:
subproduct_record = sub_obj.browse(cr ,uid, sub_id[0], context=context)
subproduct_record = move.subproduct_id
if subproduct_record.subproduct_type == 'variable':
if subproduct_record.bom_id.product_qty:
subproduct_factor = subproduct_record.product_qty / subproduct_record.bom_id.product_qty
return subproduct_factor
return super(mrp_production, self)._get_subproduct_factor(cr, uid, production_id, move_id, context=context)
return super(mrp_production, self)._get_subproduct_factor(cr, uid, move, context=context)
def _calculate_produce_line_qty(self, cr, uid, move, quantity, context=None):
""" Compute the quantity and remainig quantity of products to produce.
:param move: stock.move record that needs to be produced, identify the product to produce.
:param quantity: quantity to produce, in the uom of the production order.
:return: The quantity and remaining quantity of product produce.
"""
if move.subproduct_id.subproduct_type == 'variable':
subproduct_factor = self._get_subproduct_factor(cr, uid, move, context=context)
# Needed when producing more than maximum quantity
qty = min(subproduct_factor * quantity, move.product_qty)
remaining_qty = subproduct_factor * quantity - qty
return qty, remaining_qty
elif move.subproduct_id.subproduct_type == 'fixed':
return move.product_qty, 0
# no subproduct
return super(mrp_production, self)._calculate_produce_line_qty(cr, uid, move, quantity, context=context)
class change_production_qty(osv.osv_memory):
_inherit = 'change.production.qty'
def _update_product_to_produce(self, cr, uid, prod, qty, context=None):
bom_obj = self.pool.get('mrp.bom')
move_lines_obj = self.pool.get('stock.move')
prod_obj = self.pool.get('mrp.production')
for m in prod.move_created_ids:
@@ -131,6 +141,6 @@ class change_production_qty(osv.osv_memory):
else:
for sub_product_line in prod.bom_id.sub_products:
if sub_product_line.product_id.id == m.product_id.id:
factor = prod_obj._get_subproduct_factor(cr, uid, prod.id, m.id, context=context)
factor = prod_obj._get_subproduct_factor(cr, uid, m, context=context)
subproduct_qty = sub_product_line.subproduct_type == 'variable' and qty * factor or sub_product_line.product_qty
move_lines_obj.write(cr, uid, [m.id], {'product_uom_qty': subproduct_qty})
+11
View File
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import fields, models
class Stok_Move(models.Model):
_inherit = 'stock.move'
subproduct_id = fields.Many2one('mrp.subproduct', 'Subproduct',
help="Subproduct line that generated the move in a manufacturing order")
+8 -4
View File
@@ -2,7 +2,8 @@
In order to test mrp_byproduct with OpenERP, I add byproduct in bill of material.
I make a production order, confirm it so stock moves for byproducts are generated.
-
I add a sub product in Bill of material for product External Hard Disk.
I add a sub product in Bill of material for product Datacard with Switch, 24
ports as byproduct
-
!record {model: mrp.bom, id: mrp.mrp_bom_kit}:
product_id: product.product_delivery_02
@@ -10,10 +11,10 @@
sub_products:
- product_id: product.product_delivery_01
product_uom: product.product_uom_unit
product_qty: 2.0
product_qty: 3.0
subproduct_type: fixed
-
I create a production order for External Hard Disk.
I create a production order for 2 Datacard.
-
!record {model: mrp.production, id: mrp_production_mo0}:
product_id: product.product_delivery_02
@@ -40,8 +41,10 @@
This move is created automatically when I confirmed the production order.
-
!python {model: stock.move}: |
move_ids = self.search(cr, uid, [('product_id','=',ref('product.product_delivery_01'))])
production_order = self.pool.get('mrp.production').browse(cr, uid, ref("mrp_production_mo0"))
move_ids = self.search(cr, uid, [('origin','=',production_order.name), ('product_id','=',ref('product.product_delivery_01'))])
assert move_ids, 'No moves are created !'
assert self.browse(cr, uid, move_ids).product_uom_qty == 3, "Incorrect quantity for byproduct move"
-
I want to start the production so I force the reservation of products.
-
@@ -77,3 +80,4 @@
move_ids = self.search(cr, uid, [('origin','=',production_order.name), ('product_id','in',[ref("product.product_delivery_02"),ref("product.product_delivery_01")])])
moves = self.browse(cr, uid, move_ids)
assert all(move.state == 'done' for move in moves), 'Moves are not done!'