diff --git a/mis_builder/__manifest__.py b/mis_builder/__manifest__.py index fb1d8dc44..d727bca72 100644 --- a/mis_builder/__manifest__.py +++ b/mis_builder/__manifest__.py @@ -29,6 +29,7 @@ ], "assets": { "web.assets_backend": [ + "mis_builder/static/src/components/search_model.esm.js", "mis_builder/static/src/components/mis_report_widget.esm.js", "mis_builder/static/src/components/mis_report_widget.xml", "mis_builder/static/src/components/mis_report_widget.css", diff --git a/mis_builder/static/src/components/mis_report_widget.esm.js b/mis_builder/static/src/components/mis_report_widget.esm.js index d67fbcc81..5b8db981f 100644 --- a/mis_builder/static/src/components/mis_report_widget.esm.js +++ b/mis_builder/static/src/components/mis_report_widget.esm.js @@ -4,10 +4,11 @@ import {Component, onWillStart, useState, useSubEnv} from "@odoo/owl"; import {useBus, useService} from "@web/core/utils/hooks"; import {DatePicker} from "@web/core/datepicker/datepicker"; import {FilterMenu} from "@web/search/filter_menu/filter_menu"; +import {MisReportSearchModel} from "@mis_builder/components/search_model.esm"; import {SearchBar} from "@web/search/search_bar/search_bar"; -import {SearchModel} from "@web/search/search_model"; import {parseDate} from "@web/core/l10n/dates"; import {registry} from "@web/core/registry"; +import {standardFieldProps} from "@web/views/fields/standard_field_props"; export class MisReportWidget extends Component { setup() { @@ -21,7 +22,7 @@ export class MisReportWidget extends Component { mis_report_data: {header: [], body: []}, pivot_date: null, }); - this.searchModel = new SearchModel(this.env, { + this.searchModel = new MisReportSearchModel(this.env, { user: this.user, orm: this.orm, view: this.view, @@ -61,6 +62,7 @@ export class MisReportWidget extends Component { await this.searchModel.load({ resModel: this.source_aml_model_name, searchViewId: this.widget_search_view_id, + analyticAccountId: this.analyticAccountId, }); } @@ -68,6 +70,10 @@ export class MisReportWidget extends Component { this.refresh(); } + get misAnalyticDomainContextKey() { + return "mis_analytic_domain"; + } + get showSearchBar() { return ( this.source_aml_model_name && @@ -104,12 +110,11 @@ export class MisReportWidget extends Component { } get context() { - var ctx = super.context; - if (this.showSearchBar) { - ctx = { - ...ctx, - mis_analytic_domain: this.searchModel.searchDomain, - }; + let ctx = this.props.record.context; + if (this.showSearchBar && this.searchModel.searchDomain) { + ctx[this.misAnalyticDomainContextKey] = this.searchModel.searchDomain; + } else { + this._setMisAnalyticDomainContextKey(ctx); } if (this.showPivotDate && this.state.pivot_date) { ctx = { @@ -140,6 +145,49 @@ export class MisReportWidget extends Component { ); } + get analyticAccountId() { + let analyticAccountId = this._getAnalyticAccountIdFromData(); + if ( + !analyticAccountId && + this.props.record.context && + this.props.record.context.mis_analytic_domain + ) { + analyticAccountId = this.props.record.context.mis_analytic_domain[0][2]; + } + return analyticAccountId; + } + + _getAnalyticAccountIdFromData() { + if ( + !this.props.analyticAccountIdField || + !(this.props.analyticAccountIdField in this.props.record.fields) + ) { + return false; + } + const analyticAccountIdFieldType = + this.props.record.fields[this.props.analyticAccountIdField].type; + switch (analyticAccountIdFieldType) { + case "many2one": + return this.props.record.data[this.props.analyticAccountIdField][0]; + case "integer": + return this.props.record.data[this.props.analyticAccountIdField]; + default: + throw new Error( + ```Unsupported field type for analytic_account_id: ${analyticAccountIdFieldType}``` + ); + } + } + + _setMisAnalyticDomainContextKey(context) { + const analyticAccountId = this._getAnalyticAccountIdFromData(); + if (!analyticAccountId) { + return; + } + context[this.misAnalyticDomainContextKey] = [ + ["analytic_account_id", "=", analyticAccountId], + ]; + } + async printPdf() { const action = await this.orm.call( "mis.report.instance", @@ -178,5 +226,15 @@ export class MisReportWidget extends Component { MisReportWidget.components = {FilterMenu, SearchBar, DatePicker}; MisReportWidget.template = "mis_builder.MisReportWidget"; +MisReportWidget.props = { + ...standardFieldProps, + analyticAccountIdField: { + type: String, + optional: true, + }, +}; +MisReportWidget.extractProps = ({attrs}) => ({ + analyticAccountIdField: attrs.analytic_account_id_field, +}); registry.category("fields").add("mis_report_widget", MisReportWidget); diff --git a/mis_builder/static/src/components/search_model.esm.js b/mis_builder/static/src/components/search_model.esm.js new file mode 100644 index 000000000..61bfcd835 --- /dev/null +++ b/mis_builder/static/src/components/search_model.esm.js @@ -0,0 +1,105 @@ +/** @odoo-module **/ + +import {SearchModel} from "@web/search/search_model"; +import {useService} from "@web/core/utils/hooks"; + +export class MisReportSearchModel extends SearchModel { + /** + * @override + */ + setup() { + this.notificationService = useService("notification"); + super.setup(...arguments); + } + + /** + * @override + */ + deactivateGroup(groupId) { + // Prevent removing the analytic account filter and let the user know. + let reactivateAnalyticAccountFilter = false; + if (this.analyticAccountSearchItem.groupId === groupId) { + if ( + this.query.filter( + (query) => query.searchItemId === this.analyticAccountSearchItem.id + ).length === 1 + ) { + this.notificationService.add( + this.env._t("The analytic account filter cannot be removed"), + {type: "info"} + ); + return; + } + // As there are more than one filter on the analytic account, let + // super remove them and add it back after. + reactivateAnalyticAccountFilter = true; + } + super.deactivateGroup(groupId); + if (reactivateAnalyticAccountFilter) { + this._addAnalyticAccountFilter(); + } + } + + /** + * @override + */ + async load(config) { + // Store analytic account id in the SearchModel for reuse in other functions. + this.analyticAccountId = config.analyticAccountId; + const analyticAccountNamePromise = this._loadAnalyticAccountName(); + await Promise.all([analyticAccountNamePromise, super.load(...arguments)]); + this._determineAnalyticAccountSearchItem(); + this._addAnalyticAccountFilter(); + } + + /** + * Add the filter regarding the analytic account in the search model. + * @private + */ + _addAnalyticAccountFilter() { + if (!this.analyticAccountSearchItem || !this.analyticAccountName) { + return; + } + this.addAutoCompletionValues(this.analyticAccountSearchItem.id, { + label: this.analyticAccountName, + operator: "=", + value: this.analyticAccountId, + }); + } + + /** + * Find the searchItem that correspond to the analytic account field and store it + * under analyticAccountSearchItem. + * @private + */ + _determineAnalyticAccountSearchItem() { + // Store analytic account searchItem in the SearchModel for reuse in other functions. + for (const searchItem of Object.values(this.searchItems)) { + if ( + searchItem.type === "field" && + searchItem.fieldName === "analytic_account_id" + ) { + this.analyticAccountSearchItem = searchItem; + break; + } + } + } + + /** + * Load the analytic account name and store it under analyticAccountName. + * @returns {Promise} + * @private + */ + async _loadAnalyticAccountName() { + if (!this.analyticAccountId) { + return; + } + const readResult = await this.orm.read( + "account.analytic.account", + [this.analyticAccountId], + ["display_name"] + ); + const analyticAccount = readResult[0]; + this.analyticAccountName = analyticAccount.display_name; + } +}