diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index 2b09ce31f10..a73a8a57d00 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -69,15 +69,15 @@ 'views/res_partner_views.xml', 'views/product_strategy_views.xml', - 'views/product_views.xml', 'views/stock_lot_views.xml', 'views/stock_scrap_views.xml', 'views/stock_quant_views.xml', - 'views/stock_location_views.xml', 'views/stock_warehouse_views.xml', 'views/stock_move_line_views.xml', 'views/stock_picking_views.xml', 'views/stock_move_views.xml', + 'views/product_views.xml', + 'views/stock_location_views.xml', 'views/stock_orderpoint_views.xml', 'views/stock_storage_category_views.xml', 'views/res_config_settings_views.xml', @@ -103,24 +103,8 @@ 'stock/static/src/scss/stock_traceability_report.scss', ], 'web.assets_backend': [ - 'stock/static/src/js/inventory_report_list_controller.js', - 'stock/static/src/js/inventory_report_list_view.js', - 'stock/static/src/js/inventory_singleton_list_renderer.js', - 'stock/static/src/js/inventory_singleton_list_controller.js', - 'stock/static/src/js/inventory_singleton_list_view.js', - 'stock/static/src/js/report_stock_forecasted.js', - 'stock/static/src/js/report_stock_reception.js', - 'stock/static/src/js/lazy_column_list.js', - 'stock/static/src/js/stock_orderpoint_list_controller.js', - 'stock/static/src/js/stock_orderpoint_list_model.js', - 'stock/static/src/js/stock_orderpoint_list_view.js', - 'stock/static/src/js/stock_traceability_report_backend.js', - 'stock/static/src/js/stock_traceability_report_widgets.js', - 'stock/static/src/js/popover_widget.js', - 'stock/static/src/js/forecast_widget.js', - 'stock/static/src/js/counted_quantity_widget.js', - 'stock/static/src/js/basic_model.js', - 'stock/static/src/js/stock_rescheduling_popover.js', + 'stock/static/src/**/*.js', + 'stock/static/src/views/**/*.js', 'stock/static/src/scss/forecast_widget.scss', 'stock/static/src/scss/stock_empty_screen.scss', ], @@ -134,15 +118,8 @@ 'stock/static/tests/stock_traceability_report_backend_tests.js', ], 'web.assets_qweb': [ - 'stock/static/src/xml/inventory_report.xml', - 'stock/static/src/xml/inventory_adjustments.xml', - 'stock/static/src/xml/inventory_lines.xml', - 'stock/static/src/xml/popover_widget.xml', - 'stock/static/src/xml/forecast_widget.xml', - 'stock/static/src/xml/report_stock_forecasted.xml', - 'stock/static/src/xml/report_stock_reception.xml', - 'stock/static/src/xml/stock_orderpoint.xml', - 'stock/static/src/xml/stock_traceability_report_backend.xml', + 'stock/static/src/**/*.xml', + 'stock/static/src/views/**/*.xml', ], }, 'license': 'LGPL-3', diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index 7d5189f5022..4876ba58ade 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -516,7 +516,6 @@ class Product(models.Model): # Be aware that the exact same function exists in product.template def action_open_quants(self): - domain = [('product_id', 'in', self.ids)] hide_location = not self.user_has_groups('stock.group_stock_multi_locations') hide_lot = all(product.tracking == 'none' for product in self) self = self.with_context( @@ -544,8 +543,10 @@ class Product(models.Model): else: self = self.with_context(product_tmpl_ids=self.product_tmpl_id.ids) action = self.env['stock.quant'].action_view_inventory() - action['domain'] = domain - action["name"] = _('Update Quantity') + # note that this action is used by different views w/varying customizations + if not self.env.context.get('is_stock_report'): + action['domain'] = [('product_id', 'in', self.ids)] + action["name"] = _('Update Quantity') return action def action_update_quantity_on_hand(self): diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 0a34cbc6674..9a96620d310 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -1074,3 +1074,6 @@ class Warehouse(models.Model): 'limit': 20, 'context': dict(self._context, default_warehouse_selectable=True, default_warehouse_ids=self.ids) } + + def get_current_warehouses(self): + return self.env['stock.warehouse'].search_read(fields=['id', 'name', 'code'], order='name') diff --git a/addons/stock/static/src/views/list/stock_report_list_view.js b/addons/stock/static/src/views/list/stock_report_list_view.js new file mode 100644 index 00000000000..54945043398 --- /dev/null +++ b/addons/stock/static/src/views/list/stock_report_list_view.js @@ -0,0 +1,15 @@ +/** @odoo-module **/ + +import { listView } from '@web/views/list/list_view'; +import { registry } from "@web/core/registry"; +import { StockReportSearchModel } from "../search/stock_report_search_model"; +import { StockReportSearchPanel } from '../search/stock_report_search_panel'; + + +export const StockReportListView = { + ...listView, + SearchModel: StockReportSearchModel, + SearchPanel: StockReportSearchPanel, +}; + +registry.category("views").add("stock_report_list_view", StockReportListView); diff --git a/addons/stock/static/src/views/search/stock_report_search_model.js b/addons/stock/static/src/views/search/stock_report_search_model.js new file mode 100644 index 00000000000..d2d185e7bba --- /dev/null +++ b/addons/stock/static/src/views/search/stock_report_search_model.js @@ -0,0 +1,48 @@ +/** @odoo-module **/ + +import { SearchModel } from "@web/search/search_model"; + +export class StockReportSearchModel extends SearchModel { + + async load() { + await super.load(...arguments); + await this._loadWarehouses(); + } + + + //--------------------------------------------------------------------- + // Actions / Getters + //--------------------------------------------------------------------- + + getWarehouses() { + return this.warehouses; + } + + async _loadWarehouses() { + this.warehouses = await this.orm.call( + 'stock.warehouse', + 'get_current_warehouses', + [[]], + { context: this.context }, + ); + } + + /** + * Clears the context of a warehouse so values calculate based on all possible + * warehouses + */ + clearWarehouseContext() { + delete this.globalContext.warehouse; + this._notify(); + } + + /** + * @param {number} warehouse_id + * Sets the context to the selected warehouse so values that take this into account + * will recalculate based on this without filtering out any records + */ + applyWarehouseContext(warehouse_id) { + this.globalContext['warehouse'] = warehouse_id; + this._notify(); + } +} diff --git a/addons/stock/static/src/views/search/stock_report_search_panel.js b/addons/stock/static/src/views/search/stock_report_search_panel.js new file mode 100644 index 00000000000..5cdcda15694 --- /dev/null +++ b/addons/stock/static/src/views/search/stock_report_search_panel.js @@ -0,0 +1,30 @@ +/** @odoo-module **/ + +import { SearchPanel } from "@web/search/search_panel/search_panel"; + +export class StockReportSearchPanel extends SearchPanel { + setup() { + super.setup(...arguments); + this.selectedWarehouse = false; + } + + //--------------------------------------------------------------------- + // Actions / Getters + //--------------------------------------------------------------------- + + get warehouses() { + return this.env.searchModel.getWarehouses(); + } + + clearWarehouseContext() { + this.env.searchModel.clearWarehouseContext(); + this.selectedWarehouse = null; + } + + applyWarehouseContext(warehouse_id) { + this.env.searchModel.applyWarehouseContext(warehouse_id); + this.selectedWarehouse = warehouse_id; + } +} + +StockReportSearchPanel.template = "stock.StockReportSearchPanel"; diff --git a/addons/stock/static/src/views/search/stock_report_search_panel.xml b/addons/stock/static/src/views/search/stock_report_search_panel.xml new file mode 100644 index 00000000000..0fddad34d1f --- /dev/null +++ b/addons/stock/static/src/views/search/stock_report_search_panel.xml @@ -0,0 +1,24 @@ + + + + + + + + + Warehouses + + + + All Warehouses + + + + + + + + + diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml index 087da7de0d3..bbbf54ddbe5 100644 --- a/addons/stock/views/product_views.xml +++ b/addons/stock/views/product_views.xml @@ -450,6 +450,74 @@ + + + product.product.stock.tree + product.product + + + + + + + + + + + + + + + + + + + + + + + product.product.search.stock.form.stock.report + product.product + primary + + + + {'invisible': 1} + + + {'invisible': 1} + + + + + + + + + + + Stock + product.product + tree,form + + + {'default_detailed_type': 'product'} + [('detailed_type', '=', 'product')] + + + No stock to show + + Create some storable products to see their stock info in this view. + + + + + Products ir.actions.act_window @@ -479,6 +547,8 @@ + diff --git a/addons/stock/views/stock_orderpoint_views.xml b/addons/stock/views/stock_orderpoint_views.xml index d3f74723404..b1f1e81a186 100644 --- a/addons/stock/views/stock_orderpoint_views.xml +++ b/addons/stock/views/stock_orderpoint_views.xml @@ -43,7 +43,7 @@ - + diff --git a/addons/stock_account/__manifest__.py b/addons/stock_account/__manifest__.py index 71d79243b45..f1b67867cd2 100644 --- a/addons/stock_account/__manifest__.py +++ b/addons/stock_account/__manifest__.py @@ -29,10 +29,10 @@ Dashboard / Reports for Warehouse Management includes: 'views/stock_account_views.xml', 'views/res_config_settings_views.xml', 'data/product_data.xml', - 'views/product_views.xml', 'views/report_invoice.xml', 'views/stock_valuation_layer_views.xml', 'views/stock_quant_views.xml', + 'views/product_views.xml', 'wizard/stock_request_count.xml', 'wizard/stock_valuation_layer_revaluation_views.xml', 'wizard/stock_quantity_history.xml', diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 61aa0939440..779e9daff66 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -96,6 +96,12 @@ class ProductProduct(models.Model): value_svl = fields.Float(compute='_compute_value_svl', compute_sudo=True) quantity_svl = fields.Float(compute='_compute_value_svl', compute_sudo=True) + avg_cost = fields.Monetary(string="Average Cost", compute='_compute_value_svl', compute_sudo=True, currency_field='company_currency_id') + total_value = fields.Monetary(string="Total Value", compute='_compute_value_svl', compute_sudo=True, currency_field='company_currency_id') + company_currency_id = fields.Many2one( + 'res.currency', 'Valuation Currency', compute='_compute_value_svl', compute_sudo=True, + help="Technical field to correctly show the currently selected company's currency that corresponds " + "to the totaled value of the product's valuation layers") stock_valuation_layer_ids = fields.One2many('stock.valuation.layer', 'product_id') valuation = fields.Selection(related="categ_id.property_valuation", readonly=True) cost_method = fields.Selection(related="categ_id.property_cost_method", readonly=True) @@ -108,11 +114,12 @@ class ProductProduct(models.Model): @api.depends('stock_valuation_layer_ids') @api.depends_context('to_date', 'company') def _compute_value_svl(self): - """Compute `value_svl` and `quantity_svl`.""" - company_id = self.env.company.id + """Compute totals of multiple svl related values""" + company_id = self.env.company + self.company_currency_id = company_id.currency_id domain = [ ('product_id', 'in', self.ids), - ('company_id', '=', company_id), + ('company_id', '=', company_id.id), ] if self.env.context.get('to_date'): to_date = fields.Datetime.to_datetime(self.env.context['to_date']) @@ -121,12 +128,18 @@ class ProductProduct(models.Model): products = self.browse() for group in groups: product = self.browse(group['product_id'][0]) - product.value_svl = self.env.company.currency_id.round(group['value']) + value_svl = company_id.currency_id.round(group['value']) + avg_cost = value_svl / group['quantity'] if group['quantity'] else 0 + product.value_svl = value_svl product.quantity_svl = group['quantity'] + product.avg_cost = avg_cost + product.total_value = avg_cost * product.sudo(False).qty_available products |= product remaining = (self - products) remaining.value_svl = 0 remaining.quantity_svl = 0 + remaining.avg_cost = 0 + remaining.total_value = 0 # ------------------------------------------------------------------------- # Actions diff --git a/addons/stock_account/views/product_views.xml b/addons/stock_account/views/product_views.xml index e657b71979e..f5a127c0c1c 100644 --- a/addons/stock_account/views/product_views.xml +++ b/addons/stock_account/views/product_views.xml @@ -45,5 +45,22 @@ + + + + product.product.stock.tree.inherit.stock.account + product.product + + + + + + + + + + +
+ No stock to show +
+ Create some storable products to see their stock info in this view. +