diff --git a/web_m2x_options/static/src/components/form.esm.js b/web_m2x_options/static/src/components/form.esm.js index ecb37d270021..eb932f6e58ff 100644 --- a/web_m2x_options/static/src/components/form.esm.js +++ b/web_m2x_options/static/src/components/form.esm.js @@ -3,19 +3,24 @@ import { Many2ManyTagsField, Many2ManyTagsFieldColorEditable, + many2ManyTagsField, } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import {Many2OneField, many2OneField} from "@web/views/fields/many2one/many2one_field"; +import {X2ManyField, x2ManyField} from "@web/views/fields/x2many/x2many_field"; import {Dialog} from "@web/core/dialog/dialog"; import {FormController} from "@web/views/form/form_controller"; import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog"; import {Many2OneAvatarField} from "@web/views/fields/many2one_avatar/many2one_avatar_field"; import {Many2OneBarcodeField} from "@web/views/fields/many2one_barcode/many2one_barcode_field"; -import {Many2OneField} from "@web/views/fields/many2one/many2one_field"; +import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; import {ReferenceField} from "@web/views/fields/reference/reference_field"; -import {X2ManyField} from "@web/views/fields/x2many/x2many_field"; +import {_t} from "@web/core/l10n/translation"; +import {evaluateBooleanExpr} from "@web/core/py_js/py"; import {isX2Many} from "@web/views/utils"; import {is_option_set} from "@web_m2x_options/components/relational_utils.esm"; import {patch} from "@web/core/utils/patch"; +import {session} from "@web/session"; import {sprintf} from "@web/core/utils/strings"; import {useService} from "@web/core/utils/hooks"; @@ -24,16 +29,16 @@ const {Component} = owl; /** * Patch Many2ManyTagsField **/ -patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", { +patch(Many2ManyTagsField.prototype, { setup() { - this._super(...arguments); + super.setup(); this.actionService = useService("action"); }, /** * @override */ getTagProps(record) { - const props = this._super(...arguments); + const props = super.getTagProps(record); props.onClick = (ev) => this.onMany2ManyBadgeClick(ev, record); return props; }, @@ -88,37 +93,36 @@ Many2ManyTagsField.props = { nodeOptions: {type: Object, optional: true}, }; -const Many2ManyTagsFieldExtractProps = Many2ManyTagsField.extractProps; -Many2ManyTagsField.extractProps = ({attrs, field}) => { - const canOpen = Boolean(attrs.options.open); - const canWrite = attrs.can_write && Boolean(JSON.parse(attrs.can_write)); - return Object.assign(Many2ManyTagsFieldExtractProps({attrs, field}), { - open: canOpen, - canWrite: canWrite, - nodeOptions: attrs.options, - }); +const Many2ManyTagsFieldExtractProps = many2ManyTagsField.extractProps; +many2ManyTagsField.extractProps = ({attrs, options, string}, dynamicInfo) => { + const canOpen = Boolean(options.open); + const canWrite = attrs.can_write && evaluateBooleanExpr(attrs.can_write); + return Object.assign( + Many2ManyTagsFieldExtractProps({attrs, options, string}, dynamicInfo), + { + open: canOpen, + canWrite: canWrite, + nodeOptions: options, + } + ); }; /** * Many2ManyTagsFieldColorEditable **/ -patch( - Many2ManyTagsFieldColorEditable.prototype, - "web_m2x_options.Many2ManyTagsFieldColorEditable", - { - async onBadgeClick(event, record) { - if (this.props.canEditColor && !this.props.open) { - this._super(...arguments); - } - if (this.props.open) { - Many2ManyTagsField.prototype.onMany2ManyBadgeClick.bind(this)( - event, - record - ); - } - }, - } -); +patch(Many2ManyTagsFieldColorEditable.prototype, { + async onBadgeClick(event, record) { + if (this.props.canEditColor && !this.props.open) { + super.onBadgeClick(event, record); + } + if (this.props.open) { + Many2ManyTagsField.prototype.onMany2ManyBadgeClick.bind(this)( + event, + record + ); + } + }, +}); Many2ManyTagsFieldColorEditable.props = { ...Many2ManyTagsFieldColorEditable.props, @@ -134,7 +138,7 @@ Many2ManyTagsFieldColorEditable.props = { class CreateConfirmationDialog extends Component { get title() { - return sprintf(this.env._t("New: %s"), this.props.name); + return sprintf(_t("New: %s"), this.props.name); } async onCreate() { @@ -154,16 +158,17 @@ CreateConfirmationDialog.template = * Many2OneField **/ -patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", { +patch(Many2OneField.prototype, { setup() { - this._super(...arguments); - this.ir_options = Component.env.session.web_m2x_options; + super.setup(); + this.ir_options = session.web_m2x_options; }, + /** * @override */ get Many2XAutocompleteProps() { - const props = this._super(...arguments); + const props = super.Many2XAutocompleteProps; return { ...props, searchLimit: this.props.searchLimit, @@ -176,10 +181,10 @@ patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", { async openConfirmationDialog(request) { var m2o_dialog_opt = is_option_set(this.props.nodeOptions.m2o_dialog) || - (_.isUndefined(this.props.nodeOptions.m2o_dialog) && + (this.props.nodeOptions.m2o_dialog === undefined && is_option_set(this.ir_options["web_m2x_options.m2o_dialog"])) || - (_.isUndefined(this.props.nodeOptions.m2o_dialog) && - _.isUndefined(this.ir_options["web_m2x_options.m2o_dialog"])); + (this.props.nodeOptions.m2o_dialog === undefined && + this.ir_options["web_m2x_options.m2o_dialog"] === undefined); if (this.props.canCreate && this.state.isFloating && m2o_dialog_opt) { return new Promise((resolve, reject) => { this.addDialog(CreateConfirmationDialog, { @@ -212,13 +217,22 @@ patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", { }, }); -const Many2OneFieldExtractProps = Many2OneField.extractProps; -Many2OneField.extractProps = ({attrs, field}) => { - return Object.assign(Many2OneFieldExtractProps({attrs, field}), { - searchLimit: attrs.options.limit, - searchMore: attrs.options.search_more, - nodeOptions: attrs.options, - }); +const Many2OneFieldExtractProps = many2OneField.extractProps; +many2OneField.extractProps = ( + {attrs, context, decorations, options, string}, + dynamicInfo +) => { + return Object.assign( + Many2OneFieldExtractProps( + {attrs, context, decorations, options, string}, + dynamicInfo + ), + { + searchLimit: options.limit, + searchMore: options.search_more, + nodeOptions: options, + } + ); }; Many2OneField.props = { @@ -268,6 +282,20 @@ Many2OneAvatarField.props = { nodeOptions: {type: Object, optional: true}, }; +/** + * FIXME: find better way to extend props in Many2OneField + * Override Many2XAutocomplete + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + */ +Many2XAutocomplete.props = { + ...Many2XAutocomplete.props, + canCreate: {type: Boolean, optional: true}, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + /** * FIXME: find better way to extend props in Many2OneField * Override mailing_m2o_filter @@ -280,15 +308,12 @@ Many2OneAvatarField.props = { try { (async () => { // Make sure component mailing_m2o_filter in mass mailing module loaded - const installed_mass_mailing = await odoo.ready( + const installed_mass_mailing = await odoo.loader.modules.get( "@mass_mailing/js/mailing_m2o_filter" ); if (installed_mass_mailing) { - const {FieldMany2OneMailingFilter} = await odoo.runtimeImport( - "@mass_mailing/js/mailing_m2o_filter" - ); - FieldMany2OneMailingFilter.props = { - ...FieldMany2OneMailingFilter.props, + installed_mass_mailing.FieldMany2OneMailingFilter.props = { + ...installed_mass_mailing.FieldMany2OneMailingFilter.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; @@ -300,10 +325,132 @@ try { ); } +/** + * FIXME: find better way to extend props in Many2OneField + * Override partner_autocomplete_many2one + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + * This component is in module partner_autocomplete as optional module, + * So need to import dynamic way + */ +try { + (async () => { + // Make sure component partner_autocomplete_many2one in partner_autocomplete module loaded + const installed_partner_autocomplete = await odoo.loader.modules.get( + "@partner_autocomplete/js/partner_autocomplete_many2one" + ); + if (installed_partner_autocomplete) { + installed_partner_autocomplete.PartnerMany2XAutocomplete.props = { + ...installed_partner_autocomplete.PartnerMany2XAutocomplete.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, + }; + const PartnerAutoCompleteMany2oneExtractProps = + installed_partner_autocomplete.partnerAutoCompleteMany2one.extractProps; + installed_partner_autocomplete.partnerAutoCompleteMany2one.extractProps = ( + {attrs, context, decorations, options, string}, + dynamicInfo + ) => { + return Object.assign( + PartnerAutoCompleteMany2oneExtractProps( + {attrs, context, decorations, options, string}, + dynamicInfo + ), + { + searchLimit: options.limit, + searchMore: options.search_more, + nodeOptions: options, + } + ); + }; + } + })(); +} catch { + console.log( + "Ignore overriding props of component partner_autocomplete_many2one since the module is not installed" + ); +} + +/** + * FIXME: find better way to extend props in Many2OneField + * Override sale_product_field + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + * This component is in module sale as optional module, + * So need to import dynamic way + */ +try { + (async () => { + // Make sure component sale_product_field in sale module loaded + const installed_sale = await odoo.loader.modules.get( + "@sale/js/sale_product_field" + ); + if (installed_sale) { + installed_sale.SaleOrderLineProductField.props = { + ...installed_sale.SaleOrderLineProductField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, + }; + } + })(); +} catch { + console.log( + "Ignore overriding props of component sale_product_field since the module is not installed" + ); +} + +/** + * FIXME: find better way to extend props in Many2OneField + * Override many2one_avatar_user_field + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + * This component is in module sale as optional module, + * So need to import dynamic way + */ +try { + (async () => { + // Make sure component many2one_avatar_user_field in mail module loaded + const installed_mail = await odoo.loader.modules.get( + "@mail/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field" + ); + if (installed_mail) { + installed_mail.Many2OneAvatarUserField.props = { + ...installed_mail.Many2OneAvatarUserField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, + }; + const Many2OneAvatarUserFieldExtractProps = + installed_mail.many2OneAvatarUserField.extractProps; + installed_mail.many2OneAvatarUserField.extractProps = ( + fieldInfo, + dynamicInfo + ) => { + const canWrite = + fieldInfo.attrs.can_write && + evaluateBooleanExpr(fieldInfo.attrs.can_write); + return Object.assign( + Many2OneAvatarUserFieldExtractProps(fieldInfo, dynamicInfo), + { + canWrite: canWrite, + nodeOptions: fieldInfo.options, + } + ); + }; + } + })(); +} catch { + console.log( + "Ignore overriding props of component many2one_avatar_user_field since the module is not installed" + ); +} + /** * X2ManyField **/ -patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", { +patch(X2ManyField.prototype, { /** * @override */ @@ -319,16 +466,25 @@ patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", { ); return self.env.model.actionService.doAction(action); } - return this._super.apply(this, arguments); + return super.openRecord(record); }, }); -const X2ManyFieldExtractProps = X2ManyField.extractProps; -X2ManyField.extractProps = ({attrs}) => { - const canOpen = Boolean(attrs.options.open); - return Object.assign(X2ManyFieldExtractProps({attrs}), { - open: canOpen, - }); +const X2ManyFieldExtractProps = x2ManyField.extractProps; +x2ManyField.extractProps = ( + {attrs, relatedFields, viewMode, views, widget, options, string}, + dynamicInfo +) => { + const canOpen = Boolean(options.open); + return Object.assign( + X2ManyFieldExtractProps( + {attrs, relatedFields, viewMode, views, widget, options, string}, + dynamicInfo + ), + { + open: canOpen, + } + ); }; X2ManyField.props = { @@ -339,13 +495,13 @@ X2ManyField.props = { /** * FormController **/ -patch(FormController.prototype, "web_m2x_options.FormController", { +patch(FormController.prototype, { /** * @override */ setup() { var self = this; - this._super(...arguments); + super.setup(); /** Due to problem of 2 onWillStart in native web core * (see: https://github.com/odoo/odoo/blob/16.0/addons/web/static/src/views/model.js#L142) @@ -369,7 +525,7 @@ patch(FormController.prototype, "web_m2x_options.FormController", { isSmall = this.user; var limit = ir_options["web_m2x_options.field_limit_entries"]; - if (!_.isUndefined(limit)) { + if (!(limit === undefined)) { limit = parseInt(limit, 10); } diff --git a/web_m2x_options/static/src/components/relational_utils.esm.js b/web_m2x_options/static/src/components/relational_utils.esm.js index 1fbe39eae08a..1e6c03cbe0c4 100644 --- a/web_m2x_options/static/src/components/relational_utils.esm.js +++ b/web_m2x_options/static/src/components/relational_utils.esm.js @@ -1,21 +1,22 @@ /** @odoo-module **/ import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {_t} from "@web/core/l10n/translation"; import {patch} from "@web/core/utils/patch"; +import {session} from "@web/session"; import {sprintf} from "@web/core/utils/strings"; -const {Component} = owl; export function is_option_set(option) { - if (_.isUndefined(option)) return false; + if (option === undefined) return false; if (typeof option === "string") return option === "true" || option === "True"; if (typeof option === "boolean") return option; return false; } -patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { +patch(Many2XAutocomplete.prototype, { setup() { - this._super(...arguments); - this.ir_options = Component.env.session.web_m2x_options; + super.setup(); + this.ir_options = session.web_m2x_options; }, async loadOptionsSource(request) { @@ -24,7 +25,7 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { } // Add options limit used to change number of selections record // returned. - if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) { + if (!(this.ir_options["web_m2x_options.limit"] === undefined)) { this.props.searchLimit = parseInt( this.ir_options["web_m2x_options.limit"], 10 @@ -95,12 +96,12 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { var create_enabled = this.props.quickCreate && !this.props.nodeOptions.no_create; - var raw_result = _.map(records, function (x) { + var raw_result = Object.values(records).map((x) => { return x[1]; }); var quick_create = is_option_set(this.props.nodeOptions.create), - quick_create_undef = _.isUndefined(this.props.nodeOptions.create), - m2x_create_undef = _.isUndefined(this.ir_options["web_m2x_options.create"]), + quick_create_undef = this.props.nodeOptions.create === undefined, + m2x_create_undef = this.ir_options["web_m2x_options.create"] === undefined, m2x_create = is_option_set(this.ir_options["web_m2x_options.create"]); var show_create = (!this.props.nodeOptions && (m2x_create_undef || m2x_create)) || @@ -111,11 +112,11 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { create_enabled && !this.props.nodeOptions.no_quick_create && request.length > 0 && - !_.contains(raw_result, request) && + !raw_result.includes(request) && show_create ) { options.push({ - label: sprintf(this.env._t(`Create "%s"`), request), + label: sprintf(_t(`Create "%s"`), request), classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create", action: async (params) => { try { @@ -136,9 +137,9 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { // 4- if set globally, apply its value // 5- if not set globally either, check if returned values are more than node's limit var search_more = false; - if (!_.isUndefined(this.props.nodeOptions.search_more)) { + if (!(this.props.nodeOptions.search_more === undefined)) { search_more = is_option_set(this.props.nodeOptions.search_more); - } else if (!_.isUndefined(this.ir_options["web_m2x_options.search_more"])) { + } else if (!(this.ir_options["web_m2x_options.search_more"] === undefined)) { search_more = is_option_set(this.ir_options["web_m2x_options.search_more"]); } else { search_more = @@ -146,7 +147,7 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { } if (search_more) { options.push({ - label: this.env._t("Search More..."), + label: _t("Search More..."), action: this.onSearchMore.bind(this, request), classList: "o_m2o_dropdown_option o_m2o_dropdown_option_search_more", }); @@ -163,22 +164,17 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { (this.props.quickCreate || canCreateEdit) ) { options.push({ - label: this.env._t("Start typing..."), + label: _t("Start typing..."), classList: "o_m2o_start_typing", unselectable: true, }); } // Create and edit ... - var create_edit = - is_option_set(this.props.nodeOptions.create) || - is_option_set(this.props.nodeOptions.create_edit), - create_edit_undef = - _.isUndefined(this.props.nodeOptions.create) && - _.isUndefined(this.props.nodeOptions.create_edit), - m2x_create_edit_undef = _.isUndefined( - this.ir_options["web_m2x_options.create_edit"] - ), + var create_edit = is_option_set(this.props.nodeOptions.create_edit), + create_edit_undef = this.props.nodeOptions.create_edit === undefined, + m2x_create_edit_undef = + this.ir_options["web_m2x_options.create_edit"] === undefined, m2x_create_edit = is_option_set( this.ir_options["web_m2x_options.create_edit"] ); @@ -187,16 +183,10 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { (this.props.nodeOptions && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); - if ( - create_enabled && - !this.props.nodeOptions.no_create_edit && - show_create_edit && - request.length && - canCreateEdit - ) { + if (show_create_edit && request.length && canCreateEdit) { const context = this.getCreationContext(request); options.push({ - label: this.env._t("Create and edit..."), + label: _t("Create and edit..."), classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create_edit", action: () => this.openMany2X({context}), }); @@ -205,7 +195,7 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { // No records if (!records.length && !this.activeActions.create) { options.push({ - label: this.env._t("No records"), + label: _t("No records"), classList: "o_m2o_no_result", unselectable: true, });