diff --git a/pandoc-3.2-1-amd64.deb b/pandoc-3.2-1-amd64.deb deleted file mode 100644 index f92671621201..000000000000 Binary files a/pandoc-3.2-1-amd64.deb and /dev/null differ diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst index 13d0293ddb8f..d088e2177a3e 100644 --- a/web_m2x_options/README.rst +++ b/web_m2x_options/README.rst @@ -155,7 +155,7 @@ Your XML form view definition could contain: .. code:: xml ... - + ... Known issues / Roadmap diff --git a/web_m2x_options/readme/USAGE.md b/web_m2x_options/readme/USAGE.md index 73507a0f981e..ae26215507ca 100644 --- a/web_m2x_options/readme/USAGE.md +++ b/web_m2x_options/readme/USAGE.md @@ -101,6 +101,6 @@ Your XML form view definition could contain: ``` xml ... - + ... ``` diff --git a/web_m2x_options/static/description/index.html b/web_m2x_options/static/description/index.html index bf406ab1885f..0c7ec4227f68 100644 --- a/web_m2x_options/static/description/index.html +++ b/web_m2x_options/static/description/index.html @@ -1,4 +1,3 @@ - diff --git a/web_m2x_options/static/src/components/form.esm.js b/web_m2x_options/static/src/components/form.esm.js index ecb37d270021..d89487a82148 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; }, @@ -41,12 +46,12 @@ patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", { var self = this; if (self.props.open) { var context = self.context; - var id = record.data.id; + var id = record.resId; if (self.props.readonly) { event.preventDefault(); event.stopPropagation(); const action = await self.orm.call( - self.props.relation, + record.resModel, "get_formview_action", [[id]], {context: context} @@ -54,27 +59,27 @@ patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", { self.actionService.doAction(action); } else { const view_id = await self.orm.call( - self.props.relation, + record.resModel, "get_formview_id", [[id]], {context: context} ); const write_access = await self.orm.call( - self.props.relation, + record.resModel, "check_access_rights", [], {operation: "write", raise_exception: false} ); var can_write = self.props.canWrite; self.dialog.add(FormViewDialog, { - resModel: self.props.relation, + resModel: record.resModel, resId: id, context: context, - title: self.env._t("Open: ") + self.string, + title: _t("Open: ") + self.string, viewId: view_id, mode: !can_write || !write_access ? "readonly" : "edit", - onRecordSaved: () => self.props.value.model.load(), + onRecordSaved: () => record.model.load(), }); } } @@ -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,182 @@ 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" + ); +} + +/** + * FIXME: find better way to extend props in Many2ManyField + * Override many2manyattendee + * Since extracted/added props: nodeOptions and searchMore into Many2ManyField props + * and this component inherited props from Many2ManyField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + * This component is in module calendar as optional module, + * So need to import dynamic way + */ +try { + (async () => { + // Make sure component sale_product_field in sale module loaded + const installed_calendar = await odoo.loader.modules.get( + "@calendar/views/fields/many2many_attendee" + ); + if (installed_calendar) { + installed_calendar.Many2ManyAttendee.props = { + ...installed_calendar.Many2ManyAttendee.props, + open: {type: Boolean, optional: true}, + canWrite: {type: Boolean, optional: true}, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, + }; + const Many2ManyAttendeeExtractProps = + installed_calendar.many2ManyAttendee.extractProps; + installed_calendar.many2ManyAttendee.extractProps = ( + fieldInfo, + dynamicInfo + ) => { + const canWrite = + fieldInfo.attrs.can_write && + evaluateBooleanExpr(fieldInfo.attrs.can_write); + return Object.assign( + Many2ManyAttendeeExtractProps(fieldInfo, dynamicInfo), + { + open: Boolean(fieldInfo.options.open), + canWrite: canWrite, + searchMore: fieldInfo.options.search_more, + nodeOptions: fieldInfo.options, + } + ); + }; + } + })(); +} catch { + console.log( + "Ignore overriding props of component installed_calendar since the module is not installed" + ); +} + /** * X2ManyField **/ -patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", { +patch(X2ManyField.prototype, { /** * @override */ @@ -319,16 +516,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 +545,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 +575,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, });