"""
bom_obj = self.pool.get('mrp.bom')
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]
- if move.product_id.supply_method == 'produce':
- bis = bom_obj.search(cr, uid, [
- ('product_id','=',move.product_id.id),
- ('bom_id','=',False),
- ('type','=','phantom')])
- if bis:
- factor = move.product_qty
- bom_point = bom_obj.browse(cr, uid, bis[0], context=context)
- res = bom_obj._bom_explode(cr, uid, bom_point, factor, [])
- for line in res[0]:
+ prod_obj = self.pool.get("product.product")
+ proc_obj = self.pool.get("procurement.order")
+ uom_obj = self.pool.get("product.uom")
+ to_explode_again_ids = []
+ processed_ids = []
+ bis = self._check_phantom_bom(cr, uid, move, context=context)
+ if bis:
+ bom_point = bom_obj.browse(cr, SUPERUSER_ID, bis[0], context=context)
+ factor = uom_obj._compute_qty(cr, SUPERUSER_ID, move.product_uom.id, move.product_uom_qty, bom_point.product_uom.id) / bom_point.product_qty
+ res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, move.product_id, factor, [], context=context)
+
- state = 'confirmed'
- if move.state == 'assigned':
- state = 'assigned'
+ for line in res[0]:
+ product = prod_obj.browse(cr, uid, line['product_id'], context=context)
+ if product.type != 'service':
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_uom_qty': line['product_qty'],
'product_uos': line['product_uos'],
'product_uos_qty': line['product_uos_qty'],
- 'state': state,
- 'move_dest_id': move.id,
+ 'state': 'draft', #will be confirmed below
'name': line['name'],
- 'move_history_ids': [(6,0,[move.id])],
- 'move_history_ids2': [(6,0,[])],
- 'procurements': [],
+ 'procurement_id': move.procurement_id.id,
+ 'split_from': move.id, #Needed in order to keep sale connection, but will be removed by unlink
}
- 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
- 'auto_validate': True,
- '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)
- if processed_ids and move.state == 'assigned':
- # Set the state of resulting moves according to 'assigned' as the original move is assigned
- move_obj.write(cr, uid, list(set(processed_ids) - set([move.id])), {'state': 'assigned'}, context=context)
- return processed_ids
-
- def action_consume(self, cr, uid, ids, product_qty, location_id=False, context=None):
- """ Consumed product with specific quatity from specific source location.
- @param product_qty: Consumed product quantity
+ mid = move_obj.copy(cr, uid, move.id, default=valdef, context=context)
+ to_explode_again_ids.append(mid)
+ else:
+ if prod_obj.need_procurement(cr, uid, [product.id], context=context):
+ valdef = {
+ 'name': move.rule_id and move.rule_id.name or "/",
+ 'origin': move.origin,
+ 'company_id': move.company_id and move.company_id.id or False,
+ '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_qty'],
+ 'product_uos': line['product_uos'],
+ 'group_id': move.group_id.id,
+ 'priority': move.priority,
+ 'partner_dest_id': move.partner_id.id,
+ }
+ if move.procurement_id:
+ proc = proc_obj.copy(cr, uid, move.procurement_id.id, default=valdef, context=context)
+ else:
+ proc = proc_obj.create(cr, uid, valdef, context=context)
+ proc_obj.run(cr, uid, [proc], context=context) #could be omitted
+
+
+ #check if new moves needs to be exploded
+ if to_explode_again_ids:
+ for new_move in self.browse(cr, uid, to_explode_again_ids, context=context):
+ processed_ids.extend(self._action_explode(cr, uid, new_move, context=context))
+
+ if not move.split_from and move.procurement_id:
+ # Check if procurements have been made to wait for
+ moves = move.procurement_id.move_ids
+ if len(moves) == 1:
+ proc_obj.write(cr, uid, [move.procurement_id.id], {'state': 'done'}, context=context)
++
++ if processed_ids and move.state == 'assigned':
++ # Set the state of resulting moves according to 'assigned' as the original move is assigned
++ move_obj.write(cr, uid, list(set(processed_ids) - set([move.id])), {'state': 'assigned'}, context=context)
+
+ #delete the move with original product which is not relevant anymore
+ move_obj.unlink(cr, SUPERUSER_ID, [move.id], context=context)
+ #return list of newly created move or the move id otherwise, unless there is no move anymore
+ return processed_ids or (not bis and [move.id]) or []
+
+ def action_confirm(self, cr, uid, ids, context=None):
+ move_ids = []
+ for move in self.browse(cr, uid, ids, context=context):
+ #in order to explode a move, we must have a picking_type_id on that move because otherwise the move
+ #won't be assigned to a picking and it would be weird to explode a move into several if they aren't
+ #all grouped in the same picking.
+ if move.picking_type_id:
+ move_ids.extend(self._action_explode(cr, uid, move, context=context))
+ else:
+ move_ids.append(move.id)
+
+ #we go further with the list of ids potentially changed by action_explode
+ return super(StockMove, self).action_confirm(cr, uid, move_ids, 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.
+ @param product_qty: Consumed/produced product quantity (= in quantity of UoM of product)
@param location_id: Source location
- @return: Consumed lines
- """
+ @param restrict_lot_id: optionnal parameter that allows to restrict the choice of quants on this specific lot
+ @param restrict_partner_id: optionnal parameter that allows to restrict the choice of quants to this specific partner
+ @param consumed_for: optionnal parameter given to this function to make the link between raw material consumed and produced product, for a better traceability
+ @return: New lines created if not everything was consumed for this line
+ """
+ if context is None:
+ context = {}
res = []
production_obj = self.pool.get('mrp.production')
- for move in self.browse(cr, uid, ids):
- move.action_confirm(context)
- new_moves = super(StockMove, self).action_consume(cr, uid, [move.id], product_qty, location_id, context=context)
- production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])])
- for prod in production_obj.browse(cr, uid, production_ids, context=context):
- if prod.state == 'confirmed':
- production_obj.force_production(cr, uid, [prod.id])
- production_obj.signal_button_produce(cr, uid, production_ids)
- for new_move in new_moves:
- if new_move == move.id:
- #This move is already there in move lines of production order
- continue
- production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]})
- res.append(new_move)
+
+ if product_qty <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide proper quantity.'))
+ #because of the action_confirm that can create extra moves in case of phantom bom, we need to make 2 loops
+ ids2 = []
+ for move in self.browse(cr, uid, ids, context=context):
+ if move.state == 'draft':
+ ids2.extend(self.action_confirm(cr, uid, [move.id], context=context))
+ else:
+ ids2.append(move.id)
+
+ prod_orders = set()
+ for move in self.browse(cr, uid, ids2, context=context):
+ prod_orders.add(move.raw_material_production_id.id or move.production_id.id)
+ move_qty = move.product_qty
+ if move_qty <= 0:
+ raise osv.except_osv(_('Error!'), _('Cannot consume a move with negative or zero quantity.'))
+ quantity_rest = move_qty - product_qty
+ # Compare with numbers of move uom as we want to avoid a split with 0 qty
+ quantity_rest_uom = move.product_uom_qty - self.pool.get("product.uom")._compute_qty_obj(cr, uid, move.product_id.uom_id, product_qty, move.product_uom)
+ if float_compare(quantity_rest_uom, 0, precision_rounding=move.product_uom.rounding) != 0:
+ new_mov = self.split(cr, uid, move, quantity_rest, context=context)
+ res.append(new_mov)
+ vals = {'restrict_lot_id': restrict_lot_id,
+ 'restrict_partner_id': restrict_partner_id,
+ 'consumed_for': consumed_for}
+ if location_id:
+ vals.update({'location_id': location_id})
+ self.write(cr, uid, [move.id], vals, context=context)
+ # Original moves will be the quantities consumed, so they need to be done
+ self.action_done(cr, uid, ids2, context=context)
+ if res:
+ self.action_assign(cr, uid, res, context=context)
+ if prod_orders:
+ production_obj.signal_workflow(cr, uid, list(prod_orders), 'button_produce')
return res
-
- def action_scrap(self, cr, uid, ids, product_qty, location_id, context=None):
+
+ def action_scrap(self, cr, uid, ids, product_qty, location_id, restrict_lot_id=False, restrict_partner_id=False, context=None):
""" Move the scrap/damaged product into scrap location
@param product_qty: Scraped product quantity
@param location_id: Scrap location