[FIX]sale_order with product using phantom bom

bzr revid: csn@openerp.com-20140207112606-k0cxzhpytlodjd1y
This commit is contained in:
Cedric Snauwaert 2014-02-07 12:26:06 +01:00
parent 0a74a775c5
commit 31348b1e4a
6 changed files with 121 additions and 48 deletions

View File

@ -1033,8 +1033,8 @@ class mrp_production(osv.osv):
'company_id': production.company_id.id,
}
move_id = stock_move.create(cr, uid, data, context=context)
stock_move.action_confirm(cr, uid, [move_id], context=context)
production.write({'move_created_ids': [(6, 0, [move_id])]}, context=context)
stock_move.action_confirm(cr, uid, [move_id], context=context)
return move_id
def _make_production_consume_line(self, cr, uid, production_line, parent_move_id, source_location_id=False, context=None):
@ -1059,8 +1059,8 @@ class mrp_production(osv.osv):
'company_id': production.company_id.id,
'procure_method': 'make_to_order',
})
stock_move.action_confirm(cr, uid, [move_id], context=context)
production.write({'move_lines': [(4, move_id)]}, context=context)
stock_move.action_confirm(cr, uid, [move_id], context=context)
return move_id
def action_confirm(self, cr, uid, ids, context=None):

View File

@ -687,7 +687,7 @@
</div>
<group>
<group>
<field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False)]" class="oe_inline" context='{"default_type": "product"}'/>
<field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False),('bom_ids.type','!=','phantom')]" class="oe_inline" context='{"default_type": "product"}'/>
<label for="product_qty"/>
<div>
<field name="product_qty" class="oe_inline"/>

View File

@ -22,6 +22,7 @@
from openerp.osv import fields
from openerp.osv import osv
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
class StockMove(osv.osv):
@ -33,6 +34,14 @@ class StockMove(osv.osv):
'consumed_for': fields.many2one('stock.move', 'Consumed for', help='Technical field used to make the traceability of produced products'),
}
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({
'production_id': False,
})
return super(StockMove, self).copy(cr, uid, id, default, context=context)
def check_tracking(self, cr, uid, move, lot_id, context=None):
super(StockMove, self).check_tracking(cr, uid, move, lot_id, context=context)
if move.product_id.track_production and (move.location_id.usage == 'production' or move.location_dest_id.usage == 'production') and not lot_id:
@ -49,8 +58,7 @@ class StockMove(osv.osv):
move_obj = self.pool.get('stock.move')
procurement_obj = self.pool.get('procurement.order')
product_obj = self.pool.get('product.product')
processed_ids = [move.id]
processed_ids = []
bis = bom_obj.search(cr, uid, [
('product_id', '=', move.product_id.id),
('bom_id', '=', False),
@ -64,45 +72,39 @@ class StockMove(osv.osv):
state = 'assigned'
for line in res[0]:
valdef = {
'picking_id': move.picking_id.id,
'picking_id': move.picking_id.id if move.picking_id else False,
'product_id': line['product_id'],
'product_uom': line['product_uom'],
'product_qty': line['product_qty'],
'product_uos': line['product_uos'],
'product_uos_qty': line['product_uos_qty'],
'move_dest_id': move.id,
'state': state,
'name': line['name'],
'procurements': [],
}
mid = move_obj.copy(cr, uid, move.id, default=valdef)
processed_ids.append(mid)
prodobj = product_obj.browse(cr, uid, line['product_id'], context=context)
proc_id = procurement_obj.create(cr, uid, {
'name': (move.picking_id.origin or ''),
'origin': (move.picking_id.origin or ''),
'date_planned': move.date,
'product_id': line['product_id'],
'product_qty': line['product_qty'],
'product_uom': line['product_uom'],
'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False,
'product_uos': line['product_uos'],
'location_id': move.location_id.id,
'procure_method': prodobj.procure_method,
'move_id': mid,
})
procurement_obj.signal_button_confirm(cr, uid, [proc_id])
move_obj.write(cr, uid, [move.id], {
'location_dest_id': move.location_id.id, # dummy move for the kit
'picking_id': False,
'state': 'confirmed'
})
procurement_ids = procurement_obj.search(cr, uid, [('move_id', '=', move.id)], context)
procurement_obj.signal_button_confirm(cr, uid, procurement_ids)
procurement_obj.signal_button_wait_done(cr, uid, procurement_ids)
move_obj.unlink(cr, SUPERUSER_ID, [move.id], context=context)
#confirm all new confirm moves
move_obj.action_confirm(cr, uid, processed_ids, context=context)
return processed_ids
def action_confirm(self, cr, uid, ids, context=None):
move_exploded = []
for move in self.browse(cr, uid, ids, context=context):
#in order to explode a move, we must have a picking_id on that move!
#if action_explode return a list of new move, it means it has exploded
#and that original move has been deleted, so we should remove that id
#from super call to prevent problem
move_id = move.id
if move.picking_type_id and self._action_explode(cr, uid, move, context=context):
move_exploded.append(move_id)
#if no more id, don't call super
move_unexploded = list(set(ids)-set(move_exploded))
if not len(move_unexploded):
return True
return super(StockMove, self).action_confirm(cr, uid, move_unexploded, context=context)
def action_consume(self, cr, uid, ids, product_qty, location_id=False, restrict_lot_id=False, restrict_partner_id=False,
consumed_for=False, context=None):
""" Consumed product with specific quantity from specific source location.
@ -182,22 +184,6 @@ class StockMove(osv.osv):
workflow.trg_trigger(uid, 'stock.move', move.id, cr)
return res
class StockPicking(osv.osv):
_inherit = 'stock.picking'
#
# Explode picking by replacing phantom BoMs
#
def action_explode(self, cr, uid, move_ids, *args):
"""Explodes moves by expanding kit components"""
move_obj = self.pool.get('stock.move')
todo = move_ids[:]
for move in move_obj.browse(cr, uid, move_ids):
todo.extend(move_obj._action_explode(cr, uid, move))
return list(set(todo))
class stock_warehouse(osv.osv):
_inherit = 'stock.warehouse'
_columns = {

View File

@ -14,7 +14,7 @@
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp_production_test1"), context=context)
assert len(order.product_lines) == 5, "Production lines are not generated proper."
assert len(order.product_lines) == 1, "Production lines are not generated proper."
-
Now I check workcenter lines.
-

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_move_explode
checks = [
test_move_explode,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
class TestMoveExplode(common.TransactionCase):
def setUp(self):
super(TestMoveExplode, self).setUp()
cr, uid = self.cr, self.uid
# Usefull models
self.ir_model_data = self.registry('ir.model.data')
self.sale_order_line = self.registry('sale.order.line')
self.sale_order = self.registry('sale.order')
self.mrp_bom = self.registry('mrp.bom')
#product that has a phantom bom
self.product_bom_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_4')[1]
#bom with that product
self.bom_id = self.ir_model_data.get_object_reference(cr, uid, 'mrp', 'mrp_bom_24')[1]
#partner agrolait
self.partner_id = self.ir_model_data.get_object_reference(cr, uid, 'base', 'res_partner_1')[1]
def test_00_sale_move_explode(self):
"""check that when creating a sale order with a product that has a phantom BoM, move explode into content of the
BoM"""
cr, uid, context = self.cr, self.uid, {}
#create sale order with one sale order line containing product with a phantom bom
so_id = self.sale_order.create(cr, uid, vals={'partner_id': self.partner_id}, context=context)
self.sale_order_line.create(cr, uid, values={'order_id': so_id, 'product_id': self.product_bom_id, 'product_uom_qty': 1}, context=context)
#confirm sale order
self.sale_order.action_button_confirm(cr, uid, [so_id], context=context)
#get all move associated to that sale_order
browse_move_ids = self.sale_order.browse(cr, uid, so_id, context=context).picking_ids[0].move_lines
move_ids = [x.id for x in browse_move_ids]
#we should have same amount of move as the component in the phatom bom
bom = self.mrp_bom.browse(cr, uid, self.bom_id, context=context)
bom_component_length = self.mrp_bom._bom_explode(cr, uid, bom, 1, [])
self.assertEqual(len(move_ids), len(bom_component_length[0]))