/** @odoo-module **/ import { Many2ManyTagsField, Many2ManyTagsFieldColorEditable, } from "@web/views/fields/many2many_tags/many2many_tags_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 {ReferenceField} from "@web/views/fields/reference/reference_field"; import {X2ManyField} from "@web/views/fields/x2many/x2many_field"; 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 {sprintf} from "@web/core/utils/strings"; import {useService} from "@web/core/utils/hooks"; const {Component} = owl; /** * Patch Many2ManyTagsField **/ patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", { setup() { this._super(...arguments); this.actionService = useService("action"); }, /** * @override */ getTagProps(record) { const props = this._super(...arguments); props.onClick = (ev) => this.onMany2ManyBadgeClick(ev, record); return props; }, async onMany2ManyBadgeClick(event, record) { var self = this; if (self.props.open) { var context = self.context; var id = record.data.id; if (self.props.readonly) { event.preventDefault(); event.stopPropagation(); const action = await self.orm.call( self.props.relation, "get_formview_action", [[id]], {context: context} ); self.actionService.doAction(action); } else { const view_id = await self.orm.call( self.props.relation, "get_formview_id", [[id]], {context: context} ); const write_access = await self.orm.call( self.props.relation, "check_access_rights", [], {operation: "write", raise_exception: false} ); var can_write = self.props.canWrite; self.dialog.add(FormViewDialog, { resModel: self.props.relation, resId: id, context: context, title: self.env._t("Open: ") + self.string, viewId: view_id, mode: !can_write || !write_access ? "readonly" : "edit", onRecordSaved: () => self.props.value.model.load(), }); } } }, }); Many2ManyTagsField.props = { ...Many2ManyTagsField.props, open: {type: Boolean, optional: true}, canWrite: {type: Boolean, optional: true}, 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, }); }; /** * 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 ); } }, } ); Many2ManyTagsFieldColorEditable.props = { ...Many2ManyTagsFieldColorEditable.props, open: {type: Boolean, optional: true}, canWrite: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; /** * CreateConfirmationDialog * New customized component for Many2One Field **/ class CreateConfirmationDialog extends Component { get title() { return sprintf(this.env._t("New: %s"), this.props.name); } async onCreate() { await this.props.create(); this.props.close(); } async onCreateEdit() { await this.props.createEdit(); this.props.close(); } } CreateConfirmationDialog.components = {Dialog}; CreateConfirmationDialog.template = "web_m2x_options.Many2OneField.CreateConfirmationDialog"; /** * Many2OneField **/ patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", { setup() { this._super(...arguments); this.ir_options = Component.env.session.web_m2x_options; }, /** * @override */ get Many2XAutocompleteProps() { const props = this._super(...arguments); return { ...props, searchLimit: this.props.searchLimit, searchMore: this.props.searchMore, canCreate: this.props.canCreate, nodeOptions: this.props.nodeOptions, }; }, async openConfirmationDialog(request) { var m2o_dialog_opt = is_option_set(this.props.nodeOptions.m2o_dialog) || (_.isUndefined(this.props.nodeOptions.m2o_dialog) && 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"])); if (this.props.canCreate && this.state.isFloating && m2o_dialog_opt) { return new Promise((resolve, reject) => { this.addDialog(CreateConfirmationDialog, { value: request, name: this.props.string, create: async () => { try { await this.quickCreate(request); resolve(); } catch (e) { reject(e); } }, createEdit: async () => { try { await this.quickCreate(request); await this.props.record.model.load(); this.openMany2X({ resId: this.props.value[0], context: this.user_context, }); resolve(); } catch (e) { reject(e); } }, }); }); } }, }); 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, }); }; Many2OneField.props = { ...Many2OneField.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; /** * FIXME: find better way to extend props in Many2OneField * Override ReferenceField * 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 */ ReferenceField.props = { ...ReferenceField.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; /** * FIXME: find better way to extend props in Many2OneField * Override Many2OneBarcodeField * 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 */ Many2OneBarcodeField.props = { ...Many2OneBarcodeField.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; /** * FIXME: find better way to extend props in Many2OneField * Override Many2OneAvatarField * 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 */ Many2OneAvatarField.props = { ...Many2OneAvatarField.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; /** * FIXME: find better way to extend props in Many2OneField * Override mailing_m2o_filter * 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 mass_mailing as optional module, * So need to import dynamic way */ try { (async () => { // Make sure component mailing_m2o_filter in mass mailing module loaded const installed_mass_mailing = await odoo.ready( "@mass_mailing/js/mailing_m2o_filter" ); if (installed_mass_mailing) { var FieldMany2OneMailingFilter = await odoo.runtimeImport( "@mass_mailing/js/mailing_m2o_filter" ); FieldMany2OneMailingFilter.props = { ...FieldMany2OneMailingFilter.props, searchMore: {type: Boolean, optional: true}, nodeOptions: {type: Object, optional: true}, }; } })(); } catch { console.log( "Ignore overriding props of component mailing_m2o_filter since the module is not installed" ); } /** * X2ManyField **/ patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", { /** * @override */ async openRecord(record) { var self = this; var open = this.props.open; if (open && self.props.readonly) { var res_id = record.data.id; const action = await self.env.model.orm.call( self.props.value.resModel, "get_formview_action", [[res_id]] ); return self.env.model.actionService.doAction(action); } return this._super.apply(this, arguments); }, }); const X2ManyFieldExtractProps = X2ManyField.extractProps; X2ManyField.extractProps = ({attrs}) => { const canOpen = Boolean(attrs.options.open); return Object.assign(X2ManyFieldExtractProps({attrs}), { open: canOpen, }); }; X2ManyField.props = { ...X2ManyField.props, open: {type: Boolean, optional: true}, }; /** * FormController **/ patch(FormController.prototype, "web_m2x_options.FormController", { /** * @override */ setup() { var self = this; this._super(...arguments); /** 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) * do the trick to override beforeLoadResolver here to customize viewLimit */ this.superBeforeLoadResolver = this.beforeLoadResolver; this.beforeLoadResolver = async () => { await self._setSubViewLimit(); self.superBeforeLoadResolver(); }; }, /** * @override * add more method to add subview limit on formview */ async _setSubViewLimit() { const ir_options = Component.env.session.web_m2x_options; const activeFields = this.archInfo.activeFields, fields = this.props.fields, isSmall = this.user; var limit = ir_options["web_m2x_options.field_limit_entries"]; if (!_.isUndefined(limit)) { limit = parseInt(limit, 10); } for (const fieldName in activeFields) { const field = fields[fieldName]; if (!isX2Many(field)) { // What follows only concerns x2many fields continue; } const fieldInfo = activeFields[fieldName]; if (fieldInfo.modifiers.invisible === true) { // No need to fetch the sub view if the field is always invisible continue; } if (!fieldInfo.FieldComponent.useSubView) { // The FieldComponent used to render the field doesn't need a sub view continue; } let viewType = fieldInfo.viewMode || "list,kanban"; viewType = viewType.replace("tree", "list"); if (viewType.includes(",")) { viewType = isSmall ? "kanban" : "list"; } fieldInfo.viewMode = viewType; if (fieldInfo.views[viewType] && limit) { fieldInfo.views[viewType].limit = limit; } } }, });