Skip to content
Permalink
Browse files

[FIX] stock: computation of quantity on hand

On the product form view, the stat button computing the quantity on hand
doesn't give the same result as the quants list triggered by this button
- What's displayed in the stat button of the quantity on hand ?
   1 get the view location of all the warehouses
   2 built the generation tree from the view locations
   3 get all the quants for those locations

- What's displayed in the quants list of the quantity on hand ?
   1 get all the quants stored in internal locations

This commit share the same logic for those two views, by building the
generation tree for the view location then filter on internal locations

Task : 1929515
  • Loading branch information...
Whenrow committed Apr 17, 2019
1 parent 6dd2599 commit fe195b7b49f9083361edfe8f0284419f5e7c8274
Showing with 25 additions and 17 deletions.
  1. +20 −15 addons/stock/models/product.py
  2. +5 −1 addons/stock/views/product_views.xml
  3. +0 −1 addons/stock/views/stock_quant_views.xml
@@ -4,6 +4,7 @@
from odoo import api, fields, models, _
from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError
from odoo.osv import expression
from odoo.tools.float_utils import float_round
from datetime import datetime
import operator as py_operator
@@ -77,7 +78,7 @@ class Product(models.Model):

@api.depends('stock_move_ids.product_qty', 'stock_move_ids.state')
def _compute_quantities(self):
res = self._compute_quantities_dict(self._context.get('lot_id'), self._context.get('owner_id'), self._context.get('package_id'), self._context.get('from_date'), self._context.get('to_date'))
res = self._compute_quantities_dict(self._context.get('lot_id'), self._context.get('owner_id'), self._context.get('package_id'), self._context.get('from_date'), self._context.get('to_date'), self._context.get('quantity_available_locations_domain'))
for product in self:
product.qty_available = res[product.id]['qty_available']
product.incoming_qty = res[product.id]['incoming_qty']
@@ -86,10 +87,10 @@ def _compute_quantities(self):

def _product_available(self, field_names=None, arg=False):
""" Compatibility method """
return self._compute_quantities_dict(self._context.get('lot_id'), self._context.get('owner_id'), self._context.get('package_id'), self._context.get('from_date'), self._context.get('to_date'))
return self._compute_quantities_dict(self._context.get('lot_id'), self._context.get('owner_id'), self._context.get('package_id'), self._context.get('from_date'), self._context.get('to_date'), self._context.get('quantity_available_locations_domain'))

def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False):
domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations()
def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False, quantity_available_locations_domain=False):
domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(usage=quantity_available_locations_domain)
domain_quant = [('product_id', 'in', self.ids)] + domain_quant_loc
dates_in_the_past = False
# only to_date as to_date will correspond to qty_available
@@ -163,7 +164,7 @@ def _get_description(self, picking_type_id):
if picking_code == 'internal':
return self.description_picking or description

def _get_domain_locations(self):
def _get_domain_locations(self, usage=False):
'''
Parses the context and returns a list of location_ids based on it.
It will return all stock locations when no parameters are given
@@ -205,9 +206,9 @@ def _get_domain_locations(self):

for w in Warehouse.browse(wids):
location_ids.append(w.view_location_id.id)
return self._get_domain_locations_new(location_ids, company_id=self.env.context.get('force_company', False), compute_child=self.env.context.get('compute_child', True))
return self._get_domain_locations_new(location_ids, company_id=self.env.context.get('force_company', False), compute_child=self.env.context.get('compute_child', True), usage=usage)

def _get_domain_locations_new(self, location_ids, company_id=False, compute_child=True):
def _get_domain_locations_new(self, location_ids, company_id=False, compute_child=True, usage=False):
operator = compute_child and 'child_of' or 'in'
domain = company_id and ['&', ('company_id', '=', company_id)] or []
locations = self.env['stock.location'].browse(location_ids)
@@ -229,8 +230,12 @@ def _get_domain_locations_new(self, location_ids, company_id=False, compute_chil
loc_domain = loc_domain + [('location_id', operator, other_locations.ids)]
dest_loc_domain = dest_loc_domain and ['|'] + dest_loc_domain or dest_loc_domain
dest_loc_domain = dest_loc_domain + [('location_dest_id', operator, other_locations.ids)]
if usage:
stock_loc_domain = expression.AND([domain + loc_domain, [('location_id.usage', 'in', usage)]])
else:
stock_loc_domain = domain + loc_domain
return (
domain + loc_domain,
stock_loc_domain,
domain + dest_loc_domain + ['!'] + loc_domain if loc_domain else domain + dest_loc_domain,
domain + loc_domain + ['!'] + dest_loc_domain if dest_loc_domain else domain + loc_domain
)
@@ -243,7 +248,7 @@ def _search_qty_available(self, operator, value):
if value == 0.0 and operator == '>' and not ({'from_date', 'to_date'} & set(self.env.context.keys())):
product_ids = self._search_qty_available_new(
operator, value, self.env.context.get('lot_id'), self.env.context.get('owner_id'),
self.env.context.get('package_id')
self.env.context.get('package_id'), self.env.context.get('quantity_available_locations_domain')
)
return [('id', 'in', product_ids)]
return self._search_product_quantity(operator, value, 'qty_available')
@@ -277,10 +282,10 @@ def _search_product_quantity(self, operator, value, field):
ids.append(product.id)
return [('id', 'in', ids)]

def _search_qty_available_new(self, operator, value, lot_id=False, owner_id=False, package_id=False):
def _search_qty_available_new(self, operator, value, lot_id=False, owner_id=False, package_id=False, quantity_available_locations_domain=False):
''' Optimized method which doesn't search on stock.moves, only on stock.quants. '''
product_ids = set()
domain_quant = self._get_domain_locations()[0]
domain_quant = self._get_domain_locations(usage=quantity_available_locations_domain)[0]
if lot_id:
domain_quant.append(('lot_id', '=', lot_id))
if owner_id:
@@ -381,8 +386,8 @@ def action_open_quants(self):
self.ensure_one()
self.env['stock.quant']._quant_tasks()
action = self.env.ref('stock.product_open_quants').read()[0]
action['domain'] = [('product_id', '=', self.id)]
action['context'] = {'search_default_internal_loc': 1}
location_domain = self._get_domain_locations(usage=self._context.get('quantity_available_locations_domain'))[0]
action['domain'] = expression.AND([[('product_id', '=', self.id)], location_domain])
return action

@api.model
@@ -589,8 +594,8 @@ def action_open_quants(self):
self.env['stock.quant']._quant_tasks()
products = self.mapped('product_variant_ids')
action = self.env.ref('stock.product_open_quants').read()[0]
action['domain'] = [('product_id', 'in', products.ids)]
action['context'] = {'search_default_internal_loc': 1}
location_domain = products._get_domain_locations(usage=self._context.get('quantity_available_locations_domain'))[0]
action['domain'] = expression.AND([[('product_id', 'in', products.ids)], location_domain])
return action

def action_view_orderpoints(self):
@@ -257,6 +257,9 @@
attrs="{'invisible': [('tracking', '=', 'none')]}"
class="oe_stat_button" icon="fa-bars" groups="stock.group_production_lot"/>
</button>
<xpath expr="//field[@name='product_tmpl_id']" position="attributes">
<attribute name="context">{'usage': ('internal',)}</attribute>
</xpath>
</data>
</field>
</record>
@@ -361,7 +364,7 @@
<field name="view_mode">kanban,tree,form</field>
<field name="view_type">form</field>
<field name="search_view_id" ref="product_template_search_form_view_stock"/>
<field name="context">{"search_default_consumable": 1, 'default_type': 'product'}</field>
<field name="context">{"search_default_consumable": 1, 'default_type': 'product', 'usage': ('internal',)}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new product
@@ -375,6 +378,7 @@
<field name="res_model">product.product</field>
<field name="view_mode">tree,form,kanban</field>
<field name="view_type">form</field>
<field name="context">{'usage': ('internal',)}</field>
<field name="search_view_id" ref="stock_product_search_form_view"/>
</record>

@@ -138,7 +138,6 @@
</record>
<record model="ir.actions.act_window" id="product_open_quants"> <!-- product_view + python -->
<field name="name">Stock On Hand</field>
<field name="context">{'search_default_internal_loc': 1, 'search_default_locationgroup':1}</field>
<field name="domain">[('product_id', '=', active_id)]</field>
<field name="res_model">stock.quant</field>
</record>

0 comments on commit fe195b7

Please sign in to comment.
You can’t perform that action at this time.