diff --git a/CHANGELOG b/CHANGELOG
index 68bcda4904..dbaaa4cefa 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Add products_by_locations Model
+
Version 5.8.0 - 2020-11-02
* Bug fixes (see mercurial logs for details)
* Remove support for Python 3.5
diff --git a/__init__.py b/__init__.py
index a0e3032df2..d9f97d0bd0 100644
--- a/__init__.py
+++ b/__init__.py
@@ -25,6 +25,7 @@ def register():
location.Party,
location.PartyLocation,
location.ProductsByLocationsContext,
+ location.ProductsByLocations,
location.LocationLeadTime,
move.Move,
shipment.ShipmentIn,
diff --git a/location.py b/location.py
index 88187f4110..d2fab328e5 100644
--- a/location.py
+++ b/location.py
@@ -4,10 +4,12 @@ import datetime
import operator
from decimal import Decimal
+from sql import Column
+
from trytond.i18n import gettext
from trytond.model import (
- ModelView, ModelSQL, MatchMixin, ValueMixin, DeactivableMixin, fields,
- sequence_ordered, tree)
+ Model, ModelView, ModelSQL, MatchMixin, ValueMixin, DeactivableMixin,
+ fields, sequence_ordered, tree)
from trytond import backend
from trytond.cache import Cache
from trytond.pyson import Eval, If
@@ -710,6 +712,65 @@ class ProductsByLocationsContext(ModelView):
return self.forecast_date
+class ProductsByLocations(DeactivableMixin, ModelSQL, ModelView):
+ "Products by Locations"
+ __name__ = 'stock.products_by_locations'
+
+ product = fields.Many2One('product.product', "Product")
+ quantity = fields.Function(
+ fields.Float(
+ "Quantity", digits=(16, Eval('default_uom_digits', 2)),
+ depends=['default_uom_digits']),
+ 'get_product', searcher='search_product')
+ forecast_quantity = fields.Function(
+ fields.Float(
+ "Forecast Quantity", digits=(16, Eval('default_uom_digits', 2)),
+ depends=['default_uom_digits']),
+ 'get_product', searcher='search_product')
+ default_uom = fields.Function(
+ fields.Many2One('product.uom', "Default UOM"),
+ 'get_product', searcher='search_product')
+ default_uom_digits = fields.Function(
+ fields.Integer("Default UOM Digits"), 'get_product')
+ cost_value = fields.Function(
+ fields.Numeric("Cost Value"), 'get_product')
+ consumable = fields.Function(
+ fields.Boolean("Consumable"), 'get_product',
+ searcher='search_product')
+
+ @classmethod
+ def __setup__(cls):
+ super().__setup__()
+ cls._order.insert(0, ('product', 'ASC'))
+
+ @classmethod
+ def table_query(cls):
+ pool = Pool()
+ Product = pool.get('product.product')
+ product = Product.__table__()
+ columns = []
+ for fname, field in cls._fields.items():
+ if not hasattr(field, 'set'):
+ if (isinstance(field, fields.Many2One)
+ and field.get_target() == Product):
+ column = Column(product, 'id')
+ else:
+ column = Column(product, fname)
+ columns.append(column.as_(fname))
+ return product.select(*columns)
+
+ def get_product(self, name):
+ value = getattr(self.product, name)
+ if isinstance(value, Model):
+ value = value.id
+ return value
+
+ @classmethod
+ def search_product(cls, name, clause):
+ nested = clause[0].lstrip(name)
+ return [('product.' + name + nested,) + tuple(clause[1:])]
+
+
class LocationLeadTime(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
'Location Lead Time'
__name__ = 'stock.location.lead_time'
diff --git a/location.xml b/location.xml
index e60ad7c2f9..870fd5c6b6 100644
--- a/location.xml
+++ b/location.xml
@@ -67,11 +67,16 @@ this repository contains the full copyright notices and license terms. -->
products_by_locations_context_form
-
- Products
- product.product
-
+
+ stock.products_by_locations
+ tree
+ products_by_locations_list
+
+
+
+ Products by Locations
+ stock.products_by_locations
+
@@ -80,27 +85,20 @@ this repository contains the full copyright notices and license terms. -->
pyson="1"/>
stock.products_by_locations.context
-
+
-
-
+
+
-
-
-
-
-
-
+
tree_open
stock.location,-1
-
+
-
+
form_relate
stock.location,-1
-
+
diff --git a/product.py b/product.py
index 9e6dc1a692..9fd2649abb 100644
--- a/product.py
+++ b/product.py
@@ -23,7 +23,7 @@ from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta
from trytond.tools import grouped_slice
-from trytond.modules.product import round_price
+from trytond.modules.product import round_price, price_digits
from .exceptions import ProductCostPriceError
from .move import StockMixin
@@ -111,14 +111,19 @@ class Template(metaclass=PoolMeta):
class Product(StockMixin, object, metaclass=PoolMeta):
__name__ = "product.product"
- quantity = fields.Function(fields.Float('Quantity',
- help="The amount of stock in the location."),
+ quantity = fields.Function(fields.Float(
+ "Quantity", digits=(16, Eval('default_uom_digits', 2)),
+ depends=['default_uom_digits'],
+ help="The amount of stock in the location."),
'get_quantity', searcher='search_quantity')
- forecast_quantity = fields.Function(fields.Float('Forecast Quantity',
- help="The amount of stock expected to be in the location."),
+ forecast_quantity = fields.Function(fields.Float(
+ "Forecast Quantity", digits=(16, Eval('default_uom_digits', 2)),
+ depends=['default_uom_digits'],
+ help="The amount of stock expected to be in the location."),
'get_quantity', searcher='search_quantity')
- cost_value = fields.Function(fields.Numeric('Cost Value',
- help="The value of the stock in the location."),
+ cost_value = fields.Function(fields.Numeric(
+ "Cost Value", digits=price_digits,
+ help="The value of the stock in the location."),
'get_cost_value')
@classmethod
@@ -149,7 +154,7 @@ class Product(StockMixin, object, metaclass=PoolMeta):
for product in cls.browse(products):
# The product may not have a cost price
if product.cost_price is not None:
- cost_values[product.id] = (
+ cost_values[product.id] = round_price(
Decimal(str(product.quantity)) * product.cost_price)
return cost_values
diff --git a/product.xml b/product.xml
index d30847d100..1394414dac 100644
--- a/product.xml
+++ b/product.xml
@@ -3,13 +3,6 @@
this repository contains the full copyright notices and license terms. -->
-
- product.product
- tree
-
- product_tree_qty
-
-
Recompute Cost Price
product.recompute_cost_price
diff --git a/view/product_tree_qty.xml b/view/products_by_locations_list.xml
similarity index 65%
rename from view/product_tree_qty.xml
rename to view/products_by_locations_list.xml
index 9028e02868..0c937b675a 100644
--- a/view/product_tree_qty.xml
+++ b/view/products_by_locations_list.xml
@@ -2,11 +2,9 @@
-
-
-
-
-
+
+
+