diff --git a/addons/account/static/src/components/account_move_form/account_move_form.js b/addons/account/static/src/components/account_move_form/account_move_form.js index d0189f65f20..b6c12f81881 100644 --- a/addons/account/static/src/components/account_move_form/account_move_form.js +++ b/addons/account/static/src/components/account_move_form/account_move_form.js @@ -37,7 +37,7 @@ AccountMoveFormNotebook.props = { } export class AccountMoveFormRenderer extends FormRenderer { async saveBeforeTabChange() { - if (this.props.record.mode === "edit" && this.props.record.isDirty) { + if (this.props.record.mode === "edit" && await this.props.record.isDirty()) { const contentEl = document.querySelector('.o_content'); const scrollPos = contentEl.scrollTop; await this.props.record.save({ diff --git a/addons/account/static/src/components/account_payment_field/account_payment_field.js b/addons/account/static/src/components/account_payment_field/account_payment_field.js index 5da2d3b00d9..f356c6bd599 100644 --- a/addons/account/static/src/components/account_payment_field/account_payment_field.js +++ b/addons/account/static/src/components/account_payment_field/account_payment_field.js @@ -35,7 +35,7 @@ export class AccountPaymentField extends Component { content: [], outstanding: false, title: "", - move_id: this.props.record.data.id, + move_id: this.props.record.resId, }; for (let [key, value] of Object.entries(info.content)) { value.index = key; diff --git a/addons/account/static/src/components/mail_attachments/mail_attachments.js b/addons/account/static/src/components/mail_attachments/mail_attachments.js index a8d46ac5c56..3e68a8f0111 100644 --- a/addons/account/static/src/components/mail_attachments/mail_attachments.js +++ b/addons/account/static/src/components/mail_attachments/mail_attachments.js @@ -64,7 +64,7 @@ export class MailAttachments extends Component { async onWillUnmount(){ // Unlink added attachments if the wizard is not saved. - if(!this.props.record.data.id){ + if(!this.props.record.resId){ this.getValue().forEach((item) => { if(item.manual){ this.attachmentIdsToUnlink.add(item.id); diff --git a/addons/account/static/src/components/tax_totals/tax_totals.js b/addons/account/static/src/components/tax_totals/tax_totals.js index 282ecb60891..1072dd5277f 100644 --- a/addons/account/static/src/components/tax_totals/tax_totals.js +++ b/addons/account/static/src/components/tax_totals/tax_totals.js @@ -6,7 +6,7 @@ import { standardFieldProps } from "@web/views/fields/standard_field_props"; import { registry } from "@web/core/registry"; import { getCurrency } from "@web/core/currency"; -const { Component, onPatched, onWillUpdateProps, useRef, useState } = owl; +const { Component, onPatched, onWillUpdateProps, onWillRender, toRaw, useRef, useState } = owl; /** A line of some TaxTotalsComponent, giving the values of a tax group. @@ -108,9 +108,7 @@ export class TaxTotalsComponent extends Component { setup() { this.totals = {}; this.formatData(this.props); - onWillUpdateProps((nextProps) => { - this.formatData(nextProps); - }); + onWillRender(() => this.formatData(this.props)); } get readonly() { @@ -143,7 +141,7 @@ export class TaxTotalsComponent extends Component { } formatData(props) { - let totals = JSON.parse(JSON.stringify(props.record.data[this.props.name])); + let totals = JSON.parse(JSON.stringify(toRaw(props.record.data[this.props.name]))); const currencyFmtOpts = { currencyId: props.record.data.currency_id && props.record.data.currency_id[0] }; let amount_untaxed = totals.amount_untaxed; diff --git a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js index a140b66fde3..51822ebb110 100644 --- a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js +++ b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js @@ -10,6 +10,7 @@ import { shallowEqual } from "@web/core/utils/arrays"; import { sprintf } from "@web/core/utils/strings"; import { _lt } from "@web/core/l10n/translation"; import { AutoComplete } from "@web/core/autocomplete/autocomplete"; +import { useRecordObserver } from "@web/model/relational_model/utils"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; import { TagsList } from "@web/core/tags_list/tags_list"; @@ -18,7 +19,7 @@ import { parseFloat as oParseFloat } from "@web/views/fields/parsers"; import { formatPercentage } from "@web/views/fields/formatters"; import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog"; -const { Component, useState, useRef, useExternalListener, onWillUpdateProps, onWillStart, onPatched } = owl; +const { Component, useState, useRef, useExternalListener, onWillStart, onPatched } = owl; const PLAN_APPLICABILITY = { mandatory: _lt("Mandatory"), @@ -50,7 +51,7 @@ export class AnalyticDistribution extends Component { this.currentValue = this.props.record.data[this.props.name]; onWillStart(this.willStart); - onWillUpdateProps(this.willUpdate); + useRecordObserver(this.willUpdateRecord.bind(this)); onPatched(this.patched); useExternalListener(window, "click", this.onWindowClick, true); @@ -83,43 +84,44 @@ export class AnalyticDistribution extends Component { // Lifecycle async willStart() { if (this.editingRecord) { - await this.fetchAllPlans(this.props); + await this.fetchAllPlans(); } - await this.formatData(this.props); + await this.formatData(); } - async willUpdate(nextProps) { + async willUpdateRecord(record) { // Unless force_applicability, Plans need to be retrieved again as the product or account might have changed // and thus different applicabilities apply // or a model applies that contains unavailable plans // This should only execute when these fields have changed, therefore we use the `_field` props. const valueChanged = JSON.stringify(this.currentValue) !== - JSON.stringify(nextProps.record.data[nextProps.name]); - const currentAccount = this.props.account_field && this.props.record.data[this.props.account_field] || false; - const currentProduct = this.props.product_field && this.props.record.data[this.props.product_field] || false; + JSON.stringify(record.data[this.props.name]); + const currentAccount = this.props.account_field && record.data[this.props.account_field] || false; + const currentProduct = this.props.product_field && record.data[this.props.product_field] || false; const accountChanged = !shallowEqual(this.lastAccount, currentAccount); const productChanged = !shallowEqual(this.lastProduct, currentProduct); if (valueChanged || accountChanged || productChanged) { if (!this.props.force_applicability) { - await this.fetchAllPlans(nextProps); + await this.fetchAllPlans(); } this.lastAccount = accountChanged && currentAccount || this.lastAccount; this.lastProduct = productChanged && currentProduct || this.lastProduct; - await this.formatData(nextProps); + await this.formatData(); } - this.currentValue = nextProps.record.data[nextProps.name]; + this.currentValue = record.data[this.props.name]; } patched() { this.focusToSelector(); } - async formatData(nextProps) { - const data = nextProps.record.data[nextProps.name]; + async formatData() { + const { record, name } = this.props; + const data = record.data[name]; const analytic_account_ids = Object.keys(data).map((id) => parseInt(id)); const records = analytic_account_ids.length ? await this.fetchAnalyticAccounts([["id", "in", analytic_account_ids]]) : []; - let widgetData = Object.assign({}, ...this.allPlans.map((plan) => ({[plan.id]: {...plan, distribution: []}}))); + const widgetData = Object.assign({}, ...this.allPlans.map((plan) => ({ [plan.id]: { ...plan, distribution: [] } }))); records.map((record) => { if (!widgetData[record.root_plan_id[0]]) { // plans might not have been retrieved @@ -144,36 +146,37 @@ export class AnalyticDistribution extends Component { } // ORM - fetchPlansArgs(nextProps) { + fetchPlansArgs() { + const { record, name } = this.props; let args = {}; if (this.props.business_domain_compute) { - args['business_domain'] = evaluateExpr(this.props.business_domain_compute, this.props.record.evalContext); + args['business_domain'] = evaluateExpr(this.props.business_domain_compute, record.evalContext); } if (this.props.business_domain) { args['business_domain'] = this.props.business_domain; } - if (this.props.product_field && this.props.record.data[this.props.product_field]) { - args['product'] = this.props.record.data[this.props.product_field][0]; + if (this.props.product_field && record.data[this.props.product_field]) { + args['product'] = record.data[this.props.product_field][0]; } - if (this.props.account_field && this.props.record.data[this.props.account_field]) { - args['account'] = this.props.record.data[this.props.account_field][0]; + if (this.props.account_field && record.data[this.props.account_field]) { + args['account'] = record.data[this.props.account_field][0]; } if (this.props.force_applicability) { args['applicability'] = this.props.force_applicability; } - const existing_account_ids = Object.keys(nextProps.record.data[nextProps.name]).map((i) => parseInt(i)); + const existing_account_ids = Object.keys(record.data[name]).map((i) => parseInt(i)); if (existing_account_ids.length) { args['existing_account_ids'] = existing_account_ids; } - if (this.props.record.data.company_id) { - args['company_id'] = this.props.record.data.company_id[0]; + if (record.data.company_id) { + args['company_id'] = record.data.company_id[0]; } return args; } - async fetchAllPlans(nextProps) { + async fetchAllPlans() { // TODO: Optimize to execute once for all records when `force_applicability` is set - const argsPlan = this.fetchPlansArgs(nextProps); + const argsPlan = this.fetchPlansArgs(); this.allPlans = await this.orm.call("account.analytic.plan", "get_relevant_plans", [], argsPlan); } @@ -530,8 +533,8 @@ export class AnalyticDistribution extends Component { async openAnalyticEditor() { if (!this.allPlans.length) { - await this.fetchAllPlans(this.props); - await this.formatData(this.props); + await this.fetchAllPlans(); + await this.formatData(); } this.autoFill(); const incompletePlan = this.firstIncompletePlanId; diff --git a/addons/analytic/static/tests/analytic_distribution_tests.js b/addons/analytic/static/tests/analytic_distribution_tests.js index 54ac36a70d2..46f7ad38b21 100644 --- a/addons/analytic/static/tests/analytic_distribution_tests.js +++ b/addons/analytic/static/tests/analytic_distribution_tests.js @@ -74,6 +74,7 @@ QUnit.module("Analytic", (hooks) => { amount: { string: "Amount", type: "float" }, analytic_distribution: { string: "Analytic", type: "json" }, move_id: { string: "Account Move", type: "many2one", relation: "move" }, + analytic_precision: { string: "Analytic Precision", type: "integer" }, }, records: [ { id: 1, label: "Developer Time", amount: 100.00, analytic_distribution: {"1": 30.3, "3": 69.7}}, @@ -140,7 +141,7 @@ QUnit.module("Analytic", (hooks) => { `, - mockRPC(route, { kwargs, method, model }) { + mockRPC(route, { method, model }) { if (method === "get_relevant_plans" && model === "account.analytic.plan") { return Promise.resolve( serverData.models['plan'].records.filter((r) => !r.parent_id && r.applicability !== "unavailable") @@ -333,6 +334,7 @@ QUnit.module("Analytic", (hooks) => { // apply the changes to both move lines triggerHotkey("Escape"); await nextTick(); + await nextTick(); await click(target.querySelector(".modal-dialog .btn-primary")); assert.containsN(target, ".badge", 10, "should contain 2 rows of 5 tags each"); diff --git a/addons/auth_totp/wizard/auth_totp_wizard_views.xml b/addons/auth_totp/wizard/auth_totp_wizard_views.xml index 76b967acf21..2c11bb26bdc 100644 --- a/addons/auth_totp/wizard/auth_totp_wizard_views.xml +++ b/addons/auth_totp/wizard/auth_totp_wizard_views.xml @@ -40,7 +40,7 @@
- +