From 350ad39d51a39719ede4dc9958aecb362a2d71a6 Mon Sep 17 00:00:00 2001 From: "Simon Genin (ges)" Date: Fri, 1 Jul 2022 12:43:25 +0000 Subject: [PATCH] [REF] account: convert fields to owl This commit converts the following fields to owl: - account_resequence_widget - grouped_view_widget - account-tax-totals-field Part-of: odoo/odoo#78221 Co-authored-by: luvi --- addons/account/__manifest__.py | 3 + .../static/src/js/account_resequence_field.js | 37 ++-- .../static/src/js/grouped_view_widget.js | 46 +++-- .../static/src/js/legacy_tax_totals.js | 154 +++++++++++++++++ addons/account/static/src/js/tax_totals.js | 159 ++++++++++-------- .../static/src/scss/account_resequence.scss | 3 + .../static/src/scss/group_view_widget.scss | 3 + .../static/src/xml/account_resequence.xml | 24 +-- .../static/src/xml/grouped_view_widget.xml | 4 +- .../static/src/xml/legacy_tax_totals.xml | 84 +++++++++ addons/account/static/src/xml/tax_totals.xml | 23 +-- 11 files changed, 392 insertions(+), 148 deletions(-) create mode 100644 addons/account/static/src/js/legacy_tax_totals.js create mode 100644 addons/account/static/src/scss/account_resequence.scss create mode 100644 addons/account/static/src/scss/group_view_widget.scss create mode 100644 addons/account/static/src/xml/legacy_tax_totals.xml diff --git a/addons/account/__manifest__.py b/addons/account/__manifest__.py index 5070b45ca55..e33a0eb7846 100644 --- a/addons/account/__manifest__.py +++ b/addons/account/__manifest__.py @@ -93,15 +93,18 @@ You could use this simplified accounting in case you work with an (external) acc 'account/static/src/scss/account_reconciliation.scss', 'account/static/src/scss/account_journal_dashboard.scss', 'account/static/src/scss/account_dashboard.scss', + 'account/static/src/scss/account_resequence.scss', 'account/static/src/scss/account_searchpanel.scss', 'account/static/src/scss/section_and_note_backend.scss', 'account/static/src/scss/account_activity.scss', 'account/static/src/scss/bills_upload.scss', + 'account/static/src/scss/group_view_widget.scss', 'account/static/src/js/account_payment_field.js', 'account/static/src/js/account_resequence_field.js', 'account/static/src/js/grouped_view_widget.js', 'account/static/src/js/mail_activity.js', 'account/static/src/js/tax_totals.js', + 'account/static/src/js/legacy_tax_totals.js', 'account/static/src/js/section_and_note_fields_backend.js', 'account/static/src/js/tours/account.js', 'account/static/src/js/bills_upload.js', diff --git a/addons/account/static/src/js/account_resequence_field.js b/addons/account/static/src/js/account_resequence_field.js index cd447bc7df0..ecb9b3d173e 100644 --- a/addons/account/static/src/js/account_resequence_field.js +++ b/addons/account/static/src/js/account_resequence_field.js @@ -1,31 +1,24 @@ -odoo.define('account.ShowResequenceRenderer', function (require) { -"use strict"; +/** @odoo-module */ -const AbstractFieldOwl = require('web.AbstractFieldOwl'); -const field_registry = require('web.field_registry_owl'); +import { registry } from "@web/core/registry"; const { Component, onWillUpdateProps } = owl; -class ChangeLine extends Component { } -ChangeLine.template = 'account.ResequenceChangeLine'; -ChangeLine.props = ["changeLine", 'ordering']; +class ChangeLine extends Component {} +ChangeLine.template = "account.ResequenceChangeLine"; +ChangeLine.props = ["changeLine", "ordering"]; - -class ShowResequenceRenderer extends AbstractFieldOwl { +class ShowResequenceRenderer extends Component { setup() { - super.setup(); - this.data = this.value ? JSON.parse(this.value) : { - changeLines: [], - ordering: 'date', - }; - onWillUpdateProps(() => { - Object.assign(this.data, JSON.parse(this.value)); - }); + this.formatData(this.props); + onWillUpdateProps((nextProps) => this.formatData(nextProps)); + } + + formatData(props) { + this.data = props.value ? JSON.parse(props.value) : { changeLines: [], ordering: "date" }; } } -ShowResequenceRenderer.template = 'account.ResequenceRenderer'; -ShowResequenceRenderer.components = { ChangeLine } +ShowResequenceRenderer.template = "account.ResequenceRenderer"; +ShowResequenceRenderer.components = { ChangeLine }; -field_registry.add('account_resequence_widget', ShowResequenceRenderer); -return ShowResequenceRenderer; -}); +registry.category("fields").add("account_resequence_widget", ShowResequenceRenderer); diff --git a/addons/account/static/src/js/grouped_view_widget.js b/addons/account/static/src/js/grouped_view_widget.js index 457bd897c8e..f8b90ab2dd7 100644 --- a/addons/account/static/src/js/grouped_view_widget.js +++ b/addons/account/static/src/js/grouped_view_widget.js @@ -1,39 +1,31 @@ -odoo.define('account.ShowGroupedList', function (require) { -"use strict"; +/** @odoo-module */ -const AbstractFieldOwl = require('web.AbstractFieldOwl'); -const field_registry = require('web.field_registry_owl'); +import { registry } from "@web/core/registry"; const { Component, onWillUpdateProps } = owl; -class ListItem extends Component { } -ListItem.template = 'account.GroupedItemTemplate'; +class ListItem extends Component {} +ListItem.template = "account.GroupedItemTemplate"; ListItem.props = ["item_vals", "options"]; -class ListGroup extends Component { } -ListGroup.template = 'account.GroupedItemsTemplate'; -ListGroup.components = { ListItem } +class ListGroup extends Component {} +ListGroup.template = "account.GroupedItemsTemplate"; +ListGroup.components = { ListItem }; ListGroup.props = ["group_vals", "options"]; - -class ShowGroupedList extends AbstractFieldOwl { +class ShowGroupedList extends Component { setup() { - super.setup(); - this.data = this.value ? JSON.parse(this.value) : { - groups_vals: [], - options: { - discarded_number: '', - columns: [], - }, - }; - onWillUpdateProps(() => { - Object.assign(this.data, JSON.parse(this.value)); - }); + this.formatData(this.props); + onWillUpdateProps((nextProps) => this.formatData(nextProps)); + } + + formatData(props) { + this.data = props.value + ? JSON.parse(props.value) + : { groups_vals: [], options: { discarded_number: "", columns: [] } }; } } -ShowGroupedList.template = 'account.GroupedListTemplate'; -ShowGroupedList.components = { ListGroup } +ShowGroupedList.template = "account.GroupedListTemplate"; +ShowGroupedList.components = { ListGroup }; -field_registry.add('grouped_view_widget', ShowGroupedList); -return ShowGroupedList; -}); +registry.category("fields").add("grouped_view_widget", ShowGroupedList); diff --git a/addons/account/static/src/js/legacy_tax_totals.js b/addons/account/static/src/js/legacy_tax_totals.js new file mode 100644 index 00000000000..2bbcf155a9a --- /dev/null +++ b/addons/account/static/src/js/legacy_tax_totals.js @@ -0,0 +1,154 @@ +/** @odoo-module alias=account.tax_group_owl **/ +"use strict"; + +import session from 'web.session'; +import AbstractFieldOwl from 'web.AbstractFieldOwl'; +import fieldUtils from 'web.field_utils'; +import field_registry from 'web.field_registry_owl'; +import { LegacyComponent } from "@web/legacy/legacy_component"; + +const { onPatched, onWillUpdateProps, useRef, useState } = owl; + +/** + A line of some LegacyTaxTotalsComponent, giving the values of a tax group. +**/ +class LegacyTaxGroupComponent extends LegacyComponent { + setup() { + this.inputTax = useRef('taxValueInput'); + this.state = useState({value: 'readonly'}); + this.allowTaxEdition = this.props.mode === 'edit' ? this.props.allowTaxEdition : false; + onPatched(this.onPatched); + onWillUpdateProps(this.onWillUpdateProps); + } + + //-------------------------------------------------------------------------- + // Life cycle methods + //-------------------------------------------------------------------------- + + onWillUpdateProps(nextProps) { + this.setState('readonly'); // If props are edited, we set the state to readonly + } + + onPatched() { + if (this.state.value === 'edit') { + this.inputTax.el.focus(); // Focus the input + this.inputTax.el.value = this.props.taxGroup.tax_group_amount; + } + } + + //-------------------------------------------------------------------------- + // Main methods + //-------------------------------------------------------------------------- + + /** + * The purpose of this method is to change the state of the component. + * It can have one of the following three states: + * - readonly: display in read-only mode of the field, + * - edit: display with a html input field, + * - disable: display with a html input field that is disabled. + * + * If a value other than one of these 3 states is passed as a parameter, + * the component is set to readonly by default. + * + * @param {String} value + */ + setState(value) { + if (['readonly', 'edit', 'disable'].includes(value)) { + this.state.value = value; + } + else { + this.state.value = 'readonly'; + } + } + + /** + * This method handles the "_onChangeTaxValue" event. In this method, + * we get the new value for the tax group, we format it and we call + * the method to recalculate the tax lines. At the moment the method + * is called, we disable the html input field. + * + * In case the value has not changed or the tax group is equal to 0, + * the modification does not take place. + */ + _onChangeTaxValue() { + this.setState('disable'); // Disable the input + let newValue = this.inputTax.el.value; // Get the new value + let currency = session.get_currency(this.props.record.data.currency_id.data.id); // The records using this widget must have a currency_id field. + try { + newValue = fieldUtils.parse.float(newValue); // Need a float for format the value + newValue = fieldUtils.format.float(newValue, null, {digits: currency.digits}); // Return a string rounded to currency precision + newValue = fieldUtils.parse.float(newValue); // Convert back to Float to compare with oldValue to know if value has changed + } catch (_err) { + $(this.inputTax.el).addClass('o_field_invalid'); + this.setState('edit'); + return; + } + // The newValue can't be equals to 0 + if (newValue === this.props.taxGroup.tax_group_amount || newValue === 0) { + this.setState('readonly'); + return; + } + this.props.taxGroup.tax_group_amount = newValue; + this.props.onChangeTaxGroup({ + oldValue: this.props.taxGroup.tax_group_amount, + newValue: newValue, + taxGroupId: this.props.taxGroup.tax_group_id + }); + } +} +LegacyTaxGroupComponent.props = ['taxGroup', 'allowTaxEdition', 'record', 'onChangeTaxGroup', 'mode']; +LegacyTaxGroupComponent.template = 'account.LegacyTaxGroupComponent'; + +/** + Widget used to display tax totals by tax groups for invoices, PO and SO, + and possibly allowing editing them. + + Note that this widget requires the object it is used on to have a + currency_id field. +**/ +class LegacyTaxTotalsComponent extends AbstractFieldOwl { + setup() { + super.setup(); + this.totals = useState({value: this.value ? JSON.parse(this.value) : null}); + this.allowTaxEdition = this.nodeOptions['allowTaxEdition']; + onWillUpdateProps(this.onWillUpdateProps); + } + + onWillUpdateProps(nextProps) { + // We only reformat tax groups if there are changed + this.totals.value = JSON.parse(nextProps.record.data[this.props.fieldName]); + } + + _onKeydown(ev) { + switch (ev.which) { + // Trigger only if the user clicks on ENTER or on TAB. + case $.ui.keyCode.ENTER: + case $.ui.keyCode.TAB: + // trigger blur to prevent the code being executed twice + $(ev.target).blur(); + } + } + + /** + * This method is the main function of the tax group widget. + * It is called by an event trigger (from the LegacyTaxGroupComponent) and receives + * a particular payload. + * + * It is responsible for calculating taxes based on tax groups and triggering + * an event to notify the ORM of a change. + */ + _onChangeTaxValueByTaxGroup(ev) { + this.trigger('field-changed', { + dataPointID: this.record.id, + changes: { tax_totals_json: JSON.stringify(this.totals.value) } + }) + } +} + +LegacyTaxTotalsComponent.template = 'account.LegacyTaxTotalsField'; +LegacyTaxTotalsComponent.components = { LegacyTaxGroupComponent }; + + +field_registry.add('account-tax-totals-field', LegacyTaxTotalsComponent); + +export default LegacyTaxTotalsComponent diff --git a/addons/account/static/src/js/tax_totals.js b/addons/account/static/src/js/tax_totals.js index 402473622cc..797c57ea7ae 100644 --- a/addons/account/static/src/js/tax_totals.js +++ b/addons/account/static/src/js/tax_totals.js @@ -1,43 +1,34 @@ -/** @odoo-module alias=account.tax_group_owl **/ -"use strict"; +/** @odoo-module **/ -import session from 'web.session'; -import AbstractFieldOwl from 'web.AbstractFieldOwl'; -import fieldUtils from 'web.field_utils'; -import field_registry from 'web.field_registry_owl'; -import { LegacyComponent } from "@web/legacy/legacy_component"; +import { formatFloat } from "@web/views/fields/formatters"; +import { standardFieldProps } from "@web/views/fields/standard_field_props"; +import { registry } from "@web/core/registry"; +import { session } from "@web/session"; -const { onPatched, onWillUpdateProps, useRef, useState } = owl; +const { Component, onPatched, onWillUpdateProps, useRef, useState } = owl; /** A line of some TaxTotalsComponent, giving the values of a tax group. **/ -class TaxGroupComponent extends LegacyComponent { +class TaxGroupComponent extends Component { setup() { - this.inputTax = useRef('taxValueInput'); - this.state = useState({value: 'readonly'}); - this.allowTaxEdition = this.props.mode === 'edit' ? this.props.allowTaxEdition : false; - onPatched(this.onPatched); - onWillUpdateProps(this.onWillUpdateProps); + this.inputTax = useRef("taxValueInput"); + this.state = useState({value: "readonly"}); + onPatched(() => { + if (this.state.value === "edit") { + const { taxGroup, currency } = this.props; + const newVal = formatFloat(taxGroup.tax_group_amount, { digits: currency.digits }); + this.inputTax.el.value = newVal; + this.inputTax.el.focus(); // Focus the input + } + }); + onWillUpdateProps(() => { + this.setState("readonly"); + }); } - //-------------------------------------------------------------------------- - // Life cycle methods - //-------------------------------------------------------------------------- - - onWillUpdateProps(nextProps) { - this.setState('readonly'); // If props are edited, we set the state to readonly - } - - onPatched() { - if (this.state.value === 'edit') { - let newValue = this.props.taxGroup.tax_group_amount; - let currency = session.get_currency(this.props.record.data.currency_id.data.id); - - newValue = fieldUtils.format.float(newValue, null, {digits: currency.digits}); - this.inputTax.el.focus(); // Focus the input - this.inputTax.el.value = newValue; - } + get allowTaxEdition() { + return !this.props.isReadonly && this.props.allowTaxEdition; } //-------------------------------------------------------------------------- @@ -57,11 +48,11 @@ class TaxGroupComponent extends LegacyComponent { * @param {String} value */ setState(value) { - if (['readonly', 'edit', 'disable'].includes(value)) { + if (["readonly", "edit", "disable"].includes(value)) { this.state.value = value; } else { - this.state.value = 'readonly'; + this.state.value = "readonly"; } } @@ -75,33 +66,40 @@ class TaxGroupComponent extends LegacyComponent { * the modification does not take place. */ _onChangeTaxValue() { - this.setState('disable'); // Disable the input - let newValue = this.inputTax.el.value; // Get the new value - let currency = session.get_currency(this.props.record.data.currency_id.data.id); // The records using this widget must have a currency_id field. + this.setState("disable"); // Disable the input + let newValue = Number(this.inputTax.el.value); // Get the new value try { - newValue = fieldUtils.parse.float(newValue); // Need a float for format the value - newValue = fieldUtils.format.float(newValue, null, {digits: currency.digits}); // Return a string rounded to currency precision - newValue = fieldUtils.parse.float(newValue); // Convert back to Float to compare with oldValue to know if value has changed + newValue = formatFloat(newValue, { digits: this.props.currency.digits }); // Return a string rounded to currency precision } catch (_err) { - $(this.inputTax.el).addClass('o_field_invalid'); - this.setState('edit'); + this.props.invalidate(); + this.setState("edit"); return; } - // The newValue can't be equals to 0 + // The newValue can"t be equals to 0 if (newValue === this.props.taxGroup.tax_group_amount || newValue === 0) { - this.setState('readonly'); + this.setState("readonly"); return; } + const oldValue = this.props.taxGroup.tax_group_amount; this.props.taxGroup.tax_group_amount = newValue; + this.props.taxGroup.formatted_tax_group_amount = this.props.taxGroup.formatted_tax_group_amount.replace(oldValue.toString(), newValue.toString()); this.props.onChangeTaxGroup({ - oldValue: this.props.taxGroup.tax_group_amount, - newValue: newValue, + oldValue, + newValue: Number(newValue), taxGroupId: this.props.taxGroup.tax_group_id }); } } -TaxGroupComponent.props = ['taxGroup', 'allowTaxEdition', 'record', 'onChangeTaxGroup', 'mode']; -TaxGroupComponent.template = 'account.TaxGroupComponent'; + +TaxGroupComponent.props = { + currency: {}, + taxGroup: { optional: true }, + allowTaxEdition: { optional: true }, + onChangeTaxGroup: { optional: true }, + isReadonly: Boolean, + invalidate: Function, +}; +TaxGroupComponent.template = "account.TaxGroupComponent"; /** Widget used to display tax totals by tax groups for invoices, PO and SO, @@ -110,49 +108,62 @@ TaxGroupComponent.template = 'account.TaxGroupComponent'; Note that this widget requires the object it is used on to have a currency_id field. **/ -class TaxTotalsComponent extends AbstractFieldOwl { +export class TaxTotalsComponent extends Component { setup() { - super.setup(); - this.totals = useState({value: this.value ? JSON.parse(this.value) : null}); - this.allowTaxEdition = this.nodeOptions['allowTaxEdition']; - onWillUpdateProps(this.onWillUpdateProps); + this.totals = this.parseValue(); + onWillUpdateProps((nextProps) => { + // We only reformat tax groups if there are changed + this.totals = this.parseValue(); + }); } - onWillUpdateProps(nextProps) { - // We only reformat tax groups if there are changed - this.totals.value = JSON.parse(nextProps.record.data[this.props.fieldName]); + get allowTaxEdition() { + return this.totals && this.totals.allow_tax_edition; } - _onKeydown(ev) { - switch (ev.which) { - // Trigger only if the user clicks on ENTER or on TAB. - case $.ui.keyCode.ENTER: - case $.ui.keyCode.TAB: - // trigger blur to prevent the code being executed twice - $(ev.target).blur(); + get currency() { + const recordCurrency = this.props.record.data.currency_id; + let currencyId; + if (recordCurrency) { + currencyId = recordCurrency[0]; + } else { + currencyId = session.company_currency_id; } + return session.currencies[currencyId]; + } + + parseValue() { + if (this.props.record.data[this.props.name]) { + return JSON.parse(this.props.record.data[this.props.name]); + } + return null; + } + + invalidate() { + return this.props.record.setInvalidField(this.props.name); } /** * This method is the main function of the tax group widget. - * It is called by an event trigger (from the TaxGroupComponent) and receives - * a particular payload. + * It is called by the TaxGroupComponent and receives the + * newer tax value. * * It is responsible for calculating taxes based on tax groups and triggering * an event to notify the ORM of a change. */ - _onChangeTaxValueByTaxGroup(ev) { - this.trigger('field-changed', { - dataPointID: this.record.id, - changes: { tax_totals_json: JSON.stringify(this.totals.value) } - }) + _onChangeTaxValueByTaxGroup({ oldValue, newValue, taxGroupId }) { + if (oldValue === newValue) return; + const [oldTotal, newTotal] = [this.totals.amount_total, this.totals.amount_untaxed + newValue]; + this.totals.amount_total = newTotal; + this.totals.formatted_amount_total = this.totals.formatted_amount_total.replace(oldTotal.toString(), newTotal.toString()); + this.props.update(JSON.stringify(this.totals)); } } -TaxTotalsComponent.template = 'account.TaxTotalsField'; +TaxTotalsComponent.template = "account.TaxTotalsField"; TaxTotalsComponent.components = { TaxGroupComponent }; +TaxTotalsComponent.props = { + ...standardFieldProps, +} - -field_registry.add('account-tax-totals-field', TaxTotalsComponent); - -export default TaxTotalsComponent +registry.category("fields").add("account-tax-totals-field", TaxTotalsComponent); diff --git a/addons/account/static/src/scss/account_resequence.scss b/addons/account/static/src/scss/account_resequence.scss new file mode 100644 index 00000000000..95dbfe45f22 --- /dev/null +++ b/addons/account/static/src/scss/account_resequence.scss @@ -0,0 +1,3 @@ +.o_field_account_resequence_widget { + display: block; +} diff --git a/addons/account/static/src/scss/group_view_widget.scss b/addons/account/static/src/scss/group_view_widget.scss new file mode 100644 index 00000000000..49eb4dec469 --- /dev/null +++ b/addons/account/static/src/scss/group_view_widget.scss @@ -0,0 +1,3 @@ +.o_field_grouped_view_widget { + display: block; +} diff --git a/addons/account/static/src/xml/account_resequence.xml b/addons/account/static/src/xml/account_resequence.xml index 172a0e94461..7db2dbae422 100644 --- a/addons/account/static/src/xml/account_resequence.xml +++ b/addons/account/static/src/xml/account_resequence.xml @@ -1,18 +1,18 @@ -
- - - - - - - - - -
DateBeforeAfter
-
+ + + + + + + + + + +
DateBeforeAfter
+
diff --git a/addons/account/static/src/xml/grouped_view_widget.xml b/addons/account/static/src/xml/grouped_view_widget.xml index b187277418e..8c6620d720b 100644 --- a/addons/account/static/src/xml/grouped_view_widget.xml +++ b/addons/account/static/src/xml/grouped_view_widget.xml @@ -1,6 +1,6 @@ -
+ @@ -14,7 +14,7 @@ are not shown in the preview - + diff --git a/addons/account/static/src/xml/legacy_tax_totals.xml b/addons/account/static/src/xml/legacy_tax_totals.xml new file mode 100644 index 00000000000..0df8ea3771e --- /dev/null +++ b/addons/account/static/src/xml/legacy_tax_totals.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +
+
+
diff --git a/addons/account/static/src/xml/tax_totals.xml b/addons/account/static/src/xml/tax_totals.xml index b46d00425b5..e1b00f94e86 100644 --- a/addons/account/static/src/xml/tax_totals.xml +++ b/addons/account/static/src/xml/tax_totals.xml @@ -17,7 +17,7 @@ class="o_field_float o_field_number o_input" t-att-disabled="state.value === 'disable'" - t-on-blur.prevent="_onChangeTaxValue"/> + t-on-change.prevent="_onChangeTaxValue"/>
@@ -38,10 +38,10 @@ -
- + +
- + - + @@ -72,13 +73,13 @@
-
+