[MERGE] [FIX] mrp: producing a BoM of type service is no longer blocked in ready state, correctly transmit project reference when create task from sale order
bzr revid: mat@openerp.com-20130916113323-8an09x0d7svv2j7d
This commit is contained in:
@@ -77,6 +77,7 @@ Dashboard / Reports for MRP will include:
|
||||
#TODO: This yml tests are needed to be completely reviewed again because the product wood panel is removed in product demo as it does not suit for new demo context of computer and consultant company
|
||||
# so the ymls are too complex to change at this stage
|
||||
'test': [
|
||||
'test/bom_with_service_type_product.yml',
|
||||
# 'test/order_demo.yml',
|
||||
# 'test/order_process.yml',
|
||||
# 'test/cancel_order.yml',
|
||||
|
||||
+56
-24
@@ -591,12 +591,12 @@ class mrp_production(osv.osv):
|
||||
"""
|
||||
self.write(cr, uid, ids, {'state': 'picking_except'})
|
||||
return True
|
||||
|
||||
def action_compute(self, cr, uid, ids, properties=None, context=None):
|
||||
""" Computes bills of material of a product.
|
||||
@param properties: List containing dictionaries of properties.
|
||||
@return: No. of products.
|
||||
|
||||
def _action_compute_lines(self, cr, uid, ids, properties=None, context=None):
|
||||
""" Compute product_lines and workcenter_lines from BoM structure
|
||||
@return: product_lines
|
||||
"""
|
||||
|
||||
if properties is None:
|
||||
properties = []
|
||||
results = []
|
||||
@@ -604,13 +604,15 @@ class mrp_production(osv.osv):
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
prod_line_obj = self.pool.get('mrp.production.product.line')
|
||||
workcenter_line_obj = self.pool.get('mrp.production.workcenter.line')
|
||||
for production in self.browse(cr, uid, ids):
|
||||
|
||||
p_ids = prod_line_obj.search(cr, SUPERUSER_ID, [('production_id', '=', production.id)], context=context)
|
||||
prod_line_obj.unlink(cr, SUPERUSER_ID, p_ids, context=context)
|
||||
w_ids = workcenter_line_obj.search(cr, SUPERUSER_ID, [('production_id', '=', production.id)], context=context)
|
||||
workcenter_line_obj.unlink(cr, SUPERUSER_ID, w_ids, context=context)
|
||||
|
||||
for production in self.browse(cr, uid, ids, context=context):
|
||||
#unlink product_lines
|
||||
prod_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.product_lines], context=context)
|
||||
|
||||
#unlink workcenter_lines
|
||||
workcenter_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.workcenter_lines], context=context)
|
||||
|
||||
# search BoM structure and route
|
||||
bom_point = production.bom_id
|
||||
bom_id = production.bom_id.id
|
||||
if not bom_point:
|
||||
@@ -619,20 +621,33 @@ class mrp_production(osv.osv):
|
||||
bom_point = bom_obj.browse(cr, uid, bom_id)
|
||||
routing_id = bom_point.routing_id.id or False
|
||||
self.write(cr, uid, [production.id], {'bom_id': bom_id, 'routing_id': routing_id})
|
||||
|
||||
|
||||
if not bom_id:
|
||||
raise osv.except_osv(_('Error!'), _("Cannot find a bill of material for this product."))
|
||||
|
||||
# get components and workcenter_lines from BoM structure
|
||||
factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
|
||||
res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id)
|
||||
results = res[0]
|
||||
results2 = res[1]
|
||||
results = res[0] # product_lines
|
||||
results2 = res[1] # workcenter_lines
|
||||
|
||||
# reset product_lines in production order
|
||||
for line in results:
|
||||
line['production_id'] = production.id
|
||||
prod_line_obj.create(cr, uid, line)
|
||||
|
||||
#reset workcenter_lines in production order
|
||||
for line in results2:
|
||||
line['production_id'] = production.id
|
||||
workcenter_line_obj.create(cr, uid, line)
|
||||
return len(results)
|
||||
return results
|
||||
|
||||
def action_compute(self, cr, uid, ids, properties=None, context=None):
|
||||
""" Computes bills of material of a product.
|
||||
@param properties: List containing dictionaries of properties.
|
||||
@return: No. of products.
|
||||
"""
|
||||
return len(self._action_compute_lines(cr, uid, ids, properties=properties, context=context))
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
""" Cancels the production order and related stock moves.
|
||||
@@ -659,8 +674,12 @@ class mrp_production(osv.osv):
|
||||
move_obj = self.pool.get('stock.move')
|
||||
self.write(cr, uid, ids, {'state': 'ready'})
|
||||
|
||||
for (production_id,name) in self.name_get(cr, uid, ids):
|
||||
production = self.browse(cr, uid, production_id)
|
||||
for production in self.browse(cr, uid, ids, context=context):
|
||||
if not production.move_created_ids:
|
||||
produce_move_id = self._make_production_produce_line(cr, uid, production, context=context)
|
||||
for scheduled in production.product_lines:
|
||||
self._make_production_line_procurement(cr, uid, scheduled, False, context=context)
|
||||
|
||||
if production.move_prod_id and production.move_prod_id.location_id.id != production.location_dest_id.id:
|
||||
move_obj.write(cr, uid, [production.move_prod_id.id],
|
||||
{'location_id': production.location_dest_id.id})
|
||||
@@ -712,6 +731,11 @@ class mrp_production(osv.osv):
|
||||
stock_mov_obj = self.pool.get('stock.move')
|
||||
production = self.browse(cr, uid, production_id, context=context)
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
if not production.move_lines and production.state == 'ready':
|
||||
# trigger workflow if not products to consume (eg: services)
|
||||
wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_produce', cr)
|
||||
|
||||
produced_qty = 0
|
||||
for produced_product in production.move_created_ids2:
|
||||
if (produced_product.scrapped) or (produced_product.product_id.id != production.product_id.id):
|
||||
@@ -789,7 +813,6 @@ class mrp_production(osv.osv):
|
||||
for new_parent_id in new_parent_ids:
|
||||
stock_mov_obj.write(cr, uid, [raw_product.id], {'move_history_ids': [(4,new_parent_id)]})
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_produce_done', cr)
|
||||
return True
|
||||
|
||||
@@ -849,13 +872,19 @@ class mrp_production(osv.osv):
|
||||
"""
|
||||
res = True
|
||||
for production in self.browse(cr, uid, ids):
|
||||
if not production.product_lines:
|
||||
if not self.action_compute(cr, uid, [production.id]):
|
||||
res = False
|
||||
boms = self._action_compute_lines(cr, uid, [production.id])
|
||||
res = False
|
||||
for bom in boms:
|
||||
product = self.pool.get('product.product').browse(cr, uid, bom['product_id'])
|
||||
if product.type in ('product', 'consu'):
|
||||
res = True
|
||||
return res
|
||||
|
||||
def _get_auto_picking(self, cr, uid, production):
|
||||
return True
|
||||
|
||||
def _hook_create_post_procurement(self, cr, uid, production, procurement_id, context=None):
|
||||
return True
|
||||
|
||||
def _make_production_line_procurement(self, cr, uid, production_line, shipment_move_id, context=None):
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
@@ -878,6 +907,7 @@ class mrp_production(osv.osv):
|
||||
'move_id': shipment_move_id,
|
||||
'company_id': production.company_id.id,
|
||||
})
|
||||
self._hook_create_post_procurement(cr, uid, production, procurement_id, context=context)
|
||||
wf_service.trg_validate(uid, procurement_order._name, procurement_id, 'button_confirm', cr)
|
||||
return procurement_id
|
||||
|
||||
@@ -1005,11 +1035,13 @@ class mrp_production(osv.osv):
|
||||
|
||||
for line in production.product_lines:
|
||||
consume_move_id = self._make_production_consume_line(cr, uid, line, produce_move_id, source_location_id=source_location_id, context=context)
|
||||
shipment_move_id = self._make_production_internal_shipment_line(cr, uid, line, shipment_id, consume_move_id,\
|
||||
if shipment_id:
|
||||
shipment_move_id = self._make_production_internal_shipment_line(cr, uid, line, shipment_id, consume_move_id,\
|
||||
destination_location_id=source_location_id, context=context)
|
||||
self._make_production_line_procurement(cr, uid, line, shipment_move_id, context=context)
|
||||
self._make_production_line_procurement(cr, uid, line, shipment_move_id, context=context)
|
||||
|
||||
wf_service.trg_validate(uid, 'stock.picking', shipment_id, 'button_confirm', cr)
|
||||
if shipment_id:
|
||||
wf_service.trg_validate(uid, 'stock.picking', shipment_id, 'button_confirm', cr)
|
||||
production.write({'state':'confirmed'}, context=context)
|
||||
return shipment_id
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
-
|
||||
I create Bill of Materials with one service type product and one consumable product.
|
||||
-
|
||||
!record {model: mrp.bom, id: mrp_bom_test1}:
|
||||
company_id: base.main_company
|
||||
name: PC Assemble SC234
|
||||
product_id: product.product_product_3
|
||||
product_qty: 1.0
|
||||
type: normal
|
||||
bom_lines:
|
||||
- company_id: base.main_company
|
||||
name: On Site Assistance
|
||||
product_id: product.product_product_2
|
||||
product_qty: 1.0
|
||||
- company_id: base.main_company
|
||||
name: GrapWorks Software
|
||||
product_id: product.product_product_44
|
||||
product_qty: 1.0
|
||||
-
|
||||
I make the production order using BoM having one service type product and one consumable product.
|
||||
-
|
||||
!record {model: mrp.production, id: mrp_production_servicetype_mo1}:
|
||||
product_id: product.product_product_5
|
||||
product_qty: 1.0
|
||||
bom_id: mrp_bom_test1
|
||||
date_planned: !eval time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
-
|
||||
I compute the data of production order.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
self.action_compute(cr, uid, [ref("mrp_production_servicetype_mo1")], {"lang": "en_US", "tz": False, "search_default_Current": 1,
|
||||
"active_model": "ir.ui.menu", "active_ids": [ref("mrp.menu_mrp_production_action")],
|
||||
"active_id": ref("mrp.menu_mrp_production_action"), })
|
||||
-
|
||||
I confirm the production order.
|
||||
-
|
||||
!workflow {model: mrp.production, action: button_confirm, ref: mrp_production_servicetype_mo1}
|
||||
-
|
||||
I confirm the Consume Products.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
order = self.browse(cr, uid, ref("mrp_production_servicetype_mo1"))
|
||||
assert order.state == 'confirmed', "Production order should be confirmed."
|
||||
for move_line in order.move_lines:
|
||||
move_line.action_consume(move_line.product_qty)
|
||||
-
|
||||
I processed the Product Entirely.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
order = self.browse(cr, uid, ref("mrp_production_servicetype_mo1"))
|
||||
assert order.state == 'in_production', 'Production order should be in production State.'
|
||||
for move_created in order.move_created_ids:
|
||||
move_created.action_done()
|
||||
-
|
||||
I produce product.
|
||||
-
|
||||
!python {model: mrp.product.produce}: |
|
||||
context.update({'active_id': ref('mrp_production_servicetype_mo1')})
|
||||
-
|
||||
!record {model: mrp.product.produce, id: mrp_product_produce_1}:
|
||||
mode: 'consume_produce'
|
||||
-
|
||||
!python {model: mrp.product.produce}: |
|
||||
self.do_produce(cr, uid, [ref('mrp_product_produce_1')], context=context)
|
||||
-
|
||||
I check production order after produced.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
order = self.browse(cr, uid, ref("mrp_production_servicetype_mo1"))
|
||||
assert order.state == 'done', "Production order should be closed."
|
||||
-
|
||||
I create Bill of Materials with two service type products.
|
||||
-
|
||||
!record {model: mrp.bom, id: mrp_bom_test_2}:
|
||||
company_id: base.main_company
|
||||
name: PC Assemble SC234
|
||||
product_id: product.product_product_3
|
||||
product_qty: 1.0
|
||||
type: normal
|
||||
bom_lines:
|
||||
- company_id: base.main_company
|
||||
name: On Site Monitoring
|
||||
product_id: product.product_product_1
|
||||
product_qty: 1.0
|
||||
- company_id: base.main_company
|
||||
name: On Site Assistance
|
||||
product_id: product.product_product_2
|
||||
product_qty: 1.0
|
||||
-
|
||||
I make the production order using BoM having two service type products.
|
||||
-
|
||||
!record {model: mrp.production, id: mrp_production_servicetype_2}:
|
||||
product_id: product.product_product_5
|
||||
product_qty: 1.0
|
||||
bom_id: mrp_bom_test_2
|
||||
date_planned: !eval time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
-
|
||||
I compute the data of production order.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
self.action_compute(cr, uid, [ref("mrp_production_servicetype_2")], {"lang": "en_US", "tz": False, "search_default_Current": 1,
|
||||
"active_model": "ir.ui.menu", "active_ids": [ref("mrp.menu_mrp_production_action")],
|
||||
"active_id": ref("mrp.menu_mrp_production_action"), })
|
||||
-
|
||||
I confirm the production order.
|
||||
-
|
||||
!workflow {model: mrp.production, action: button_confirm, ref: mrp_production_servicetype_2}
|
||||
-
|
||||
Now I start production.
|
||||
-
|
||||
!workflow {model: mrp.production, action: button_produce, ref: mrp_production_servicetype_2}
|
||||
-
|
||||
I check that production order in production state after start production.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
order = self.browse(cr, uid, ref("mrp_production_servicetype_2"))
|
||||
assert order.state == 'in_production', 'Production order should be in production State.'
|
||||
-
|
||||
I produce product.
|
||||
-
|
||||
!python {model: mrp.product.produce}: |
|
||||
context.update({'active_id': ref('mrp_production_servicetype_2')})
|
||||
-
|
||||
!record {model: mrp.product.produce, id: mrp_product_produce_2}:
|
||||
mode: 'consume_produce'
|
||||
-
|
||||
!python {model: mrp.product.produce}: |
|
||||
self.do_produce(cr, uid, [ref('mrp_product_produce_2')], context=context)
|
||||
-
|
||||
I check production order after produced.
|
||||
-
|
||||
!python {model: mrp.production}: |
|
||||
order = self.browse(cr, uid, ref("mrp_production_servicetype_2"))
|
||||
assert order.state == 'done', "Production order should be closed."
|
||||
@@ -109,4 +109,22 @@ class sale_order(osv.osv):
|
||||
}
|
||||
|
||||
sale_order()
|
||||
|
||||
class mrp_production(osv.osv):
|
||||
_inherit = 'mrp.production'
|
||||
|
||||
def _hook_create_post_procurement(self, cr, uid, production, procurement_id, context=None):
|
||||
def get_parent_move(move):
|
||||
if move.move_dest_id:
|
||||
return get_parent_move(move.move_dest_id)
|
||||
return move
|
||||
|
||||
res = super(mrp_production, self)._hook_create_post_procurement(cr, uid, production, procurement_id, context)
|
||||
if production.move_prod_id:
|
||||
parent_move_line = get_parent_move(production.move_prod_id)
|
||||
if parent_move_line and parent_move_line.sale_line_id:
|
||||
self.pool.get('procurement.order').write(cr, uid, procurement_id, {'sale_line_id': parent_move_line.sale_line_id.id})
|
||||
return res
|
||||
|
||||
mrp_production()
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
Reference in New Issue
Block a user