diff --git a/addons/product/product.py b/addons/product/product.py
index f22fcdbdfbf..e093cd0c0a7 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -508,16 +508,6 @@ class product_product(osv.osv):
res.setdefault(id, 0.0)
return res
- def _get_product_available_func(states, what):
- def _product_available(self, cr, uid, ids, name, arg, context=None):
- return {}.fromkeys(ids, 0.0)
- return _product_available
-
- _product_qty_available = _get_product_available_func(('done',), ('in', 'out'))
- _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out'))
- _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',))
- _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',))
-
def _product_lst_price(self, cr, uid, ids, name, arg, context=None):
res = {}
product_uom_obj = self.pool.get('product.uom')
@@ -622,10 +612,6 @@ class product_product(osv.osv):
_inherit = ['mail.thread']
_order = 'default_code,name_template'
_columns = {
- 'qty_available': fields.function(_product_qty_available, type='float', string='Quantity On Hand'),
- 'virtual_available': fields.function(_product_virtual_available, type='float', string='Quantity Available'),
- 'incoming_qty': fields.function(_product_incoming_qty, type='float', string='Incoming'),
- 'outgoing_qty': fields.function(_product_outgoing_qty, type='float', string='Outgoing'),
'price': fields.function(_product_price, type='float', string='Price', digits_compute=dp.get_precision('Product Price')),
'lst_price': fields.function(_product_lst_price, type='float', string='Public Price', digits_compute=dp.get_precision('Product Price')),
'code': fields.function(_product_code, type='char', string='Internal Reference'),
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index 99820f8b02d..4a842a0c791 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -33,14 +33,12 @@
product.product
-
+
-
-
diff --git a/addons/stock/product.py b/addons/stock/product.py
index 6ca53a5ad0d..f7fde1c33a5 100644
--- a/addons/stock/product.py
+++ b/addons/stock/product.py
@@ -21,6 +21,7 @@
from openerp.osv import fields, osv
from openerp.tools.translate import _
+from openerp.tools.safe_eval import safe_eval as eval
import openerp.addons.decimal_precision as dp
class product_product(osv.osv):
@@ -99,13 +100,13 @@ class product_product(osv.osv):
)
def _get_domain_dates(self, cr, uid, ids, context):
- from_date = context.get('from_date',False)
- to_date = context.get('to_date',False)
+ from_date = context.get('from_date', False)
+ to_date = context.get('to_date', False)
domain = []
if from_date:
- domain.append(('date','>=',from_date))
+ domain.append(('date', '>=', from_date))
if to_date:
- domain.append(('date','<=',to_date))
+ domain.append(('date', '<=', to_date))
return domain
def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None):
@@ -114,26 +115,25 @@ class product_product(osv.osv):
domain_products = [('product_id', 'in', ids)]
domain_quant, domain_move_in, domain_move_out = self._get_domain_locations(cr, uid, ids, context=context)
- domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state','not in',('done','cancel'))] + domain_products
- domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state','not in',('done','cancel'))] + domain_products
+ domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel'))] + domain_products
+ domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel'))] + domain_products
domain_quant += domain_products
if context.get('lot_id') or context.get('owner_id') or context.get('package_id'):
if context.get('lot_id'):
- domain_quant.append(('lot_id','=',context['lot_id']))
+ domain_quant.append(('lot_id', '=', context['lot_id']))
if context.get('owner_id'):
- domain_quant.append(('owner_id','=',context['owner_id']))
+ domain_quant.append(('owner_id', '=', context['owner_id']))
if context.get('package_id'):
- domain_quant.append(('package_id','=',context['package_id']))
- moves_in = []
+ domain_quant.append(('package_id', '=', context['package_id']))
+ moves_in = []
moves_out = []
else:
-# if field_names in ['incoming_qty', 'outgoing_qty', 'virtual_available']:
- moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
+ moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context)
-
+
quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context)
quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))
-
+
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))
@@ -144,16 +144,39 @@ class product_product(osv.osv):
'incoming_qty': moves_in.get(id, 0.0),
'outgoing_qty': moves_out.get(id, 0.0),
'virtual_available': quants.get(id, 0.0) + moves_in.get(id, 0.0) - moves_out.get(id, 0.0),
- }
-
+ }
+
+ return res
+
+ def _search_product_quantity(self, cr, uid, obj, name, domain, context):
+ res = []
+ for field, operator, value in domain:
+ #to prevent sql injections
+ assert field in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'), 'Invalid domain left operand'
+ assert operator in ('<', '>', '=', '<=', '>='), 'Invalid domain operator'
+ assert isinstance(value, (float, int)), 'Invalid domain right operand'
+
+ if operator == '=':
+ operator = '=='
+
+ product_ids = self.search(cr, uid, [], context=context)
+ ids = []
+ if product_ids:
+ #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget
+ #the context that can be set with a location, an owner...
+ for element in self.browse(cr, uid, product_ids, context=context):
+ if eval(str(element[field]) + operator + str(value)):
+ ids.append(element.id)
+ res.append(('id', 'in', ids))
return res
_columns = {
'reception_count': fields.function(_stock_move_count, string="Reception", type='integer', multi='pickings'),
'delivery_count': fields.function(_stock_move_count, string="Delivery", type='integer', multi='pickings'),
'qty_available': fields.function(_product_available, multi='qty_available',
- type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
+ type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
string='Quantity On Hand',
+ fnct_search=_search_product_quantity,
help="Current quantity of products.\n"
"In a context with a single Stock Location, this includes "
"goods stored at this Location, or any of its children.\n"
@@ -165,8 +188,9 @@ class product_product(osv.osv):
"Otherwise, this includes goods stored in any Stock Location "
"with 'internal' type."),
'virtual_available': fields.function(_product_available, multi='qty_available',
- type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
- string='Forecasted Quantity',
+ type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
+ string='Forecast Quantity',
+ fnct_search=_search_product_quantity,
help="Forecast quantity (computed as Quantity On Hand "
"- Outgoing + Incoming)\n"
"In a context with a single Stock Location, this includes "
@@ -179,8 +203,9 @@ class product_product(osv.osv):
"Otherwise, this includes goods stored in any Stock Location "
"with 'internal' type."),
'incoming_qty': fields.function(_product_available, multi='qty_available',
- type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
+ type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
string='Incoming',
+ fnct_search=_search_product_quantity,
help="Quantity of products that are planned to arrive.\n"
"In a context with a single Stock Location, this includes "
"goods arriving to this Location, or any of its children.\n"
@@ -193,8 +218,9 @@ class product_product(osv.osv):
"Otherwise, this includes goods arriving to any Stock "
"Location with 'internal' type."),
'outgoing_qty': fields.function(_product_available, multi='qty_available',
- type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
+ type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
string='Outgoing',
+ fnct_search=_search_product_quantity,
help="Quantity of products that are planned to leave.\n"
"In a context with a single Stock Location, this includes "
"goods leaving this Location, or any of its children.\n"
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index 277ec5b40e5..76ae15475ce 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -11,6 +11,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ product.stock.tree.inherit
+ product.product
+
+
+
+
+
+
+
+
+ {'red':virtual_available<0, 'blue':virtual_available>=0 and state in ('draft', 'end', 'obsolete'), 'black':virtual_available>=0 and state not in ('draft', 'end', 'obsolete')}
+