[IMP] No digits_compute on product_qty of move + rounding on qty_available fields + add float_compare

This commit is contained in:
Josse Colpaert 2014-11-14 12:18:07 +01:00
parent 73db5b3eef
commit cf16632b44
2 changed files with 45 additions and 27 deletions

View File

@ -23,6 +23,7 @@ from openerp.osv import fields, osv
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.safe_eval import safe_eval as eval
import openerp.addons.decimal_precision as dp import openerp.addons.decimal_precision as dp
from openerp.tools.float_utils import float_round
class product_product(osv.osv): class product_product(osv.osv):
_inherit = "product.product" _inherit = "product.product"
@ -136,14 +137,18 @@ class product_product(osv.osv):
moves_in = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_in)) moves_in = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_in))
moves_out = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_out)) moves_out = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_out))
res = {} res = {}
for id in ids: for product in self.browse(cr, uid, ids, context=context):
id = product.id
qty_available = float_round(quants.get(id, 0.0), precision_rounding=product.uom_id.rounding)
incoming_qty = float_round(moves_in.get(id, 0.0), precision_rounding=product.uom_id.rounding)
outgoing_qty = float_round(moves_out.get(id, 0.0), precision_rounding=product.uom_id.rounding)
virtual_available = float_round(quants.get(id, 0.0) + moves_in.get(id, 0.0) - moves_out.get(id, 0.0), precision_rounding=product.uom_id.rounding)
res[id] = { res[id] = {
'qty_available': quants.get(id, 0.0), 'qty_available': qty_available,
'incoming_qty': moves_in.get(id, 0.0), 'incoming_qty': incoming_qty,
'outgoing_qty': moves_out.get(id, 0.0), 'outgoing_qty': outgoing_qty,
'virtual_available': quants.get(id, 0.0) + moves_in.get(id, 0.0) - moves_out.get(id, 0.0), 'virtual_available': virtual_available,
} }
return res return res
def _search_product_quantity(self, cr, uid, obj, name, domain, context): def _search_product_quantity(self, cr, uid, obj, name, domain, context):

View File

@ -25,6 +25,7 @@ import json
import time import time
from openerp.osv import fields, osv from openerp.osv import fields, osv
from openerp.tools.float_utils import float_compare
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
from openerp import SUPERUSER_ID, api from openerp import SUPERUSER_ID, api
@ -377,9 +378,9 @@ class stock_quant(osv.osv):
if move.picking_id: if move.picking_id:
self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context) self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context)
#check if move'state needs to be set as 'assigned' #check if move'state needs to be set as 'assigned'
if reserved_availability == move.product_qty and move.state in ('confirmed', 'waiting'): if float_compare(reserved_availability, move.product_qty, precision_rounding=move.product_uom.rounding) == 0 and move.state in ('confirmed', 'waiting') :
self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context) self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context)
elif reserved_availability > 0 and not move.partially_available: elif float_compare(reserved_availability, 0, precision_rounding=move.product_uom.rounding) > 0 and not move.partially_available:
self.pool.get('stock.move').write(cr, uid, [move.id], {'partially_available': True}, context=context) self.pool.get('stock.move').write(cr, uid, [move.id], {'partially_available': True}, context=context)
def quants_move(self, cr, uid, quants, move, location_to, location_from=False, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None): def quants_move(self, cr, uid, quants, move, location_to, location_from=False, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None):
@ -438,7 +439,8 @@ class stock_quant(osv.osv):
if not prefered_domain_list: if not prefered_domain_list:
return self.quants_get(cr, uid, location, product, qty, domain=domain, restrict_lot_id=restrict_lot_id, restrict_partner_id=restrict_partner_id, context=context) return self.quants_get(cr, uid, location, product, qty, domain=domain, restrict_lot_id=restrict_lot_id, restrict_partner_id=restrict_partner_id, context=context)
for prefered_domain in prefered_domain_list: for prefered_domain in prefered_domain_list:
if res_qty > 0: res_qty_cmp = float_compare(res_qty, 0, precision_rounding=product.uom_id.rounding)
if res_qty_cmp > 0:
#try to replace the last tuple (None, res_qty) with something that wasn't chosen at first because of the prefered order #try to replace the last tuple (None, res_qty) with something that wasn't chosen at first because of the prefered order
quants.pop() quants.pop()
tmp_quants = self.quants_get(cr, uid, location, product, res_qty, domain=domain + prefered_domain, restrict_lot_id=restrict_lot_id, restrict_partner_id=restrict_partner_id, context=context) tmp_quants = self.quants_get(cr, uid, location, product, res_qty, domain=domain + prefered_domain, restrict_lot_id=restrict_lot_id, restrict_partner_id=restrict_partner_id, context=context)
@ -516,9 +518,12 @@ class stock_quant(osv.osv):
def _quant_split(self, cr, uid, quant, qty, context=None): def _quant_split(self, cr, uid, quant, qty, context=None):
context = context or {} context = context or {}
if (quant.qty > 0 and quant.qty <= qty) or (quant.qty <= 0 and quant.qty >= qty):
if (quant.qty > 0 and float_compare(quant.qty, qty, precision_rounding=quant.product_id.uom_id.rounding) <= 0)\
or (quant.qty <= 0 and float_compare(quant.qty, qty, precision_rounding=quant.product_id.uom_id.rounding) >= 0) :
#(quant.qty > 0 and quant.qty <= qty) or (quant.qty <= 0 and quant.qty >= qty):
return False return False
new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': quant.qty - qty}, context=context) new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': quant.qty - qty, 'history_ids': [(4, x.id) for x in quant.history_ids]}, context=context)
self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty}, context=context) self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty}, context=context)
quant.refresh() quant.refresh()
return self.browse(cr, uid, new_quant, context=context) return self.browse(cr, uid, new_quant, context=context)
@ -550,8 +555,9 @@ class stock_quant(osv.osv):
dom += [('owner_id', '=', quant.owner_id.id)] dom += [('owner_id', '=', quant.owner_id.id)]
dom += [('package_id', '=', quant.package_id.id)] dom += [('package_id', '=', quant.package_id.id)]
quants = self.quants_get(cr, uid, quant.location_id, quant.product_id, quant.qty, dom, context=context) quants = self.quants_get(cr, uid, quant.location_id, quant.product_id, quant.qty, dom, context=context)
product_uom_rounding = quant.product_id.uom_id.rounding
for quant_neg, qty in quants: for quant_neg, qty in quants:
if not quant_neg: if not quant_neg or not solving_quant:
continue continue
to_solve_quant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id)], context=context) to_solve_quant_ids = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id)], context=context)
if not to_solve_quant_ids: if not to_solve_quant_ids:
@ -559,7 +565,7 @@ class stock_quant(osv.osv):
solving_qty = qty solving_qty = qty
solved_quant_ids = [] solved_quant_ids = []
for to_solve_quant in self.browse(cr, uid, to_solve_quant_ids, context=context): for to_solve_quant in self.browse(cr, uid, to_solve_quant_ids, context=context):
if solving_qty <= 0: if float_compare(solving_qty, 0, precision_rounding=product_uom_rounding) <= 0:
continue continue
solved_quant_ids.append(to_solve_quant.id) 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) self._quant_split(cr, uid, to_solve_quant, min(solving_qty, to_solve_quant.qty), context=context)
@ -607,16 +613,18 @@ class stock_quant(osv.osv):
domain += [('company_id', '=', self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id)] domain += [('company_id', '=', self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id)]
res = [] res = []
offset = 0 offset = 0
while quantity > 0: while float_compare(quantity, 0, precision_rounding=product.uom_id.rounding) > 0:
quants = self.search(cr, uid, domain, order=orderby, limit=10, offset=offset, context=context) quants = self.search(cr, uid, domain, order=orderby, limit=10, offset=offset, context=context)
if not quants: if not quants:
res.append((None, quantity)) res.append((None, quantity))
break break
for quant in self.browse(cr, uid, quants, context=context): for quant in self.browse(cr, uid, quants, context=context):
if quantity >= abs(quant.qty): qty_cmp = float_compare(quantity, abs(quant.qty), precision_rounding=product.uom_id.rounding)
qty0_cmp = float_compare(quantity, 0.0, precision_rounding=product.uom_id.rounding)
if qty_cmp >= 0:
res += [(quant, abs(quant.qty))] res += [(quant, abs(quant.qty))]
quantity -= abs(quant.qty) quantity -= abs(quant.qty)
elif quantity != 0: elif qty0_cmp != 0:
res += [(quant, quantity)] res += [(quant, quantity)]
quantity = 0 quantity = 0
break break
@ -1111,8 +1119,8 @@ class stock_picking(osv.osv):
move_quants = move.reserved_quant_ids move_quants = move.reserved_quant_ids
picking_quants += move_quants picking_quants += move_quants
forced_qty = (move.state == 'assigned') and move.product_qty - sum([x.qty for x in move_quants]) or 0 forced_qty = (move.state == 'assigned') and move.product_qty - sum([x.qty for x in move_quants]) or 0
#if we used force_assign() on the move, or if the move is incomming, forced_qty > 0 #if we used force_assign() on the move, or if the move is incoming, forced_qty > 0
if forced_qty: if float_compare(forced_qty, 0, precision_rounding=move.product_id.uom_id.rounding) > 0:
if forced_qties.get(move.product_id): if forced_qties.get(move.product_id):
forced_qties[move.product_id] += forced_qty forced_qties[move.product_id] += forced_qty
else: else:
@ -1165,11 +1173,16 @@ class stock_picking(osv.osv):
'''method that creates the link between a given operation and move(s) of given product, for the given quantity. '''method that creates the link between a given operation and move(s) of given product, for the given quantity.
Returns True if it was possible to create links for the requested quantity (False if there was not enough quantity on stock moves)''' Returns True if it was possible to create links for the requested quantity (False if there was not enough quantity on stock moves)'''
qty_to_assign = qty qty_to_assign = qty
prod_obj = self.pool.get("product.product")
product = prod_obj.browse(cr, uid, product_id)
rounding = product.uom_id.rounding
qtyassign_cmp = float_compare(qty_to_assign, 0.0, precision_rounding=rounding)
if prod2move_ids.get(product_id): if prod2move_ids.get(product_id):
while prod2move_ids[product_id] and qty_to_assign > 0: while prod2move_ids[product_id] and qtyassign_cmp > 0:
qty_on_link = _create_link_for_index(operation_id, 0, product_id, qty_to_assign, quant_id=False) qty_on_link = _create_link_for_index(operation_id, 0, product_id, qty_to_assign, quant_id=False)
qty_to_assign -= qty_on_link qty_to_assign -= qty_on_link
return qty_to_assign == 0 qtyassign_cmp = float_compare(qty_to_assign, 0.0, precision_rounding=rounding)
return qtyassign_cmp == 0
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
package_obj = self.pool.get('stock.quant.package') package_obj = self.pool.get('stock.quant.package')
@ -1190,7 +1203,6 @@ class stock_picking(osv.osv):
operations = sorted(operations, key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0)) operations = sorted(operations, key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
#delete existing operations to start again from scratch #delete existing operations to start again from scratch
cr.execute("DELETE FROM stock_move_operation_link WHERE operation_id in %s", (tuple([x.id for x in operations]),)) cr.execute("DELETE FROM stock_move_operation_link WHERE operation_id in %s", (tuple([x.id for x in operations]),))
#1) first, try to create links when quants can be identified without any doubt #1) first, try to create links when quants can be identified without any doubt
for ops in operations: for ops in operations:
#for each operation, create the links with the stock move by seeking on the matching reserved quants, #for each operation, create the links with the stock move by seeking on the matching reserved quants,
@ -1210,7 +1222,7 @@ class stock_picking(osv.osv):
need_rereserve = True need_rereserve = True
elif ops.product_id.id: elif ops.product_id.id:
#Check moves with same product #Check moves with same product
qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context) qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, round=False, context=context)
for move_dict in prod2move_ids.get(ops.product_id.id, []): for move_dict in prod2move_ids.get(ops.product_id.id, []):
move = move_dict['move'] move = move_dict['move']
for quant in move.reserved_quant_ids: for quant in move.reserved_quant_ids:
@ -1230,7 +1242,8 @@ class stock_picking(osv.osv):
max_qty_on_link = min(quant.qty, qty_to_assign) max_qty_on_link = min(quant.qty, qty_to_assign)
qty_on_link = _create_link_for_quant(ops.id, quant, max_qty_on_link) qty_on_link = _create_link_for_quant(ops.id, quant, max_qty_on_link)
qty_to_assign -= qty_on_link qty_to_assign -= qty_on_link
if qty_to_assign > 0: qty_assign_cmp = float_compare(qty_to_assign, 0, precision_rounding=ops.product_id.uom_id.rounding)
if qty_assign_cmp > 0:
#qty reserved is less than qty put in operations. We need to create a link but it's deferred after we processed #qty reserved is less than qty put in operations. We need to create a link but it's deferred after we processed
#all the quants (because they leave no choice on their related move and needs to be processed with higher priority) #all the quants (because they leave no choice on their related move and needs to be processed with higher priority)
still_to_do += [(ops, ops.product_id.id, qty_to_assign)] still_to_do += [(ops, ops.product_id.id, qty_to_assign)]
@ -1352,10 +1365,11 @@ class stock_picking(osv.osv):
continue continue
elif move.state == 'draft': elif move.state == 'draft':
toassign_move_ids.append(move.id) toassign_move_ids.append(move.id)
if remaining_qty == 0: if float_compare(remaining_qty, 0, precision_rounding = move.product_id.uom_id.rounding) == 0:
if move.state in ('draft', 'assigned', 'confirmed'): if move.state in ('draft', 'assigned', 'confirmed'):
todo_move_ids.append(move.id) todo_move_ids.append(move.id)
elif remaining_qty > 0 and remaining_qty < move.product_qty: elif float_compare(remaining_qty,0, precision_rounding = move.product_id.uom_id.rounding) > 0 and \
float_compare(remaining_qty, move.product_qty, precision_rounding = move.product_id.uom_id.rounding) < 0:
new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context) new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context)
todo_move_ids.append(move.id) todo_move_ids.append(move.id)
#Assign move as it was assigned before #Assign move as it was assigned before
@ -1654,11 +1668,10 @@ class stock_move(osv.osv):
'date': fields.datetime('Date', required=True, select=True, help="Move date: scheduled date until move is done, then date of actual move processing", states={'done': [('readonly', True)]}), 'date': fields.datetime('Date', required=True, select=True, help="Move date: scheduled date until move is done, then date of actual move processing", states={'done': [('readonly', True)]}),
'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"), 'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}), 'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}),
'product_qty': fields.function(_quantity_normalize, fnct_inv=_set_product_qty, _type='float', store={ 'product_qty': fields.function(_quantity_normalize, fnct_inv=_set_product_qty, type='float', digits=0, store={
'stock.move': (lambda self, cr, uid, ids, ctx: ids, ['product_id', 'product_uom_qty', 'product_uom'], 20), 'stock.move': (lambda self, cr, uid, ids, ctx: ids, ['product_id', 'product_uom_qty', 'product_uom'], 20),
'product.product': (_get_moves_from_prod, ['uom_id'], 20), 'product.product': (_get_moves_from_prod, ['uom_id'], 20),
}, string='Quantity', }, string='Quantity',
digits_compute=dp.get_precision('Product Unit of Measure'),
help='Quantity in the default UoM of the product'), help='Quantity in the default UoM of the product'),
'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), 'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
required=True, states={'done': [('readonly', True)]}, required=True, states={'done': [('readonly', True)]},