+ def _create_lot(self, cr, uid, ids, product_id, prefix=False):
+ prodlot_obj = self.pool.get('stock.production.lot')
+ ir_sequence_obj = self.pool.get('ir.sequence')
+ sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial')
+ if not sequence:
+ raise osv.except_osv(_('Error!'), _('No production sequence defined'))
+ prodlot_id = prodlot_obj.create(cr, uid, {'name': sequence, 'prefix': prefix}, {'product_id': product_id})
+ prodlot = prodlot_obj.browse(cr, uid, prodlot_id)
+ ref = ','.join(map(lambda x:str(x),ids))
+ if prodlot.ref:
+ ref = '%s, %s' % (prodlot.ref, ref)
+ prodlot_obj.write(cr, uid, [prodlot_id], {'ref': ref})
+ return prodlot_id
+
+
+ def action_scrap(self, cr, uid, ids, quantity, location_id, context=None):
+ '''
+ Move the scrap/damaged product into scrap location
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be scraped
+ @ param quantity : specify scrap qty
+ @ param location_id : specify scrap location
+ @ param context: context arguments
+
+ @ return: Scraped lines
+ '''
+ if quantity <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+ res = []
+ for move in self.browse(cr, uid, ids, context=context):
+ move_qty = move.product_qty
+ uos_qty = quantity / move_qty * move.product_uos_qty
+ default_val = {
+ 'product_qty': quantity,
+ 'product_uos_qty': uos_qty,
+ 'state': move.state,
+ 'scraped' : True,
+ 'location_dest_id': location_id
+ }
+ new_move = self.copy(cr, uid, move.id, default_val)
+ #self.write(cr, uid, [new_move], {'move_history_ids':[(4,move.id)]}) #TODO : to track scrap moves
+ res += [new_move]
+ self.action_done(cr, uid, res)
+ return res
+
+ def action_split(self, cr, uid, ids, quantity, split_by_qty=1, prefix=False, with_lot=True, context=None):
+ '''
+ Split Stock Move lines into production lot which specified split by quantity.
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be splited
+ @ param split_by_qty : specify split by qty
+ @ param prefix : specify prefix of production lot
+ @ param with_lot : if true, prodcution lot will assign for split line otherwise not.
+ @ param context: context arguments
+
+ @ return: splited move lines
+ '''
+
+ if quantity <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+
+ res = []
+
+ for move in self.browse(cr, uid, ids):
+ if split_by_qty <= 0 or quantity == 0:
+ return res
+
+ uos_qty = split_by_qty / move.product_qty * move.product_uos_qty
+
+ quantity_rest = quantity % split_by_qty
+ uos_qty_rest = split_by_qty / move.product_qty * move.product_uos_qty
+
+ update_val = {
+ 'product_qty': split_by_qty,
+ 'product_uos_qty': uos_qty,
+ }
+ for idx in range(int(quantity//split_by_qty)):
+ if not idx and move.product_qty<=quantity:
+ current_move = move.id
+ else:
+ current_move = self.copy(cr, uid, move.id, {'state': move.state})
+ res.append(current_move)
+ if with_lot:
+ update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
+
+ self.write(cr, uid, [current_move], update_val)
+
+
+ if quantity_rest > 0:
+ idx = int(quantity//split_by_qty)
+ update_val['product_qty'] = quantity_rest
+ update_val['product_uos_qty'] = uos_qty_rest
+ if not idx and move.product_qty<=quantity:
+ current_move = move.id
+ else:
+ current_move = self.copy(cr, uid, move.id, {'state': move.state})
+
+ res.append(current_move)
+
+
+ if with_lot:
+ update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
+
+ self.write(cr, uid, [current_move], update_val)
+ return res
+
+ def action_consume(self, cr, uid, ids, quantity, location_id=False, context=None):
+ '''
+ Consumed product with specific quatity from specific source location
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be consumed
+ @ param quantity : specify consume quantity
+ @ param location_id : specify source location
+ @ param context: context arguments
+
+ @ return: Consumed lines
+ '''
+ if not context:
+ context = {}
+
+ if quantity <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+
+ res = []
+ for move in self.browse(cr, uid, ids, context=context):
+ move_qty = move.product_qty
+ quantity_rest = move.product_qty
+
+ quantity_rest -= quantity
+ uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty
+ if quantity_rest <= 0:
+ quantity_rest = 0
+ uos_qty_rest = 0
+ quantity = move.product_qty
+
+ uos_qty = quantity / move_qty * move.product_uos_qty
+
+ if quantity_rest > 0:
+ default_val = {
+ 'product_qty': quantity,
+ 'product_uos_qty': uos_qty,
+ 'state': move.state,
+ 'location_id': location_id
+ }
+ if move.product_id.track_production and location_id:
+ # IF product has checked track for production lot, move lines will be split by 1
+ res += self.action_split(cr, uid, [move.id], quantity, split_by_qty=1, context=context)
+ else:
+ current_move = self.copy(cr, uid, move.id, default_val)
+ res += [current_move]
+
+ update_val = {}
+ update_val['product_qty'] = quantity_rest
+ update_val['product_uos_qty'] = uos_qty_rest
+ self.write(cr, uid, [move.id], update_val)
+
+ else:
+ quantity_rest = quantity
+ uos_qty_rest = uos_qty
+
+ if move.product_id.track_production and location_id:
+ res += self.split_lines(cr, uid, [move.id], quantity_rest, split_by_qty=1, context=context)
+ else:
+ res += [move.id]
+ update_val = {
+ 'product_qty' : quantity_rest,
+ 'product_uos_qty' : uos_qty_rest,
+ 'location_id': location_id
+ }
+
+ self.write(cr, uid, [move.id], update_val)
+
+ self.action_done(cr, uid, res)
+ return res
+
+ def do_partial(self, cr, uid, ids, partial_datas, context={}):
+ """
+ @ partial_datas : dict. contain details of partial picking
+ like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
+ """
+ res = {}
+ picking_obj = self.pool.get('stock.picking')
+ delivery_obj = self.pool.get('stock.delivery')
+ product_obj = self.pool.get('product.product')
+ currency_obj = self.pool.get('res.currency')
+ users_obj = self.pool.get('res.users')
+ uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+ sequence_obj = self.pool.get('ir.sequence')
+ wf_service = netsvc.LocalService("workflow")
+ partner_id = partial_datas.get('partner_id', False)
+ address_id = partial_datas.get('address_id', False)
+ delivery_date = partial_datas.get('delivery_date', False)
+
+ new_moves = []
+
+ complete, too_many, too_few = [], [], []
+ move_product_qty = {}
+ for move in self.browse(cr, uid, ids, context=context):
+ if move.state in ('done', 'cancel'):
+ continue
+ partial_data = partial_datas.get('move%s'%(move.id), False)
+ assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
+ product_qty = partial_data.get('product_qty',0.0)
+ move_product_qty[move.id] = product_qty
+ product_uom = partial_data.get('product_uom',False)
+ product_price = partial_data.get('product_price',0.0)
+ product_currency = partial_data.get('product_currency',False)
+ if move.product_qty == product_qty:
+ complete.append(move)
+ elif move.product_qty > product_qty:
+ too_few.append(move)
+ else:
+ too_many.append(move)
+
+ # Average price computation
+ if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
+ product = product_obj.browse(cr, uid, move.product_id.id)
+ user = users_obj.browse(cr, uid, uid)
+ context['currency_id'] = move.company_id.currency_id.id
+ qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+ pricetype = False
+ if user.company_id.property_valuation_price_type:
+ pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
+ if pricetype and qty > 0:
+ new_price = currency_obj.compute(cr, uid, product_currency,
+ user.company_id.currency_id.id, product_price)
+ new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
+ product.uom_id.id)
+ if product.qty_available <= 0:
+ new_std_price = new_price
+ else:
+ # Get the standard price
+ amount_unit = product.price_get(pricetype.field, context)[product.id]
+ new_std_price = ((amount_unit * product.qty_available)\
+ + (new_price * qty))/(product.qty_available + qty)
+
+ # Write the field according to price type field
+ product_obj.write(cr, uid, [product.id],
+ {pricetype.field: new_std_price})
+ self.write(cr, uid, [move.id], {'price_unit': new_price})
+
+ for move in too_few:
+ product_qty = move_product_qty[move.id]
+ if product_qty != 0:
+ new_move = self.copy(cr, uid, move.id,
+ {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty,
+ 'picking_id' : move.picking_id.id,
+ 'state': 'assigned',
+ 'move_dest_id': False,
+ 'price_unit': move.price_unit,
+ })
+ complete.append(self.browse(cr, uid, new_move))
+ self.write(cr, uid, move.id,
+ {
+ 'product_qty' : move.product_qty - product_qty,
+ 'product_uos_qty':move.product_qty - product_qty,
+ })
+
+
+ for move in too_many:
+ self.write(cr, uid, move.id,
+ {
+ 'product_qty': product_qty,
+ 'product_uos_qty': product_qty
+ })
+ complete.append(move)
+
+ for move in complete:
+ self.action_done(cr, uid, [move.id])
+
+ # TOCHECK : Done picking if all moves are done
+ cr.execute("""
+ SELECT move.id FROM stock_picking pick
+ RIGHT JOIN stock_move move ON move.picking_id = pick.id AND move.state = %s
+ WHERE pick.id = %s""",
+ ('done', move.picking_id.id))
+ res = cr.fetchall()
+ if len(res) == len(move.picking_id.move_lines):
+ picking_obj.action_move(cr, uid, [move.picking_id.id])
+ wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
+
+ ref = {}
+ done_move_ids = []
+ for move in complete:
+ done_move_ids.append(move.id)
+ if move.picking_id.id not in ref:
+ delivery_id = delivery_obj.create(cr, uid, {
+ 'partner_id': partner_id,
+ 'address_id': address_id,
+ 'date': delivery_date,
+ 'name' : move.picking_id.name,
+ 'picking_id': move.picking_id.id
+ }, context=context)
+ ref[move.picking_id.id] = delivery_id
+ delivery_obj.write(cr, uid, ref[move.picking_id.id], {
+ 'move_delivered' : [(4,move.id)]
+ })
+ return done_move_ids
+