From 9c224029fd648d3a594ac6b3f8dbf20e63d3239f Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 21 May 2013 19:26:27 +0200 Subject: [PATCH] [IMP] Turn qty_remaining into a calculated field bzr revid: jco@openerp.com-20130521172627-bgbaj398y5bsg5jo --- addons/purchase/test/fifo_price.yml | 34 +++++++++++- addons/stock/stock.py | 8 +-- addons/stock/stock_fifo_lifo.py | 80 ++++++++++++++++++++++++----- 3 files changed, 100 insertions(+), 22 deletions(-) diff --git a/addons/purchase/test/fifo_price.yml b/addons/purchase/test/fifo_price.yml index 209e3f75424..35094de735b 100644 --- a/addons/purchase/test/fifo_price.yml +++ b/addons/purchase/test/fifo_price.yml @@ -322,7 +322,7 @@ for move in self.pool.get("stock.move").browse(cr, uid, list): print move.price_unit, move.product_qty, move.qty_remaining, move.type, move.date - - Receive purchase order with 200 kgm FIFO Ice Cream I create a draft Purchase Order for second shipment for 30 kg at 80 euro + Receive purchase order with 100 kgm FIFO Ice Cream at 50 euro/kg - !record {model: purchase.order, id: purchase_order_fifo_neg}: partner_id: base.res_partner_3 @@ -330,7 +330,7 @@ pricelist_id: 1 order_line: - product_id: product_fifo_icecream - product_qty: 200.0 + product_qty: 100.0 product_uom: product.product_uom_categ_kgm price_unit: 50.0 name: 'FIFO Ice Cream' @@ -345,6 +345,36 @@ pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg")).picking_ids partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) self.do_partial(cr, uid, [partial_id]) +- + Assert price on product is 50 +- + !python {model: product.product}: | + assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 50.0 + +- + Receive purchase order with 100 kgm FIFO Ice Cream at 80 euro/kg +- + !record {model: purchase.order, id: purchase_order_fifo_neg2}: + partner_id: base.res_partner_3 + location_id: stock.stock_location_stock + pricelist_id: 1 + order_line: + - product_id: product_fifo_icecream + product_qty: 100.0 + product_uom: product.product_uom_categ_kgm + price_unit: 80 + name: 'FIFO Ice Cream' +- + I confirm the second negative purchase order +- + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_fifo_neg2} +- + Process the reception of purchase order 2 +- + !python {model: stock.partial.picking}: | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2")).picking_ids + partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) + self.do_partial(cr, uid, [partial_id]) - Print price - diff --git a/addons/stock/stock.py b/addons/stock/stock.py index f2ce2f23a57..9dd9e470299 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2714,14 +2714,11 @@ class stock_move(osv.osv): match_id = matching_obj.create(cr, uid, matchvals, context=context) res[move.id].append(match_id) move_in = self.browse(cr, uid, match[0], context=context) - #Reduce remaining quantity - self.write(cr, uid, match[0], { 'qty_remaining': move_in.qty_remaining - match[3]}, context=context) price_amount += match[1] * match[2] amount += match[1] #Write price on out move if product.qty_available >= product_uom_qty and product.cost_method in ['fifo', 'lifo']: - self.write(cr, uid, move.id, {'price_unit': price_amount / amount, - 'qty_remaining': move.qty_remaining - amount}, context=context) + self.write(cr, uid, move.id, {'price_unit': price_amount / amount}, context=context) product_obj.write(cr, uid, product.id, {'standard_price': price_amount / product_uom_qty}, context=ctx) else: new_price = uom_obj._compute_price(cr, uid, product.uom_id.id, product.standard_price, @@ -2774,8 +2771,7 @@ class stock_move(osv.osv): for match in matching_obj.browse(cr, uid, matches, context=context): in_mov = self.browse(cr, uid, match.move_in_id.id, context=context) amount += uom_obj._compute_qty(cr, uid, in_mov.product_uom.id, in_mov.product_qty, out_mov.product_uom.id) - total_price += amount * uom_obj._compute_price(cr, uid, in_mov.product_uom.id, in_mov.price_unit, - out_mov.product_uom.id) + total_price += amount * uom_obj._compute_price(cr, uid, in_mov.product_uom.id, in_mov.price_unit, out_mov.product_uom.id) if amount > 0.0: self.write(cr, uid, [out_mov.id], {'price_unit': total_price / amount}, context=context) product_obj.write(cr, uid, [product.id], {'standard_price': total_price / amount}, context=ctx) diff --git a/addons/stock/stock_fifo_lifo.py b/addons/stock/stock_fifo_lifo.py index 6c6509f94a8..94d0cd00408 100644 --- a/addons/stock/stock_fifo_lifo.py +++ b/addons/stock/stock_fifo_lifo.py @@ -89,23 +89,75 @@ class product_product (osv.osv): return tuples class stock_move(osv.osv): + + def _get_moves(self, cr, uid, ids, context=None): + res = [] + #search_dom = ['|', '&', ('location_id.usage','=', 'internal'), ('location_dest_id.usage','!=', 'internal'), '&', ('location_id.usage', '=')] + #res_ids = self.search(cr, uid, [], context=context) + for move in self.browse(cr, uid, ids, context=context): + move_inorout = move.location_id and move.location_dest_id + move_inorout = move_inorout and ((move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal') or (move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal' )) + if move_inorout: + res.append(move.id) + return res + + + def _get_moves_from_matchings(self, cr, uid, ids, context=None): + match_obj = self.pool.get("stock.move.matching") + res = {} + for match in match_obj.browse(cr, uid, ids, context=context): + if match.move_out_id.id not in res: + res[match.move_out_id.id] = True + if match.move_in_id.id not in res: + res[match.move_in_id.id] = True + return res.keys() + + def _get_qty_remaining (self, cr, uid, ids, field_names, arg, context=None): + ''' + This function calculates how much of the stock move that still needs to be matched + ''' + match_obj = self.pool.get("stock.move.matching") + uom_obj = self.pool.get("product.uom") + res = {} + for move in self.browse(cr, uid, ids, context=context): + move_out = move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal' + move_in = move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal' + if move_in: + #Search all matchings + matches = match_obj.search(cr, uid, [('move_in_id', '=', move.id)], context=context) + qty = move.product_qty + for match in match_obj.browse(cr, uid, matches, context=context): + qty -= uom_obj._compute_qty(cr, uid, match.move_out_id.product_uom.id, match.qty, move.product_uom.id) + res[move.id] = qty + elif move_out: + #Search all matchings, but from the other side + matches = match_obj.search(cr, uid, [('move_out_id', '=', move.id)], context=context) + qty = move.product_qty + for match in match_obj.browse(cr, uid, matches, context=context): + qty -= match.qty + res[move.id] = qty + return res _inherit = 'stock.move' - _columns = {'qty_remaining': fields.float("Remaining Qty"), + _columns = {'qty_remaining': fields.function(_get_qty_remaining, type="float", string="Remaining quantity to be matched", + store = {'stock.move.matching': (_get_moves_from_matchings, ['qty', 'move_in_id', 'move_out_id'], 10), + 'stock.move': (_get_moves, ['product_qty', 'product_uom', 'location_id', 'location_dest_id', 'company_id'], 10)}), #locations and company_id not necessary? 'matching_ids_in': fields.one2many('stock.move.matching', 'move_in_id'), 'matching_ids_out':fields.one2many('stock.move.matching', 'move_out_id'), } - - def create(self, cr, uid, vals, context=None): - if 'product_qty' in vals: - vals['qty_remaining'] = vals['product_qty'] - res = super(stock_move, self).create(cr, uid, vals, context=context) - return res - def write(self, cr, uid, ids, vals, context=None): - if 'product_qty' in vals: - vals['qty_remaining'] = vals['product_qty'] - res = super(stock_move, self).write(cr, uid, ids, vals, context=context) - return res + + +# def create(self, cr, uid, vals, context=None): +# if 'product_qty' in vals: +# vals['qty_remaining'] = vals['product_qty'] +# res = super(stock_move, self).create(cr, uid, vals, context=context) +# return res +# +# def write(self, cr, uid, ids, vals, context=None): +# if 'product_qty' in vals: +# vals['qty_remaining'] = vals['product_qty'] +# res = super(stock_move, self).write(cr, uid, ids, vals, context=context) +# return res class stock_move_matching(osv.osv): @@ -114,8 +166,8 @@ class stock_move_matching(osv.osv): _columns = { 'move_in_id': fields.many2one('stock.move', 'Stock move in', required=True), 'move_out_id': fields.many2one('stock.move', 'Stock move out', required=True), - 'qty': fields.float('Quantity', required=True), + 'qty': fields.float('Quantity in UoM of out', required=True), 'price_unit':fields.related('move_in_id', 'price_unit', string="Unit price", type="float"), - 'price_unit_out': fields.float('Unit price out') + 'price_unit_out': fields.float('Unit price out'), }