[FIX] stock_account: account/valuation inconsistencies

When the product price is divided per product unit, inconsistencies
may arise between the real stock valuation and the stock valuation
account. This is likely to happen when a product is bought in a UoM
different from the standard UoM of the product.

A numerical example is the following: a box of 13 is bought for 15.00.
An amount of 15.00 is recorded when the products enter the stock. If the
product leave the stock one at a time, 13 entries of 1.15 are recorded
(15.00/13 = 1.153846... ≈ 1.15), which is then equal to
13 * 1.15 = 14.95. In this case, All the products have left the stock
(stock valuation is zero), but 5 cents remain on the account.

This is of course even worse the higher the ratio is. For example, a
box of 4.00 split into 1000 units sold piece by piece will never be
recorded when a product leaves the stock.

The fix is to record the rounding difference on a specific quant. In the
previous example, instead of adding 1.153846... on the unit cost of the
13 units, we do the following:
- 12 units to which we add 1.15 on unit cost
- 1 unit to which we add 1.20 on unit cost

opw-675222
This commit is contained in:
Nicolas Martinelli 2016-04-19 18:22:27 +02:00
parent bd025cda6a
commit 262d98bb69
1 changed files with 25 additions and 0 deletions

View File

@ -20,6 +20,7 @@
##############################################################################
from openerp.osv import fields, osv
from openerp.tools import float_compare, float_round
from openerp.tools.translate import _
from openerp import SUPERUSER_ID, api
import logging
@ -152,9 +153,33 @@ class stock_quant(osv.osv):
self._create_account_move_line(cr, uid, quants, move, acc_valuation, acc_dest, journal_id, context=ctx)
def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, force_location_from=False, force_location_to=False, context=None):
quant_obj = self.pool.get('stock.quant')
quant = super(stock_quant, self)._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=force_location_from, force_location_to=force_location_to, context=context)
if move.product_id.valuation == 'real_time':
self._account_entry_move(cr, uid, [quant], move, context)
# If the precision required for the variable quant cost is larger than the accounting
# precision, inconsistencies between the stock valuation and the accounting entries
# may arise.
# For example, a box of 13 units is bought 15.00. If the products leave the
# stock one unit at a time, the amount related to the cost will correspond to
# round(15/13, 2)*13 = 14.95. To avoid this case, we split the quant in 12 + 1, then
# record the difference on the new quant.
# We need to make sure to able to extract at least one unit of the product. There is
# an arbitrary minimum quantity set to 2.0 from which we consider we can extract a
# unit and adapt the cost.
curr_rounding = move.company_id.currency_id.rounding
cost_rounded = float_round(quant.cost, precision_rounding=curr_rounding)
cost_correct = cost_rounded
if float_compare(quant.product_id.uom_id.rounding, 1.0, precision_digits=1) == 0\
and float_compare(quant.qty * quant.cost, quant.qty * cost_rounded, precision_rounding=curr_rounding) != 0\
and float_compare(quant.qty, 2.0, precision_rounding=quant.product_id.uom_id.rounding) >= 0:
qty = quant.qty
cost = quant.cost
quant_correct = quant_obj._quant_split(cr, uid, quant, quant.qty - 1.0, context=context)
cost_correct += (qty * cost) - (qty * cost_rounded)
quant_obj.write(cr, SUPERUSER_ID, [quant.id], {'cost': cost_rounded}, context=context)
quant_obj.write(cr, SUPERUSER_ID, [quant_correct.id], {'cost': cost_correct}, context=context)
return quant
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None):