From ec0b1005324cfb6856d432e40b07d01297a699a0 Mon Sep 17 00:00:00 2001 From: JF Aubert Date: Tue, 30 Aug 2022 12:32:57 +0000 Subject: [PATCH] [REF] stock: convert inventory_report_list to wowl closes odoo/odoo#98436 Task: 2885757 Signed-off-by: Arnold Moyaux (arm) --- addons/stock/__manifest__.py | 5 +- .../js/inventory_report_list_controller.js | 61 ---- .../src/js/inventory_report_list_view.js | 19 -- .../js/inventory_singleton_list_controller.js | 141 -------- .../js/inventory_singleton_list_renderer.js | 31 -- .../src/js/inventory_singleton_list_view.js | 20 -- .../src/views/list/inventory_report_list.scss | 13 + .../src/views/list/inventory_report_list.xml | 17 + .../list/inventory_report_list_controller.js | 103 ++++++ .../views/list/inventory_report_list_model.js | 30 ++ .../views/list/inventory_report_list_view.js | 15 + .../static/src/xml/inventory_adjustments.xml | 12 - .../stock/static/src/xml/inventory_report.xml | 14 - .../tests/inventory_report_list_tests.js | 200 ++++++++++++ .../static/tests/singleton_list_tests.js | 305 ------------------ addons/stock/views/stock_quant_views.xml | 8 +- .../static/src/xml/inventory_report.xml | 4 +- 17 files changed, 389 insertions(+), 609 deletions(-) delete mode 100644 addons/stock/static/src/js/inventory_report_list_controller.js delete mode 100644 addons/stock/static/src/js/inventory_report_list_view.js delete mode 100644 addons/stock/static/src/js/inventory_singleton_list_controller.js delete mode 100644 addons/stock/static/src/js/inventory_singleton_list_renderer.js delete mode 100644 addons/stock/static/src/js/inventory_singleton_list_view.js create mode 100644 addons/stock/static/src/views/list/inventory_report_list.scss create mode 100644 addons/stock/static/src/views/list/inventory_report_list.xml create mode 100644 addons/stock/static/src/views/list/inventory_report_list_controller.js create mode 100644 addons/stock/static/src/views/list/inventory_report_list_model.js create mode 100644 addons/stock/static/src/views/list/inventory_report_list_view.js delete mode 100644 addons/stock/static/src/xml/inventory_adjustments.xml delete mode 100644 addons/stock/static/src/xml/inventory_report.xml create mode 100644 addons/stock/static/tests/inventory_report_list_tests.js delete mode 100644 addons/stock/static/tests/singleton_list_tests.js diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index a73a8a57d00..505e519f8b5 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -104,15 +104,16 @@ ], 'web.assets_backend': [ 'stock/static/src/**/*.js', - 'stock/static/src/views/**/*.js', 'stock/static/src/scss/forecast_widget.scss', 'stock/static/src/scss/stock_empty_screen.scss', + 'stock/static/src/views/**/*.js', + 'stock/static/src/views/**/**.scss', ], 'web.assets_tests': [ 'stock/static/tests/tours/stock_report_tests.js', ], 'web.qunit_suite_tests': [ - 'stock/static/tests/singleton_list_tests.js', + 'stock/static/tests/inventory_report_list_tests.js', 'stock/static/tests/popover_widget_tests.js', 'stock/static/tests/lazy_column_list_tests.js', 'stock/static/tests/stock_traceability_report_backend_tests.js', diff --git a/addons/stock/static/src/js/inventory_report_list_controller.js b/addons/stock/static/src/js/inventory_report_list_controller.js deleted file mode 100644 index 6bbf661b2d4..00000000000 --- a/addons/stock/static/src/js/inventory_report_list_controller.js +++ /dev/null @@ -1,61 +0,0 @@ -odoo.define('stock.InventoryReportListController', function (require) { -"use strict"; - -var ListController = require('web.ListController'); - -var InventoryReportListController = ListController.extend({ - buttons_template: 'StockInventoryReport.Buttons', - - // ------------------------------------------------------------------------- - // Public - // ------------------------------------------------------------------------- - - init: function (parent, model, renderer, params) { - this.context = renderer.state.getContext(); - return this._super.apply(this, arguments); - }, - - /** - * @override - */ - renderButtons: function ($node) { - this._super.apply(this, arguments); - if (this.context.no_at_date) { - this.$buttons.find('button.o_button_at_date').hide(); - } - this.$buttons.on('click', '.o_button_at_date', this._onOpenWizard.bind(this)); - }, - - // ------------------------------------------------------------------------- - // Handlers - // ------------------------------------------------------------------------- - - /** - * Handler called when the user clicked on the 'Valuation at Date' button. - * Opens wizard to display, at choice, the products inventory or a computed - * inventory at a given date. - */ - _onOpenWizard: function () { - var state = this.model.get(this.handle, {raw: true}); - var stateContext = state.getContext(); - var context = { - active_model: this.modelName, - }; - if (stateContext.default_product_id) { - context.product_id = stateContext.default_product_id; - } else if (stateContext.product_tmpl_id) { - context.product_tmpl_id = stateContext.product_tmpl_id; - } - this.do_action({ - res_model: 'stock.quantity.history', - views: [[false, 'form']], - target: 'new', - type: 'ir.actions.act_window', - context: context, - }); - }, -}); - -return InventoryReportListController; - -}); diff --git a/addons/stock/static/src/js/inventory_report_list_view.js b/addons/stock/static/src/js/inventory_report_list_view.js deleted file mode 100644 index 494dd838b73..00000000000 --- a/addons/stock/static/src/js/inventory_report_list_view.js +++ /dev/null @@ -1,19 +0,0 @@ -odoo.define('stock.InventoryReportListView', function (require) { -"use strict"; - -var ListView = require('web.ListView'); -var InventoryReportListController = require('stock.InventoryReportListController'); -var viewRegistry = require('web.view_registry'); - - -var InventoryReportListView = ListView.extend({ - config: _.extend({}, ListView.prototype.config, { - Controller: InventoryReportListController, - }), -}); - -viewRegistry.add('inventory_report_list', InventoryReportListView); - -return InventoryReportListView; - -}); diff --git a/addons/stock/static/src/js/inventory_singleton_list_controller.js b/addons/stock/static/src/js/inventory_singleton_list_controller.js deleted file mode 100644 index c36ad06a04c..00000000000 --- a/addons/stock/static/src/js/inventory_singleton_list_controller.js +++ /dev/null @@ -1,141 +0,0 @@ -odoo.define('stock.SingletonListController', function (require) { -"use strict"; - -var core = require('web.core'); -var InventoryReportListController = require('stock.InventoryReportListController'); -var session = require('web.session'); - -var _t = core._t; - -/** - * The purpose of this override has 2 purposes: - * - * 1. Avoid having two or more similar records in the list view. - * It is expected to be used in quant list views, when the list is editable - * and allows new line creation. Checks for whether newly created quant - * already exists and if it does => update the existing quant instead of the - * newly created one, and then we refresh the view to avoid having 2 similar lines. - * - * 2. Support Inventory Adjustments (i.e. inventory counts) view. - * Specifically adds necessary buttons (or hides them when appropriate) and - * autosaving logic to prevent button actions before edits are saved. - */ - -var SingletonListController = InventoryReportListController.extend({ - - - init: function (parent, model, renderer, params) { - this.context = renderer.state.getContext(); - return this._super(...arguments); - }, - - willStart() { - const sup = this._super(...arguments); - const acl = this.getSession().user_has_group('stock.group_stock_manager').then(hasGroup => { - if (hasGroup) { - this.buttons_template = 'StockInventoryAdjustments.Buttons'; - } - }); - return Promise.all([sup, acl]); - }, - - /** - * @override - */ - renderButtons: function ($node) { - this._super(...arguments); - if (this.context.inventory_report_mode) { - this.$buttons.find('button.o_button_apply_all').hide(); - } - this.$buttons.on('click', '.o_button_apply_all', this._onApplyAll.bind(this)); - }, - - // ------------------------------------------------------------------------- - // Private - // ------------------------------------------------------------------------- - - /** - * @override - * @return {Promise} rejected when updating the list because we don't want - * to select a cell that might not exist anymore. - */ - _confirmSave: function (id) { - var newRecord = this.model.localData[id]; - var model = newRecord.model; - var res_id = newRecord.res_id; - - var findSimilarRecords = function (record) { - if ((record.groupedBy && record.groupedBy.length > 0) || record.data.length) { - var recordsToReturn = []; - for (var i in record.data) { - var foundRecords = findSimilarRecords(record.data[i]); - recordsToReturn = recordsToReturn.concat(foundRecords || []); - } - return recordsToReturn; - } else { - if (record.res_id === res_id && record.model === model) { - if (record.count === 0){ - return [record]; - } - else if (record.ref && record.ref.indexOf('virtual') !== -1) { - return [record]; - } - } - } - }; - - var handle = this.model.get(this.handle); - var similarRecords = findSimilarRecords(handle); - - if (similarRecords.length > 1) { - var notification = _t("You have tried to create a record which already exists. " + - "The existing record has been modified instead."); - this.displayNotification({ title: _t("This record already exists."), message: notification }); - this.reload(); - return Promise.reject(); - } - else { - return this._super.apply(this, arguments); - } - }, - - // ------------------------------------------------------------------------- - // Handlers - // ------------------------------------------------------------------------- - - /** - * - * @private - * @param {OdooEvent} ev - */ - _onButtonClicked: function (ev) { - ev.stopPropagation(); - var self = this; - return self.saveRecord(ev.data.record.id, { - stayInEdit: true, - }).then(function () { - // we need to re-get the record to make sure we have changes made - // by the basic model, such as the new res_id, if the record is - // new. - var record = self.model.get(ev.data.record.id); - return self._callButtonAction(ev.data.attrs, record); - }).then(function () { - self._enableButtons(); - }).guardedCatch(this._enableButtons.bind(this)); - }, - - _onApplyAll: async function () { - const resIds = await this._domainToResIds(this.renderer.state.getDomain(), session.active_ids_limit); - this.do_action('stock.action_stock_inventory_adjustement_name', { - additional_context: { - 'active_model': 'ir.ui.view', - 'active_ids': resIds, - }, - on_close: this.reload, - }); - }, -}); - -return SingletonListController; - -}); diff --git a/addons/stock/static/src/js/inventory_singleton_list_renderer.js b/addons/stock/static/src/js/inventory_singleton_list_renderer.js deleted file mode 100644 index 970f0ddfbec..00000000000 --- a/addons/stock/static/src/js/inventory_singleton_list_renderer.js +++ /dev/null @@ -1,31 +0,0 @@ -odoo.define('stock.SingletonListRenderer', function (require) { -"use strict"; - -var ListRenderer = require('web.ListRenderer'); - -/** - * The purpose of this override is to disable line buttons when the row is edited. - * - */ - -var SingletonListRenderer = ListRenderer.extend({ - - _disableRecordSelectors: function () { - this._super.apply(this, arguments); - var row = this._getRow(this._getRecordID(this.currentRow))[0]; - var lineButtons = row.querySelectorAll('button[name=action_set_inventory_quantity]'); - lineButtons.forEach(elem => elem.setAttribute('disabled', true)); - }, - /** - * @private - */ - _enableRecordSelectors: function () { - this._super.apply(this, arguments); - var lineButtons = this.el.querySelectorAll('button.disabled'); - lineButtons.forEach(elem => elem.removeAttribute('disabled')); - }, -}); - -return SingletonListRenderer; - -}); diff --git a/addons/stock/static/src/js/inventory_singleton_list_view.js b/addons/stock/static/src/js/inventory_singleton_list_view.js deleted file mode 100644 index 4c4855d8f96..00000000000 --- a/addons/stock/static/src/js/inventory_singleton_list_view.js +++ /dev/null @@ -1,20 +0,0 @@ -odoo.define('stock.SingletonListView', function (require) { -'use strict'; - -var InventoryReportListView = require('stock.InventoryReportListView'); -var SingletonListController = require('stock.SingletonListController'); -var SingletonListRenderer = require('stock.SingletonListRenderer'); -var viewRegistry = require('web.view_registry'); - -var SingletonListView = InventoryReportListView.extend({ - config: _.extend({}, InventoryReportListView.prototype.config, { - Controller: SingletonListController, - Renderer: SingletonListRenderer, - }), -}); - -viewRegistry.add('singleton_list', SingletonListView); - -return SingletonListView; - -}); diff --git a/addons/stock/static/src/views/list/inventory_report_list.scss b/addons/stock/static/src/views/list/inventory_report_list.scss new file mode 100644 index 00000000000..368a76b160b --- /dev/null +++ b/addons/stock/static/src/views/list/inventory_report_list.scss @@ -0,0 +1,13 @@ + +.o_inventory_adjustments { + + button[name='action_set_inventory_quantity'] { + pointer-events: auto; + } + .o_selected_row { + button[name='action_set_inventory_quantity'] { + pointer-events: none; + color: $text-muted; + } + } +} diff --git a/addons/stock/static/src/views/list/inventory_report_list.xml b/addons/stock/static/src/views/list/inventory_report_list.xml new file mode 100644 index 00000000000..853f20d691b --- /dev/null +++ b/addons/stock/static/src/views/list/inventory_report_list.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/addons/stock/static/src/views/list/inventory_report_list_controller.js b/addons/stock/static/src/views/list/inventory_report_list_controller.js new file mode 100644 index 00000000000..e2ec1bfa697 --- /dev/null +++ b/addons/stock/static/src/views/list/inventory_report_list_controller.js @@ -0,0 +1,103 @@ +/** @odoo-module */ + +import { ListController } from '@web/views/list/list_controller'; +import { useBus } from "@web/core/utils/hooks"; +import { session } from "@web/session"; + +export class InventoryReportListController extends ListController { + + setup() { + super.setup(); + if (this.props.context.inventory_mode || this.props.context.inventory_report_mode) { + useBus(this.model, "record-updated", + this.recordUpdated + ); + } + } + + /** + * Handler called when the user clicked on the 'Valuation at Date' button. + * Opens wizard to display, at choice, the products inventory or a computed + * inventory at a given date. + */ + async onClickInventoryAtDate() { + const context = { + active_model: this.props.resModel, + }; + if (this.props.context.default_product_id) { + context.product_id = this.props.context.default_product_id; + } else if (this.props.context.product_tmpl_id) { + context.product_tmpl_id = this.props.context.product_tmpl_id; + } + this.actionService.doAction({ + res_model: 'stock.quantity.history', + views: [[false, 'form']], + target: 'new', + type: 'ir.actions.act_window', + context, + }); + } + + /** + * Count 'resId' loaded records. + * Multiple records occurs when adding an already loaded-in-view record. + */ + _countResIdRecords(level, resId) { + let count = 0; + if (level.groupByInfo && level.isGrouped) { + for (const i in level.groups) { + count += this._countResIdRecords(level.groups[i], resId); + } + } + else { + const records = level.groupByInfo ? level.list.records : level.records; + count += records.filter(record => record.resId == resId).length; + } + return count; + } + + /** + * Handler called when a record has been created or updated. + * We need to detect when the user added to the list a quant which already exists + * (see stock.quant.create), either already loaded or not, to warn the user + * the quant was updated. + * This is done by checking : + * - the record id against the 'lastCreatedRecordId' on model + * - the create_date against the write_date (both are equal for newly created records). + */ + async recordUpdated(event) { + const record = event.detail.record; + const justCreated = record.id == record.model.lastCreatedRecordId; + if (justCreated && record.data.create_date.diff(record.data.write_date) != 0) { + this.notificationService.add( + this.env._t("You tried to create a record that already exists. The existing record was modified instead."), + { title: this.env._t("This record already exists.") } + ); + if (this._countResIdRecords(record.model.root, record.resId) > 1) { + /* more than 1 'resId' record loaded in view (user added an already loaded record) : + * - both have been updated + * - remove the current record (the added one) + */ + this.model.root.removeRecord(record); + } + } + } + + /** + * Handler called when the user clicked on the 'Apply all' button. + */ + async onClickApplyAll() { + const activeIds = await this.model.orm.search(this.props.resModel, this.props.domain, { + limit: session.active_ids_limit, + context: this.props.context, + }); + return this.actionService.doAction('stock.action_stock_inventory_adjustement_name', { + additionalContext: { + active_ids: activeIds, + }, + onClose: () => { + this.model.load(); + }, + }); + } +} diff --git a/addons/stock/static/src/views/list/inventory_report_list_model.js b/addons/stock/static/src/views/list/inventory_report_list_model.js new file mode 100644 index 00000000000..b3b41ad0f0f --- /dev/null +++ b/addons/stock/static/src/views/list/inventory_report_list_model.js @@ -0,0 +1,30 @@ +/** @odoo-module */ + +import { RelationalModel, DynamicRecordList } from "@web/views/relational_model"; + +export class InventoryReportListModel extends RelationalModel { + + /** + * Override + */ + setup(params, { action, dialog, notification, rpc, user, view, company }) { + // model has not created any record yet + this.lastCreatedRecordId = ""; + return super.setup(...arguments); + } +} + +export class InventoryReportListDynamicRecordList extends DynamicRecordList { + + /** + * Override + */ + async createRecord(params = {}, atFirstPosition = false) { + const record = await super.createRecord(...arguments); + // keep created record id on model + record.model.lastCreatedRecordId = record.id; + return record; + } +} + +InventoryReportListModel.DynamicRecordList = InventoryReportListDynamicRecordList; diff --git a/addons/stock/static/src/views/list/inventory_report_list_view.js b/addons/stock/static/src/views/list/inventory_report_list_view.js new file mode 100644 index 00000000000..bce20f97ee8 --- /dev/null +++ b/addons/stock/static/src/views/list/inventory_report_list_view.js @@ -0,0 +1,15 @@ +/** @odoo-module */ + +import { listView } from "@web/views/list/list_view"; +import { InventoryReportListModel } from "./inventory_report_list_model"; +import { InventoryReportListController } from "./inventory_report_list_controller"; +import { registry } from "@web/core/registry"; + +export const InventoryReportListView = { + ...listView, + Model: InventoryReportListModel, + Controller: InventoryReportListController, + buttonTemplate: 'InventoryReport.Buttons', +}; + +registry.category("views").add('inventory_report_list', InventoryReportListView); diff --git a/addons/stock/static/src/xml/inventory_adjustments.xml b/addons/stock/static/src/xml/inventory_adjustments.xml deleted file mode 100644 index b423d7e70cf..00000000000 --- a/addons/stock/static/src/xml/inventory_adjustments.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/addons/stock/static/src/xml/inventory_report.xml b/addons/stock/static/src/xml/inventory_report.xml deleted file mode 100644 index 5b4484eeb1f..00000000000 --- a/addons/stock/static/src/xml/inventory_report.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - diff --git a/addons/stock/static/tests/inventory_report_list_tests.js b/addons/stock/static/tests/inventory_report_list_tests.js new file mode 100644 index 00000000000..138b7b59570 --- /dev/null +++ b/addons/stock/static/tests/inventory_report_list_tests.js @@ -0,0 +1,200 @@ +/** @odoo-module **/ + +import { getFixture, click, editInput } from "@web/../tests/helpers/utils"; +import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; + +const { DateTime } = luxon; + +let target, serverData; + +const arch = + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''; + +const setup_date = DateTime.fromISO('2022-01-03T08:03:44+00:00').toSQL(); + +function mockRPC(route, args) { + if (route === '/web/dataset/call_kw/person/create') { + // simulate 'stock.quant' create function which can return existing record + args.args[0].create_date = DateTime.now().toSQL(); + args.args[0].write_date = args.args[0].create_date; + var name = args.args[0].name; + var age = args.args[0].age; + var job = args.args[0].job; + for (var d of serverData.models.person.records) { + if (d.name === name) { + d.age = age; + d.job = job; + d.write_date = args.args[0].write_date; + return Promise.resolve(d.id); + } + } + } +} + +QUnit.module( + "Views", + { + beforeEach() { + target = getFixture(); + serverData = { + models: { + person: { + fields: { + name: {string: "Name", type: "char"}, + age: {string: "Age", type: "integer"}, + job: {string: "Profession", type: "char"}, + // standard fields + create_date: {string: "Created on", type: "datetime" }, + write_date: {string: "Last Updated on", type: "datetime" }, + }, + records: [ + {id: 1, name: 'Daniel Fortesque', age: 32, job: 'Soldier', create_date: setup_date, write_date: setup_date}, + {id: 2, name: 'Samuel Oak', age: 64, job: 'Professor', create_date: setup_date, write_date: setup_date}, + {id: 3, name: 'Leto II Atreides', age: 128, job: 'Emperor', create_date: setup_date, write_date: setup_date}, + ] + }, + }, + }; + setupViewRegistries(); + }, + }, + function () { + + QUnit.module("InventoryReportListView"); + + QUnit.test('Create new record correctly', async function (assert) { + + assert.expect(2); + + await makeView({ + type: 'list', + resModel: 'person', + serverData, + arch: arch, + mockRPC: mockRPC, + context: { + inventory_mode: true, + }, + }); + + // Check we have initially 3 records + assert.containsN(target, '.o_data_row', 3, "should have 3 records"); + + // Create a new line... + await click(target.querySelector(".o_list_button_add")); + await editInput(target, "[name=name] input", 'Bilou'); + await editInput(target, "[name=age] input", '24'); + await click(target.querySelector(".o_list_button_save")); + + // Check new record is in the list + assert.containsN(target, '.o_data_row', 4, "should now have 4 records"); + }); + + QUnit.test('Don\'t duplicate record', async function (assert) { + + assert.expect(3); + + await makeView({ + type: 'list', + resModel: 'person', + serverData, + arch: arch, + mockRPC: mockRPC, + context: { + inventory_mode: true, + }, + }); + + // Check we have initially 3 records + assert.containsN(target, '.o_data_row', 3, "should have 3 records"); + + // Create a new line for an existing record... + let name = serverData.models.person.records[2].name, age = '72'; + await click(target.querySelector(".o_list_button_add")); + await editInput(target, "[name=name] input", name); + await editInput(target, "[name=age] input", age); + await click(target.querySelector(".o_list_button_save")); + + // Check we still have 3 records... + assert.containsN(target, '.o_data_row', 3, "should still have 3 records"); + // ... and verify update has occurred. + var nameField =$(target).find('td[data-tooltip="' + name + '"]'); + var ageField = nameField.parent().find('.o_list_number'); + assert.strictEqual(ageField.text(), age, "The age field must be updated"); + }); + + QUnit.test('Work in grouped list', async function (assert) { + + assert.expect(6); + + await makeView({ + type: "list", + resModel: 'person', + serverData, + arch: arch, + mockRPC: mockRPC, + context: { + inventory_mode: true, + }, + groupBy: ['job'], // Groups are Emperor, Professor, Soldier + }); + + // Open 'Professor' group + await click(target.querySelectorAll(".o_group_header")[1]); + + // Check we have only 1 record... + assert.containsN(target, '.o_data_row', 1, "should have 1 record"); + + // Create a new record... + let name = 'Del Tutorial', age = "32"; + await click(target, ".o_group_field_row_add a"); + await editInput(target, "[name=name] input", name); + await editInput(target, "[name=age] input", age); + await click(target.querySelector(".o_list_button_save")); + // Check we have 2 records... + assert.containsN(target, '.o_data_row', 2, "should have 2 records"); + + // Create an existing record... + name = serverData.models.person.records[1].name; + age = "55"; + await click(target, ".o_group_field_row_add a"); + await editInput(target, "[name=name] input", name); + await editInput(target, "[name=age] input", age); + await click(target.querySelector(".o_list_button_save")); + // Check we still have 2 records... + assert.containsN(target, '.o_data_row', 2, "should still have 2 records"); + + // Create an existing but not displayed record... + name = serverData.models.person.records[0].name; + await click(target, ".o_group_field_row_add a"); + await editInput(target, "[name=name] input", name); + await editInput(target, "[name=age] input", age); + await editInput(target, "[name=job] input", "Soldier"); // let it in its original group + await click(target.querySelector(".o_list_button_save")); + // Check we have 3 records... + assert.containsN(target, '.o_data_row', 3, "should have 3 records"); + + // Opens 'Soldier' group + await click(target.querySelectorAll(".o_group_header")[2]); + let rows = target.querySelectorAll(".o_data_row"); + + // Check 'original' record has been updated... + // : Daniel Fortesque is in record 0 for group Soldier and in record 3 for group Professor + assert.strictEqual(rows[0].querySelector('[name="age"]').textContent, age, "age of the record must be updated"); + + // Edit the freshly created record... + await click(rows[3].querySelector(".o_field_cell")); + await editInput(target, "[name=age] input", "66"); + await click(target, ".o_list_view"); + + // Check both records have been updated... + assert.strictEqual(rows[0].querySelector('[name="age"]').textContent, rows[3].querySelector('[name="age"]').textContent, "age of the record must be updated"); + }); + } +); diff --git a/addons/stock/static/tests/singleton_list_tests.js b/addons/stock/static/tests/singleton_list_tests.js deleted file mode 100644 index 320f0779dfd..00000000000 --- a/addons/stock/static/tests/singleton_list_tests.js +++ /dev/null @@ -1,305 +0,0 @@ -odoo.define('web.singleton_list_tests', function (require) { -"use strict"; - -var SingletonListView = require('stock.SingletonListView'); -var testUtils = require('web.test_utils'); - -var createView = testUtils.createView; - - -QUnit.module('Views', { - beforeEach: function () { - this.data = { - person: { - fields: { - name: {string: "Name", type: "char"}, - age: {string: "Age", type: "integer"}, - job: {string: "Profession", type: "char"}, - }, - records: [ - {id: 1, name: 'Daniel Fortesque', age: 32, job: 'Soldier'}, - {id: 2, name: 'Samuel Oak', age: 64, job: 'Professor'}, - {id: 3, name: 'Leto II Atreides', age: 128, job: 'Emperor'}, - ] - }, - }; - this.mockRPC = function (route, args) { - if (route === '/web/dataset/call_kw/person/create') { - var name = args.args[0].name; - var age = args.args[0].age; - var job = args.args[0].job; - for (var d of this.data.person.records) { - if (d.name === name) { - d.age = age; - d.job = job; - return Promise.resolve(d.id); - } - } - } - return this._super.apply(this, arguments); - }; - } -}, function () { - - QUnit.module('SingletonListView'); - - QUnit.test('Create new record correctly', async function (assert) { - assert.expect(2); - - var list = await createView({ - View: SingletonListView, - model: 'person', - data: this.data, - arch: ''+ - ''+ - ''+ - '', - mockRPC: this.mockRPC, - }); - // Checks we have initially 3 records - assert.containsN(list, '.o_data_row', 3, "should have 3 records"); - - // Creates a new line... - await testUtils.dom.click($('.o_list_button_add')); - // ... and fills fields with new values - var $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Bilou'); - await testUtils.fields.triggerKeydown($input, 'tab'); - - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '24'); - await testUtils.fields.triggerKeydown($input, 'enter'); - await testUtils.dom.click($('.o_list_button_save')); - - // Checks new record is in the list - assert.containsN(list, '.o_data_row', 4, "should now have 4 records"); - list.destroy(); - }); - - QUnit.test('Don\'t duplicate record', async function (assert) { - assert.expect(3); - - var list = await createView({ - View: SingletonListView, - model: 'person', - data: this.data, - arch: ''+ - ''+ - ''+ - '', - mockRPC: this.mockRPC, - }); - // Checks we have initially 3 records - assert.containsN(list, '.o_data_row', 3, "should have 3 records"); - - // Creates a new line... - await testUtils.dom.click($('.o_list_button_add')); - // ... and fills fields with already existing value - var $input = $('.o_selected_row input[name=name]'); - var name = 'Samuel Oak'; - await testUtils.fields.editInput($input, name); - await testUtils.fields.triggerKeydown($input, 'tab'); - - $input = $('.o_selected_row input[name=age]'); - var age = '72'; - await testUtils.fields.editInput($input, age); - await testUtils.fields.triggerKeydown($input, 'enter'); - - // Checks we have still only 3 records... - assert.containsN(list, '.o_data_row', 3, "should still have 3 records"); - // ... and verify modification was occurred. - var nameField = list.$('td[title="' + name + '"]'); - var ageField = nameField.parent().find('.o_list_number'); - assert.strictEqual(ageField.text(), age, "The age field must be updated"); - list.destroy(); - }); - - QUnit.test('Don\'t raise error when trying to create duplicate line', async function (assert) { - assert.expect(3); - /* In some condition, a list editable with the `singletonlist` js_class - can try to select a record at a line who isn't the same place anymore. - In this case, the list can try to find the id of an undefined record. - This test just insures we don't raise a traceback in this case. - */ - var list = await createView({ - View: SingletonListView, - model: 'person', - data: { - person: { - fields: { - name: {string: "Name", type: "char"}, - age: {string: "Age", type: "integer"}, - }, - records: [ - {id: 1, name: 'Bobby B. Bop', age: 18}, - ] - } - }, - arch: ''+ - ''+ - ''+ - '', - mockRPC: this.mockRPC, - }); - // Checks we have initially 1 record - assert.containsN(list, '.o_data_row', 1, "should have 1 records"); - - // Creates a new line... - await testUtils.dom.click($('.o_list_button_add')); - // ... and fills fields with already existing value - var $input = $('.o_selected_row input[name=name]'); - var name = 'Bobby B. Bop'; - await testUtils.fields.editInput($input, name); - await testUtils.fields.triggerKeydown($input, 'tab'); - - $input = $('.o_selected_row input[name=age]'); - var age = '22'; - await testUtils.fields.editInput($input, age); - // This operation causes list'll try to select undefined record. - await testUtils.fields.triggerKeydown($input, 'enter'); - - // Checks we have still only 1 record... - assert.containsN(list, '.o_data_row', 1, "should now have 1 records"); - // ... and verify modification was occurred. - var nameField = list.$('td[title="' + name + '"]'); - var ageField = nameField.parent().find('.o_list_number'); - assert.strictEqual(ageField.text(), age, "The age field must be updated"); - list.destroy(); - }); - - QUnit.test('Refresh the list only when needed', async function (assert) { - assert.expect(3); - - var refresh_count = 0; - var list = await createView({ - View: SingletonListView, - model: 'person', - data: this.data, - arch: ''+ - ''+ - ''+ - '', - mockRPC: this.mockRPC, - }); - list.realReload = list.reload; - list.reload = function () { - refresh_count++; - return this.realReload(); - }; - // Modify Record - await testUtils.dom.click(list.$('.o_data_row:nth-child(2) > .o_list_number')); - var $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '70'); - await testUtils.fields.triggerKeydown($input, 'enter'); - await testUtils.dom.click($('.o_list_button_save')); - assert.strictEqual(refresh_count, 0, "don't refresh when edit existing line"); - - // Add existing record - await testUtils.dom.click($('.o_list_button_add')); - $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Leto II Atreides'); - await testUtils.fields.triggerKeydown($input, 'tab'); - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '800'); - await testUtils.dom.click($('.o_list_button_save')); - assert.strictEqual(refresh_count, 1, "refresh after tried to create an existing record"); - - // Add new record - await testUtils.dom.click($('.o_list_button_add')); - $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Valentin Cognito'); - await testUtils.fields.triggerKeydown($input, 'tab'); - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '37'); - await testUtils.fields.triggerKeydown($input, 'enter'); - await testUtils.dom.click($('.o_list_button_save')); - assert.strictEqual(refresh_count, 1, "don't refresh when create entirely new record"); - - list.destroy(); - }); - - QUnit.test('Work in grouped list', async function (assert) { - assert.expect(6); - - var refresh_count = 0; - var list = await createView({ - View: SingletonListView, - model: 'person', - data: this.data, - arch: ''+ - ''+ - ''+ - ''+ - '', - mockRPC: this.mockRPC, - groupBy: ['job'], - }); - list.realReload = list.reload; - list.reload = function () { - refresh_count++; - return this.realReload(); - }; - // Opens 'Professor' group - await testUtils.dom.click(list.$('.o_group_header:nth-child(2)')); - - // Creates a new record... - await testUtils.dom.click(list.$('.o_add_record_row a')); - var $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Del Tutorial'); - await testUtils.fields.triggerKeydown($input, 'tab'); - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '32'); - await testUtils.fields.triggerKeydown($input, 'tab'); - await testUtils.dom.click($('.o_list_button_save')); - // ... then checks the list didn't refresh - assert.strictEqual(refresh_count, 0, - "don't refresh when creating new record"); - - // Creates an existing record in same group... - await testUtils.dom.click(list.$('.o_add_record_row a')); - var $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Samuel Oak'); - await testUtils.dom.click($('.o_list_button_save')); - // ... then checks the list has been refreshed - assert.strictEqual(refresh_count, 1, - "refresh when try to create an existing record"); - - // Creates an existing but not displayed record... - await testUtils.dom.click(list.$('.o_add_record_row a')); - var $input = $('.o_selected_row input[name=name]'); - await testUtils.fields.editInput($input, 'Daniel Fortesque'); - await testUtils.fields.triggerKeydown($input, 'tab'); - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '55'); - await testUtils.fields.triggerKeydown($input, 'tab'); - $input = $('.o_selected_row input[name=job]'); - await testUtils.fields.editInput($input, 'Soldier'); - await testUtils.dom.click($('.o_list_button_save')); - // .. then checks the list didn't refresh - assert.strictEqual(refresh_count, 1, - "don't refresh when creating an existing record but this record " + - "isn't present in the view"); - - // Opens 'Soldier' group - await testUtils.dom.click(list.$('.o_group_header:nth-child(1)').first()); - // Checks the record has been correctly updated - var ageCell = $('tr.o_data_row td.o_list_number').first(); - assert.strictEqual(ageCell.text(), "55", - "age of the record must be updated"); - // Edits the freshly created record... - await testUtils.dom.click(list.$('tr.o_data_row td.o_list_number').eq(1)); - $input = $('.o_selected_row input[name=age]'); - await testUtils.fields.editInput($input, '66'); - await testUtils.dom.click($('.o_list_button_save')); - // ... then checks the list and data have been refreshed - assert.strictEqual(refresh_count, 2, - "refresh when try to create an existing record present in the view"); - ageCell = $('tr.o_data_row td.o_list_number').first(); - assert.strictEqual(ageCell.text(), "66", - "age of the record must be updated"); - - list.destroy(); - }); -}); - -}); diff --git a/addons/stock/views/stock_quant_views.xml b/addons/stock/views/stock_quant_views.xml index 7968323cadc..c84ec86681f 100644 --- a/addons/stock/views/stock_quant_views.xml +++ b/addons/stock/views/stock_quant_views.xml @@ -102,8 +102,10 @@ + + @@ -343,12 +345,14 @@ stock.quant - +
+ + diff --git a/addons/stock_account/static/src/xml/inventory_report.xml b/addons/stock_account/static/src/xml/inventory_report.xml index f1679721249..e4c6cce3f1e 100644 --- a/addons/stock_account/static/src/xml/inventory_report.xml +++ b/addons/stock_account/static/src/xml/inventory_report.xml @@ -1,9 +1,9 @@ - + - + Valuation at Date