From 262d98bb6988a4b52ae9eabbe553244ead304d9a Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 19 Apr 2016 18:22:27 +0200 Subject: [PATCH] [FIX] stock_account: account/valuation inconsistencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- addons/stock_account/stock_account.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 41aae4a84fb..bee410ae3ca 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -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):