[WIP] Negative quants and packing operations

bzr revid: jco@openerp.com-20130924143220-dcm9u876tfbxzayz
This commit is contained in:
Josse Colpaert 2013-09-24 16:32:20 +02:00
parent 434838b9a5
commit f90c341a24
7 changed files with 249 additions and 98 deletions

View File

@ -323,13 +323,9 @@
Process the delivery of the outgoing shipments
-
!python {model: stock.picking}: |
quant_obj = self.pool.get("stock.quant")
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
picking_obj1 = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg2"))
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
picking_obj1.do_partial(context=context)
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
-
Receive purchase order with 50 kg FIFO Ice Cream at 50 euro/kg
-
@ -351,19 +347,13 @@
Process the reception of purchase order 1
-
!python {model: stock.picking}: |
quant_obj = self.pool.get("stock.quant")
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg")).picking_ids[0]
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
picking_obj.do_partial(context=context)
-
Assert price on product is still the old price as the out move has not been received fully yet
-
!python {model: product.product}: |
quant_obj = self.pool.get("stock.quant")
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
assert self.browse(cr, uid, ref("product_fifo_negative")).standard_price == 70, 'The product price should not have been updated'
-
Receive purchase order with 60 kg FIFO Ice Cream at 80 euro/kg
@ -386,16 +376,10 @@
Process the reception of purchase order 2
-
!python {model: stock.picking}: |
quant_obj = self.pool.get("stock.quant")
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2")).picking_ids[0]
picking_obj.do_partial(context=context)
-
The price of the product should have changed back to 65.0
-
!python {model: product.product}: |
quant_obj = self.pool.get("stock.quant")
quants = quant_obj.search(cr, uid, [('product_id','=',ref("product_fifo_negative"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost, x.id) for x in quant_obj.browse(cr, uid, quants)]
assert self.browse(cr, uid, ref("product_fifo_negative")).standard_price == 65.0, "Product price not updated accordingly. %s found instead of 65" %(self.browse(cr, uid, ref("product_fifo_negative")).standard_price,)

View File

@ -93,6 +93,7 @@ Dashboard / Reports for Warehouse Management will include:
'test/procrule.yml',
'test/shipment.yml',
'test/packing.yml',
'test/packingneg.yml',
],
'installable': True,
'application': True,

View File

@ -178,7 +178,8 @@ class stock_quant(osv.osv):
'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product"),
'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant"),
'packaging_type_id': fields.related('package_id', 'packaging_id', type='many2one', relation='product.packaging', string='Type of packaging', store=True),
'reservation_id': fields.many2one('stock.move', 'Reserved for Move', help="Is this quant reserved for a stock.move?"),
'reservation_id': fields.many2one('stock.move', 'Reserved for Move', help="The move the quant is reserved for"),
'reservation_op_id': fields.many2one('stock.pack.operation', 'Reserved for Pack Operation', help="The operation the quant is reserved for"),
'lot_id': fields.many2one('stock.production.lot', 'Lot'),
'cost': fields.float('Unit Cost'),
'owner_id': fields.many2one('res.partner', 'Owner', help="This is the owner of the quant"),
@ -213,26 +214,27 @@ class stock_quant(osv.osv):
return self.write(cr, uid, toreserve, {'reservation_id': move.id}, context=context)
# add location_dest_id in parameters (False=use the destination of the move)
def quants_move(self, cr, uid, quants, move, context=None):
def quants_move(self, cr, uid, quants, move, lot_id = False, owner_id = False, package_id = False, context=None):
for quant, qty in quants:
#quant may be a browse record or None
quant_record = self.move_single_quant(cr, uid, quant, qty, move, context=context)
quant_record = self.move_single_quant(cr, uid, quant, qty, move, lot_id = lot_id, package_id = package_id, context=context)
#quant_record is the quant newly created or already split
self._quant_reconcile_negative(cr, uid, quant_record, context=context)
def check_preferred_location(self, cr, uid, move, context=None):
return move.location_dest_id
def move_single_quant(self, cr, uid, quant, qty, move, context=None):
def move_single_quant(self, cr, uid, quant, qty, move, lot_id = False, owner_id = False, package_id = False, context=None):
if not quant:
quant = self._quant_create(cr, uid, qty, move, context=context)
quant = self._quant_create(cr, uid, qty, move, lot_id = lot_id, owner_id = owner_id, package_id = package_id, context = context)
else:
self._quant_split(cr, uid, quant, qty, context=context)
# FP Note: improve this using preferred locations
location_to = self.check_preferred_location(cr, uid, move, context=context)
self.write(cr, uid, [quant.id], {
'location_id': location_to.id,
'reservation_id': move.move_dest_id and move.move_dest_id.id or False,
#'reservation_id': move.move_dest_id and move.move_dest_id.id or False,
'history_ids': [(4, move.id)]
})
quant.refresh()
@ -267,9 +269,8 @@ class stock_quant(osv.osv):
#
# Create a quant in the destination location
# Create a negative quant in the source location if it's an internal location
# Reconcile a positive quant with a negative is possible
#
def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, force_location=False, context=None):
def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, package_id = False, force_location=False, context=None):
# FP Note: TODO: compute the right price according to the move, with currency convert
# QTY is normally already converted to main product's UoM
if context is None:
@ -296,6 +297,7 @@ class stock_quant(osv.osv):
negative_vals['qty'] = -qty
negative_vals['cost'] = price_unit
negative_vals['negative_dest_location_id'] = move.location_dest_id.id
negative_vals['package_id'] = package_id
negative_quant_id = self.create(cr, uid, negative_vals, context=context)
vals.update({'propagated_from_id': negative_quant_id})
@ -402,7 +404,7 @@ class stock_quant(osv.osv):
#cr.execute('update stock_quant set reservation_id=NULL where reservation_id=%s', (move.id,))
#need write for related store of remaining qty
related_quants = [x.id for x in move.reserved_quant_ids]
self.write(cr, uid, related_quants, {'reservation_id': False}, context=context)
self.write(cr, uid, related_quants, {'reservation_id': False, 'reservation_op_id': False}, context=context)
return True
#
@ -832,6 +834,39 @@ class stock_picking(osv.osv):
Needed for parameter create
'''
self.rereserve(cr, uid, picking_ids, context=context)
def _reserve_quants_ops_move(self, cr, uid, ops, move, qty, create=False, context=None):
"""
Will return the quantity that got reserved
"""
quant_obj = self.pool.get("stock.quant")
op_obj = self.pool.get("stock.pack.operation")
if create and move.location_id.usage != 'internal':
# Create quants
quant = quant_obj._quant_create(cr, uid, qty, move, lot_id = ops.lot_id and ops.lot_id.id or False, owner_id = ops.owner_id and ops.owner_id.id or False, context=context)
#TODO: location_id -> force location?
quant.write({'reservation_op_id': ops.id, 'location_id': move.location_id.id})
quant_obj.quants_reserve(cr, uid, [(quant, qty)], move, context=context)
return 0
else:
#Quants get
prefered_order = "reservation_id IS NOT NULL"
dom = op_obj._get_domain(cr, uid, ops, context=context)
dom = dom + [('reservation_id', 'not in', [x.id for x in move.picking_id.move_lines])]
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=dom, prefered_order=prefered_order, context=context)
res_qty = qty
for quant in quants:
if quant[0]: #If quant can be reserved
res_qty -= quant[1]
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
quant_obj.write(cr, uid, [x[0].id for x in quants if x[0]], {'reservation_op_id': ops.id}, context=context)
return res_qty
#
# TODO:rereserve should be improved for giving negative quants when a certain lot is not there
@ -844,7 +879,7 @@ class stock_picking(osv.osv):
:return: Tuple (res, res2)
res: dictionary of ops with quantity that could not be processed matching ops and moves
res2: dictionary of moves with quantity that could not be processed
resneg: the negative quants that should be created with ops and move (TODO)
resneg: the negative quants to be created: resneg[move][ops] gives negative quant to be created (TODO:)
tuple of dictionary with quantities of quant operation and product that can not be matched between ops and moves
and dictionary with remaining values on moves
@ -852,13 +887,21 @@ class stock_picking(osv.osv):
quant_obj = self.pool.get("stock.quant")
move_obj = self.pool.get("stock.move")
op_obj = self.pool.get("stock.pack.operation")
pack_obj = self.pool.get("stock.quant.package")
res = {} # Qty still to do from ops
res2 = {} #what is left from moves
resneg= {} #Number of negative quants to create for move : op
for picking in self.browse(cr, uid, picking_ids, context=context):
products_moves = {}
# unreserve everything and initialize res2
for move in picking.move_lines:
quant_obj.quants_unreserve(cr, uid, move, context=context)
res2[move.id] = move.product_qty
resneg[move.id] = {}
if move.state == 'assigned':
products_moves.setdefault(move.product_id.id, []).append(move)
# Resort pack_operation_ids
orderedpackops = picking.pack_operation_ids
@ -869,68 +912,43 @@ class stock_picking(osv.osv):
#Find moves that correspond
if ops.product_id:
#TODO: Should have order such that things with lots and packings are searched first
move_ids = move_obj.search(cr, uid, [('picking_id','=',picking.id), ('product_id', '=', ops.product_id.id), ('remaining_qty', '>', 0.0), ('state', '=', 'assigned')], context=context)
move_ids = ops.product_id.id in products_moves and filter(lambda x: res2[x.id] > 0, products_moves[ops.product_id.id]) or []
qty_to_do = ops.product_qty
while qty_to_do > 0 and move_ids:
move = move_obj.browse(cr, uid, move_ids.pop(), context=context)
if move.remaining_qty > qty_to_do:
move = move_ids.pop()
if res2[move.id] > qty_to_do:
qty = qty_to_do
qty_to_do = 0
else:
qty = move.remaining_qty
qty_to_do -= move.remaining_qty
if create and move.location_id.usage != 'internal':
# Create quants
vals = {
'product_id': move.product_id.id,
'location_id': move.location_id.id,
'qty': qty,
#'cost': price_unit,
'history_ids': [(4, move.id)],
'in_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'company_id': move.company_id.id,
'lot_id': ops.lot_id and ops.lot_id.id or False,
'owner_id': ops.owner_id and ops.owner_id.id or False,
'reservation_id': move.id, #Reserve at once
'package_id': ops.result_package_id and ops.result_package_id.id or False,
}
quant_id = quant_obj.create(cr, uid, vals, context=context)
else:
#Quants get
prefered_order = "reservation_id IS NOT NULL"
dom = op_obj._get_domain(cr, uid, ops, context=context)
dom = dom + [('reservation_id', 'not in', [x.id for x in picking.move_lines])]
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=dom, prefered_order=prefered_order, context=context)
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
#In the end, move quants in correct package
if create:
quant_obj.write(cr, uid, [x[0].id for x in quants if x[0] != None], {'package_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
qty = res2[move.id]
qty_to_do -= res2[move.id]
neg_qty = self._reserve_quants_ops_move(cr, uid, ops, move, qty, create=create, context=context)
if neg_qty > 0:
resneg[move.id].setdefault(ops.id, 0)
resneg [move.id][ops.id] += neg_qty
res2[move.id] -= qty
res[ops.id] = {}
res[ops.id][ops.product_id.id] = qty_to_do
elif ops.package_id:
quants = ops.package_id.quant_ids #_get_package_lines
quants = quant_obj.browse(cr, uid, pack_obj.get_content(cr, uid, [ops.package_id.id], context=context))
quants = [x for x in quants if x.qty > 0] #Negative quants should not be moved
for quant in quants:
move_ids = move_obj.search(cr, uid, [('picking_id', '=', picking.id), ('product_id', '=', quant.product_id.id), ('remaining_qty', '>', 0.0)])
move_ids = quant.product_id.id in products_moves and filter(lambda x: res2[x.id] > 0, products_moves[quant.product_id.id]) or []
qty_to_do = quant.qty
while qty_to_do > 0 and move_ids:
move = move_obj.browse(cr, uid, move_ids.pop(), context=context)
if move.remaining_qty > qty_to_do:
move = move_ids.pop()
if res2[move.id] > qty_to_do:
qty = qty_to_do
qty_to_do = 0.0
else:
qty = move.remaining_qty
qty_to_do -= move.remaining_qty
qty = res2[move.id]
qty_to_do -= res2[move.id]
quant_obj.quants_reserve(cr, uid, [(quant, qty)], move, context=context)
quant_obj.write(cr, uid, [quant.id], {'reservation_op_id': ops.id}, context=context)
res2[move.id] -= qty
res.setdefault(ops.id, {}).setdefault(quant.product_id.id, 0.0)
res[ops.id][quant.product_id.id] += qty_to_do
#Add parent package
if create:
self.pool.get("stock.quant.package").write(cr, uid, [ops.package_id.id], {'parent_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
return (res, res2)
return (res, res2, resneg)
def do_partial(self, cr, uid, picking_ids, context=None):
@ -946,6 +964,7 @@ class stock_picking(osv.osv):
else:
#First thing that needs to happen is rereserving the quants
res = self.rereserve(cr, uid, [picking.id], create = True, context = context) #This time, quants need to be created
resneg = res[2]
orig_moves = picking.move_lines
orig_qtys = {}
for orig in orig_moves:
@ -957,8 +976,7 @@ class stock_picking(osv.osv):
product = self.pool.get('product.product').browse(cr, uid, prod, context=context)
qty = res[0][ops][prod]
if qty > 0:
#TODO: Maybe should try to reserve quants? / Make copy instead of create, but have to put move_dest_id False then e.g. link purchase order line?
quant = False
#Create moves for products too many on operation
move_id = stock_move_obj.create(cr, uid, {
'name': product.name,
'product_id': product.id,
@ -967,12 +985,17 @@ class stock_picking(osv.osv):
'location_id': picking.location_id.id,
'location_dest_id': picking.location_dest_id.id,
'picking_id': picking.id,
'reserved_quant_ids': quant and [(4, quant.id)] or [],
'picking_type_id': picking.picking_type_id.id,
'group_id': picking.group_id.id,
}, context=context)
stock_move_obj.action_confirm(cr, uid, [move_id], context=context)
move = stock_move_obj.browse(cr, uid, move_id, context=context)
ops_rec = self.pool.get("stock.pack.operation").browse(cr, uid, ops, context=context)
resneg[move_id] = {}
resneg[move_id][ops] = self._reserve_quants_ops_move(cr, uid, ops_rec, move, qty, create=True, context=context)
extra_moves.append(move_id)
res2 = res[1]
#Backorder
for move in res2.keys():
if res2[move] > 0:
mov = stock_move_obj.browse(cr, uid, move, context=context)
@ -983,13 +1006,12 @@ class stock_picking(osv.osv):
orig_moves = [x for x in orig_moves if res[1][x.id] < orig_qtys[x.id]]
for move in orig_moves + stock_move_obj.browse(cr, uid, extra_moves, context=context):
if move.state == 'draft':
self.pool.get('stock.move').action_confirm(cr, uid, [move.id],
context=context)
self.pool.get('stock.move').action_confirm(cr, uid, [move.id], context=context)
todo.append(move.id)
elif move.state in ('assigned','confirmed'):
todo.append(move.id)
if len(todo):
self.pool.get('stock.move').action_done(cr, uid, todo, context=context)
self.pool.get('stock.move').action_done(cr, uid, todo, negatives = resneg, context=context)
picking.refresh()
self._create_backorder(cr, uid, picking, context=context)
return True
@ -1634,7 +1656,7 @@ class stock_move(osv.osv):
# res[move.id] = [x.id for x in move.reserved_quant_ids]
# return res
def action_done(self, cr, uid, ids, context=None):
def action_done(self, cr, uid, ids, negatives = False, context=None):
""" Makes the move done and if all moves are done, it will finish the picking.
If quants are not assigned yet, it should assign them
Putaway strategies should be applied
@ -1642,13 +1664,20 @@ class stock_move(osv.osv):
"""
context = context or {}
quant_obj = self.pool.get("stock.quant")
ops_obj = self.pool.get("stock.pack.operation")
pack_obj = self.pool.get("stock.quant.package")
todo = [move.id for move in self.browse(cr, uid, ids, context=context) if move.state == "draft"]
if todo:
self.action_confirm(cr, uid, todo, context=context)
pickings = set()
for move in self.browse(cr, uid, ids, context=context):
# if negatives and negatives[move.id]:
# for ops in negatives[move.id].keys():
# quants_to_move = [(None, negatives[move.id, x) for x in negatives]
# quant_obj.quants_move(cr, uid, quants_to_move, move, context=context)
if move.picking_id:
pickings.add(move.picking_id.id)
qty = move.product_qty
@ -1657,17 +1686,33 @@ class stock_move(osv.osv):
# quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, context=context)
# quant_obj.quants_move(cr, uid, quants, move, location_dest_id, context=context)
# should replace the above 2 lines
domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id), ('qty', '>', 0)]
dom = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id), ('qty', '>', 0)]
prefered_order = 'reservation_id'
# if lot_id:
# prefered_order = 'lot_id<>' + lot_id + ", " + prefered_order
# if pack_id:
# prefered_order = 'pack_id<>' + pack_id + ", " + prefered_order
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = prefered_order, context=context)
#Will move all quants_get and as such create negative quants
quant_obj.quants_move(cr, uid, quants, move, context=context)
if move.picking_id and move.picking_id.pack_operation_ids:
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty - move.remaining_qty, domain=dom, prefered_order = prefered_order, context=context)
quant_obj.quants_move(cr, uid, quants, move, context=context)
for negative_op in negatives[move.id].keys():
ops = ops_obj.browse(cr, uid, negative_op, context=context)
negatives[move.id][negative_op] = quant_obj.quants_move(cr, uid, [(None, negatives[move.id][negative_op])], move,
lot_id = ops.lot_id and ops.lot_id.id or False,
owner_id = ops.owner_id and ops.owner_id.id or False,
package_id = ops.package_id and ops.package_id.id or False, context=context)
#Packing:
reserved_ops = list(set([x.reservation_op_id.id for x in move.reserved_quant_ids]))
for ops in ops_obj.browse(cr, uid, reserved_ops, context=context):
if ops.product_id:
quant_obj.write(cr, uid, [x.id for x in ops.reserved_quant_ids], {'package_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
else:
pack_obj.write(cr, uid, [ops.package_id.id], {'parent_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
else:
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=dom, prefered_order = prefered_order, context=context)
#Will move all quants_get and as such create negative quants
quant_obj.quants_move(cr, uid, quants, move, context=context)
quant_obj.quants_unreserve(cr, uid, move, context=context)
#
#Check moves that were pushed
if move.move_dest_id.state in ('waiting', 'confirmed'):
@ -2280,6 +2325,17 @@ class stock_package(osv.osv):
class stock_pack_operation(osv.osv):
_name = "stock.pack.operation"
_description = "Packing Operation"
def _get_remaining_qty(self, cr, uid, ids, context=None):
res = {}
for ops in self.browse(cr, uid, ids, context=context):
qty = ops.product_qty
for quant in ops.reserved_quant_ids:
qty -= quant.qty
res[ops.id] = qty
return res
_columns = {
'picking_id': fields.many2one('stock.picking', 'Stock Picking', help='The stock operation where the packing has been made', required=True),
'product_id': fields.many2one('product.product', 'Product', ondelete="CASCADE"), # 1
@ -2294,6 +2350,8 @@ class stock_pack_operation(osv.osv):
#'update_cost': fields.boolean('Need cost update'),
'cost': fields.float("Cost", help="Unit Cost for this product line"),
'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
'reserved_quant_ids': fields.one2many('stock.quant', 'reservation_op_id', string='Reserved Quants', readonly=True, help='Quants reserved for this operation'),
'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'),
}
_defaults = {

View File

@ -85,7 +85,7 @@
backorder = self.search(cr, uid, [('backorder_id', '=', ref('pick1'))])
assert not backorder, ""
#Check extra moves created
assert len(picking.move_lines) == 1, ""
assert len(picking.move_lines) == 1, ""
-
Make a delivery order of 300 pieces to the customer
-
@ -114,9 +114,9 @@
for rec in delivery_id.pack_operation_ids:
if rec.package_id.name == 'Pallet 2':
lot_ids = self.pool.get("stock.production.lot").search(cr, uid, [('product_id', '=', ref('product1')), ('name','=','Lot A')])
stock_pack.write(cr, uid, rec.id, {'product_id': ref('product1'), 'product_qty': 20, 'lot_id': lot_ids[0]}, context=context)
stock_pack.write(cr, uid, [rec.id], {'product_id': ref('product1'), 'product_qty': 20, 'lot_id': lot_ids[0]}, context=context)
if rec.package_id.name == 'Pallet 3':
stock_pack.write(cr, uid, rec.id, {'product_id': ref('product1'),'product_qty': 10}, context=context)
stock_pack.write(cr, uid, [rec.id], {'product_id': ref('product1'),'product_qty': 10}, context=context)
-
Process this picking
-

View File

@ -0,0 +1,113 @@
-
Create a new "negative" stockable product
-
!record {model: product.product, id: product_neg}:
name: Negative product
type: product
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 2.0
qty: 5.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
Create an incoming picking for this product of 300 PCE from suppliers to stock
-
!record {model: stock.picking, id: pick_neg}:
name: Incoming picking
partner_id: base.res_partner_2
picking_type_id: picking_type_in
move_lines:
- product_id: product_neg
product_uom_qty: 300.00
location_id: stock_location_suppliers
location_dest_id: stock_location_stock
-
Confirm and assign picking and prepare partial
-
!python {model: stock.picking}: |
self.action_confirm(cr, uid, [ref('pick_neg')], context=context)
self.do_prepare_partial(cr, uid, [ref('pick_neg')], context=context)
-
Put 120 pieces on Palneg 1 (package), 120 pieces on Palneg 2 with lot A and 60 pieces on Palneg 3
-
!python {model: stock.picking}: |
#Change quantity of first to 120 and create 2 others quant operations
record = self.browse(cr, uid, ref('pick_neg'), context=context)
stock_pack = self.pool.get('stock.pack.operation')
stock_quant_pack = self.pool.get('stock.quant.package')
#create lot A
lot_a = self.pool.get('stock.production.lot').create(cr, uid, {'name': 'Lot neg', 'product_id': ref('product_neg')}, context=context)
#create package
package1 = stock_quant_pack.create(cr, uid, {'name': 'Palneg 1'}, context=context)
package2 = stock_quant_pack.create(cr, uid, {'name': 'Palneg 2'}, context=context)
package3 = stock_quant_pack.create(cr, uid, {'name': 'Palneg 3'}, context=context)
#Create package for each line and assign it as result_package_id
#create pack operation
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 120})
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120}, context=context)
new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'result_package_id': package3, 'product_qty': 60}, context=context)
-
Transfer the reception
-
!python {model: stock.picking}: |
self.do_partial(cr, uid, [ref('pick_neg')], context=context)
-
Make a delivery order of 300 pieces to the customer
-
!record {model: stock.picking, id: delivery_order_neg}:
name: outgoing picking
partner_id: base.res_partner_4
picking_type_id: stock.picking_type_out
move_lines:
- product_id: product_neg
product_uom_qty: 300.00
location_id: stock_location_stock
location_dest_id: stock_location_customers
-
Assign and confirm
-
!python {model: stock.picking}: |
self.action_confirm(cr, uid, [ref('delivery_order_neg')], context=context)
self.action_assign(cr, uid, [ref('delivery_order_neg')])
-
Instead of doing the 300 pieces, you decide to take pallet 1 (do not mention product in operation here) and 140 pieces from lot A/pallet 2 and 10 pieces from pallet 3
-
!python {model: stock.picking}: |
stock_pack = self.pool.get('stock.pack.operation')
self.do_prepare_partial(cr, uid, [ref('delivery_order_neg')], context=context)
delivery_id = self.browse(cr, uid, ref('delivery_order_neg'), context=context)
for rec in delivery_id.pack_operation_ids:
if rec.package_id.name == 'Palneg 2':
lot_ids = self.pool.get("stock.production.lot").search(cr, uid, [('product_id', '=', ref('product_neg')), ('name','=','Lot neg')])
stock_pack.write(cr, uid, [rec.id], {'product_id': ref('product_neg'), 'product_qty': 140, 'lot_id': lot_ids[0]}, context=context)
if rec.package_id.name == 'Palneg 3':
stock_pack.write(cr, uid, [rec.id], {'product_id': ref('product_neg'),'product_qty': 10}, context=context)
-
Process this picking
-
!python {model: stock.picking}: |
self.do_partial(cr, uid, [ref('delivery_order_neg')], context=context)
-
Check the quants that you have 120 pieces pallet 1 in customers, 100 pieces pallet 2 in stock and 20 with customers and 50 in stock, 10 in customers from pallet 3
-
!python {model: stock.quant}: |
reco_id = self.search(cr ,uid , [('product_id','=',ref('product_neg'))], context=context)
for rec in self.browse(cr, uid, reco_id, context=context):
if rec.package_id.name == 'Palneg 1' and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 120, "Should have 120 pieces on pallet 1"
elif rec.package_id.name == 'Palneg 2' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == -20, "Should have -20 pieces in stock on pallet 2"
assert rec.lot_id.name == 'Lot neg', "It should have kept its Lot"
elif rec.lot_id.name == 'Lot neg' and rec.location_id.id == ref('stock_location_customers'):
assert ((rec.qty == 20 or rec.qty == 120) and not rec.package_id), "Should have 140 pieces (120+20) in customer location from pallet 2 and lot A"
elif rec.package_id.name == 'Palneg 3' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 30 or rec.qty == 20, "Should have 30 and 20 pieces in stock on pallet 3"
elif not rec.package_id and not rec.lot_id and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 10, "Should have 10 pieces in customer location from pallet 3"
else:
assert False, "Unrecognized quant"

View File

@ -124,11 +124,13 @@ class stock_quant(osv.osv):
else:
self._create_account_move_line(cr, uid, quant, move, acc_valuation, acc_dest, journal_id, context=ctx)
def move_single_quant(self, cr, uid, quant, qty, move, context=None):
quant_record = super(stock_quant, self).move_single_quant(cr, uid, quant, qty, move, context=context)
def move_single_quant(self, cr, uid, quant, qty, move, lot_id=False, owner_id=False, package_id= False, context=None):
quant_record = super(stock_quant, self).move_single_quant(cr, uid, quant, qty, move, lot_id = lot_id, owner_id = owner_id, package_id = package_id, context=context)
self._account_entry_move(cr, uid, quant_record, move, context=context)
return quant_record
def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
"""
Return the accounts and journal to use to post Journal Entries for the real-time
@ -223,9 +225,9 @@ class stock_quant(osv.osv):
class stock_move(osv.osv):
_inherit = "stock.move"
def action_done(self, cr, uid, ids, context=None):
def action_done(self, cr, uid, ids, negatives = False, context=None):
self.product_price_update_before_done(cr, uid, ids, context=context)
super(stock_move, self).action_done(cr, uid, ids, context=context)
super(stock_move, self).action_done(cr, uid, ids, negatives=negatives, context=context)
self.product_price_update_after_done(cr, uid, ids, context=context)
def _store_average_cost_price(self, cr, uid, move, context=None):

View File

@ -68,20 +68,13 @@
-
!python {model: stock.picking}: |
po_id = self.pool.get('purchase.order').search(cr, uid, [('partner_id', '=', ref('supplier_dropship'))])
print "Picking_ids", self.pool.get("purchase.order").browse(cr, uid, po_id[0]).picking_ids
picking_id = self.search(cr, uid, [('purchase_id', '=', po_id[0])])
print "Pick_id", picking_id
print [(x.move_lines[0].product_id.name, x.move_lines[0].product_uom_qty) for x in self.browse(cr, uid, picking_id)]
self.do_partial(cr, uid, picking_id)
-
Check one quant was created in Customers location with 200 pieces and one move in the history_ids
-
!python {model: stock.quant}: |
quant_obj = self.pool.get("stock.quant")
quant_ids = self.search(cr, uid, [('product_id', '=', ref("drop_shop_product"))])
print "Quants:", [(x.qty, x.location_id.name, x.in_date, x.cost) for x in quant_obj.browse(cr, uid, quant_ids)]
quant_ids = self.search(cr, uid, [('location_id', '=', ref('stock.stock_location_customers')),('qty', '=', 200), ('product_id', '=', ref("drop_shop_product"))])
print "Quants", quant_ids
assert quant_ids, 'No Quant found'
assert len(quant_ids) == 1
assert len(self.browse(cr, uid, quant_ids)[0].history_ids) == 1