[FIX] negative quants fixing

bzr revid: qdp-launchpad@openerp.com-20130923143309-42mo2trr8jy6m5qk
This commit is contained in:
Quentin (OpenERP) 2013-09-23 16:33:09 +02:00
parent ba60391f07
commit 4bea4bc1f3
2 changed files with 97 additions and 47 deletions

View File

@ -215,7 +215,10 @@ class stock_quant(osv.osv):
# add location_dest_id in parameters (False=use the destination of the move)
def quants_move(self, cr, uid, quants, move, context=None):
for quant, qty in quants:
self.move_single_quant(cr, uid, quant, qty, move, context=context)
#quant may be a browse record or None
quant_record = self.move_single_quant(cr, uid, quant, qty, move, 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
@ -233,7 +236,6 @@ class stock_quant(osv.osv):
'history_ids': [(4, move.id)]
})
quant.refresh()
self._quant_reconcile_negative(cr, uid, quant, context=context)
return quant
def quants_get(self, cr, uid, location, product, qty, domain=None, prefered_order=False, reservedcontext=None, restrict_lot_id=False, restrict_partner_id=False, context=None):
@ -317,19 +319,28 @@ class stock_quant(osv.osv):
move = m
return move
def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
remaining_solving_quant = self._quant_split(cr, uid, quant, qty, context=context)
remaining_to_solve_quant = self._quant_split(cr, uid, to_solve_quant, qty, context=context)
remaining_neg_quant = self._quant_split(cr, uid, quant_neg, -qty, context=context)
#if the reconciliation was not complete, we need to link together the remaining parts
if remaining_to_solve_quant and remaining_neg_quant:
self.write(cr, uid, remaining_to_solve_quant.id, {'propagated_from_id': remaining_neg_quant.id}, context=context)
#delete the reconciled quants, as it is replaced by the solving quant
self.unlink(cr, SUPERUSER_ID, [quant_neg.id, to_solve_quant.id], context=context)
#call move_single_quant to ensure recursivity if necessary and do the stock valuation
self.move_single_quant(cr, uid, quant, qty, move, context=context)
return remaining_solving_quant, remaining_to_solve_quant
#def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
# move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
# remaining_solving_quant = self._quant_split(cr, uid, quant, qty, context=context)
# remaining_to_solve_quant = self._quant_split(cr, uid, to_solve_quant, qty, context=context)
# remaining_neg_quant = self._quant_split(cr, uid, quant_neg, -qty, context=context)
# #if the reconciliation was not complete, we need to link together the remaining parts
# if remaining_to_solve_quant and remaining_neg_quant:
# self.write(cr, uid, remaining_to_solve_quant.id, {'propagated_from_id': remaining_neg_quant.id}, context=context)
# #delete the reconciled quants, as it is replaced by the solving quant
# if remaining_neg_quant:
# otherquant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id)], context=context)
# self.write(cr, uid, otherquant_ids, {'propagated_from_id': remaining_neg_quant.id}, context=context)
# self.unlink(cr, SUPERUSER_ID, [quant_neg.id, to_solve_quant.id], context=context)
# #call move_single_quant to ensure recursivity if necessary and do the stock valuation
# self.move_single_quant(cr, uid, quant, qty, move, context=context)
# return remaining_solving_quant, remaining_to_solve_quant
def _quants_merge(self, cr, uid, solved_quant_ids, solving_quant, context=None):
path = []
for move in solving_quant.history_ids:
path.append((4, move.id))
self.write(cr, uid, solved_quant_ids, {'history_ids': path}, context=context)
def _quant_reconcile_negative(self, cr, uid, quant, context=None):
"""
@ -344,14 +355,36 @@ class stock_quant(osv.osv):
for quant_neg, qty in quants:
if not quant_neg:
continue
to_solve_quant = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id), ('id', '!=', solving_quant.id)], context=context)
if not to_solve_quant:
to_solve_quant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id)], context=context)
if not to_solve_quant_ids:
continue
to_solve_quant = self.browse(cr, uid, to_solve_quant[0], context=context)
solving_quant, dummy = self._reconcile_single_negative_quant(cr, uid, to_solve_quant, solving_quant, quant_neg, qty, context=context)
solving_qty = qty
solved_quant_ids = []
for to_solve_quant in self.browse(cr, uid, to_solve_quant_ids, context=context):
if solving_qty <= 0:
continue
solved_quant_ids.append(to_solve_quant.id)
self._quant_split(cr, uid, to_solve_quant, min(solving_qty, to_solve_quant.qty), context=context)
solving_qty -= min(solving_qty, to_solve_quant.qty)
#merge history (and cost?)
self._quants_merge(cr, uid, solved_quant_ids, solving_quant, context=context)
remaining_solving_quant = self._quant_split(cr, uid, solving_quant, qty, context=context)
remaining_neg_quant = self._quant_split(cr, uid, quant_neg, -qty, context=context)
#if the reconciliation was not complete, we need to link together the remaining parts
if remaining_neg_quant:
remaining_to_solve_quant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id), ('id', 'not in', solved_quant_ids)], context=context)
if remaining_to_solve_quant_ids:
self.write(cr, uid, remaining_to_solve_quant_ids, {'propagated_from_id': remaining_neg_quant.id}, context=context)
#price update + accounting entries adjustments
self._price_update(cr, uid, solved_quant_ids, solving_quant.cost, context=context)
#delete the reconciled quants, as it is replaced by the solving quant
self.unlink(cr, SUPERUSER_ID, [quant_neg.id, solving_quant.id], context=context)
solving_quant = remaining_solving_quant
def _price_update(self, cr, uid, quant, newprice, context=None):
self.write(cr, uid, [quant.id], {'cost': newprice}, context=context)
#solving_quant, dummy = self._reconcile_single_negative_quant(cr, uid, to_solve_quant, solving_quant, quant_neg, qty, context=context)
def _price_update(self, cr, uid, ids, newprice, context=None):
self.write(cr, uid, ids, {'cost': newprice}, context=context)
def write(self, cr, uid, ids, vals, context=None):
#We want to trigger the move with nothing on reserved_quant_ids for the store of the remaining quantity

View File

@ -59,27 +59,41 @@ class stock_quant(osv.osv):
return line.cost * line.qty
return super(stock_quant, self)._get_inventory_value(cr, uid, line, prodbrow, context=context)
# FP Note: this is where we should post accounting entries for adjustment
def _price_update(self, cr, uid, quant, newprice, context=None):
super(stock_quant, self)._price_update(cr, uid, quant, newprice, context=context)
# TODO: generate accounting entries
def _price_update(self, cr, uid, quant_ids, newprice, context=None):
if context is None:
context = {}
ctx = context.copy()
for quant in self.browse(cr, uid, quant_ids, context=context):
move = self._get_latest_move(cr, uid, quant, context=context)
ctx['force_valuation_amount'] = newprice - quant.cost
# this is where we post accounting entries for adjustment
self._account_entry_move(cr, uid, quant, move, context=ctx)
#update the standard price of the product, only if we would have done it if we'd have had enough stock at first, which means
#1) the product cost's method is 'real'
#2) we just fixed a negative quant caused by an outgoing shipment
if not quant.product_id.cost_method == 'real' and quant.location_id.usage != 'internal':
self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)
super(stock_quant, self)._price_update(cr, uid, quant_ids, newprice, context=context)
"""
Accounting Valuation Entries
location_from: can be None if it's a new quant
"""
def _account_entry_move(self, cr, uid, quant, location_from, location_to, move, context=None):
def _account_entry_move(self, cr, uid, quant, move, context=None):
location_from = move.location_id
location_to = move.location_dest_id
if context is None:
context = {}
if quant.product_id.valuation != 'real_time':
return False
if quant.lot_id and quant.lot_id.partner_id:
if quant.owner_id:
#if the quant isn't owned by the company, we don't make any valuation entry
return False
if quant.qty <= 0 or quant.propagated_from_id:
#we don't make any stock valuation for negative quants because we may not know the real cost price.
#The valuation will be made at the time of the reconciliation of the negative quant.
if quant.qty <= 0:
#we don't make any stock valuation for negative quants because the valuation is already made for the counterpart.
#At that time the valuation will be made at the product cost price and afterward there will be new accounting entries
#to make the adjustments when we know the real cost price.
return False
company_from = self._location_owner(cr, uid, quant, location_from, context=context)
company_to = self._location_owner(cr, uid, quant, location_to, context=context)
@ -109,11 +123,9 @@ class stock_quant(osv.osv):
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):
location_from = quant and quant.location_id or False
quant = super(stock_quant, self).move_single_quant(cr, uid, quant, qty, move, context=context)
quant.refresh()
self._account_entry_move(cr, uid, quant, location_from, quant.location_id, move, context=context)
return quant
quant_record = super(stock_quant, self).move_single_quant(cr, uid, quant, qty, move, 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):
"""
@ -153,7 +165,12 @@ class stock_quant(osv.osv):
Generate the account.move.line values to post to track the stock valuation difference due to the
processing of the given quant.
"""
valuation_amount = quant.product_id.cost_method == 'real' and quant.cost or quant.product_id.standard_price
if context is None:
context = {}
if context.get('force_valuation_amount'):
valuation_amount = context.get('force_valuation_amount')
else:
valuation_amount = quant.product_id.cost_method == 'real' and quant.cost or quant.product_id.standard_price
partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
debit_line_vals = {
'name': move.name,
@ -187,17 +204,17 @@ class stock_quant(osv.osv):
'line_id': move_lines,
'ref': move.picking_id and move.picking_id.name}, context=context)
def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
quant_neg_position = quant_neg.negative_dest_location_id.usage
remaining_solving_quant, remaining_to_solve_quant = super(stock_quant, self)._reconcile_single_negative_quant(cr, uid, to_solve_quant, quant, quant_neg, qty, context=context)
#update the standard price of the product, only if we would have done it if we'd have had enough stock at first, which means
#1) there isn't any negative quant anymore
#2) the product cost's method is 'real'
#3) we just fixed a negative quant caused by an outgoing shipment
if not remaining_to_solve_quant and move.product_id.cost_method == 'real' and quant_neg_position != 'internal':
self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)
return remaining_solving_quant, remaining_to_solve_quant
#def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
# move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
# quant_neg_position = quant_neg.negative_dest_location_id.usage
# remaining_solving_quant, remaining_to_solve_quant = super(stock_quant, self)._reconcile_single_negative_quant(cr, uid, to_solve_quant, quant, quant_neg, qty, context=context)
# #update the standard price of the product, only if we would have done it if we'd have had enough stock at first, which means
# #1) there isn't any negative quant anymore
# #2) the product cost's method is 'real'
# #3) we just fixed a negative quant caused by an outgoing shipment
# if not remaining_to_solve_quant and move.product_id.cost_method == 'real' and quant_neg_position != 'internal':
# self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)
# return remaining_solving_quant, remaining_to_solve_quant
class stock_move(osv.osv):
_inherit = "stock.move"