[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:
committed by
Martin Trigaux
parent
f4f6e6c5dd
commit
7b1d655d8c
+11
-14
@@ -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,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import mrp_byproduct
|
||||
import stock_move
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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")
|
||||
@@ -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!'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user