Use dedicated model for products and lots by locations

issue9874
review314911002
This commit is contained in:
Cédric Krier
2020-12-23 21:55:13 +01:00
parent fc5b4497ea
commit f0ded2f87e
7 changed files with 99 additions and 41 deletions
+2
View File
@@ -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
+1
View File
@@ -25,6 +25,7 @@ def register():
location.Party,
location.PartyLocation,
location.ProductsByLocationsContext,
location.ProductsByLocations,
location.LocationLeadTime,
move.Move,
shipment.ShipmentIn,
+63 -2
View File
@@ -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'
+17 -19
View File
@@ -67,11 +67,16 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">products_by_locations_context_form</field>
</record>
<record model="ir.action.act_window" id="act_products_by_locations">
<field name="name">Products</field>
<field name="res_model">product.product</field>
<field name="context" eval="{'locations': Eval('active_ids')}"
pyson="1"/>
<record model="ir.ui.view" id="products_by_locations_view_list">
<field name="model">stock.products_by_locations</field>
<field name="type">tree</field>
<field name="name">products_by_locations_list</field>
</record>
<record model="ir.action.act_window" id="act_products_by_locations_relate">
<field name="name">Products by Locations</field>
<field name="res_model">stock.products_by_locations</field>
<field name="context" eval="{'locations': Eval('active_ids')}" pyson="1"/>
<field name="domain"
eval="['OR', ('quantity', '!=', 0.0), ('forecast_quantity', '!=', 0.0)]"
pyson="1"/>
@@ -80,27 +85,20 @@ this repository contains the full copyright notices and license terms. -->
pyson="1"/>
<field name="context_model">stock.products_by_locations.context</field>
</record>
<record model="ir.action.act_window.view" id="act_product_form_view2">
<record model="ir.action.act_window.view" id="act_products_by_locations_relate_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="product_view_tree_qty"/>
<field name="act_window" ref="act_products_by_locations"/>
<field name="view" ref="products_by_locations_view_list"/>
<field name="act_window" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.action.act_window.view" id="act_product_form_view">
<field name="sequence" eval="20"/>
<field name="view" ref="product.product_view_form"/>
<field name="act_window" ref="act_products_by_locations"/>
</record>
<record model="ir.action.keyword"
id="act_products_by_locations_keyword1">
<record model="ir.action.keyword" id="act_products_by_locations_relate_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.location,-1</field>
<field name="action" ref="act_products_by_locations"/>
<field name="action" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.action.keyword"
id="act_products_by_locations_keyword2">
<record model="ir.action.keyword" id="act_products_by_locations_relate_keyword2">
<field name="keyword">form_relate</field>
<field name="model">stock.location,-1</field>
<field name="action" ref="act_products_by_locations"/>
<field name="action" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.model.access" id="access_location">
+13 -8
View File
@@ -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
-7
View File
@@ -3,13 +3,6 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="product_view_tree_qty">
<field name="model">product.product</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">product_tree_qty</field>
</record>
<record model="ir.action.wizard" id="wizard_recompute_cost_price">
<field name="name">Recompute Cost Price</field>
<field name="wiz_name">product.recompute_cost_price</field>
@@ -2,11 +2,9 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree>
<field name="code"/>
<field name="name"/>
<field name="quantity"/>
<field name="forecast_quantity"/>
<field name="default_uom"/>
<field name="product" expand="1"/>
<field name="quantity" symbol="default_uom"/>
<field name="forecast_quantity" symbol="default_uom"/>
<field name="cost_value" sum="Cost Value"/>
<field name="consumable" tree_invisible="1"/>
</tree>