forked from Techsystech/web
[16.0][MIG] web_m2x_options: Migration to 16.0
parent
1ddab4db69
commit
b803f95772
|
@ -0,0 +1 @@
|
||||||
|
../../../../web_m2x_options
|
|
@ -0,0 +1,6 @@
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
setup_requires=['setuptools-odoo'],
|
||||||
|
odoo_addon=True,
|
||||||
|
)
|
|
@ -85,10 +85,11 @@ in the field's options dict
|
||||||
|
|
||||||
Makes many2many_tags and one2many rows buttons that open the linked resource
|
Makes many2many_tags and one2many rows buttons that open the linked resource
|
||||||
|
|
||||||
``no_color_picker`` *boolean* (Default: ``False``)
|
``no_color_picker`` *boolean* (Default: ``False``) (obselete: use native option: ``no_edit_color`` instead)
|
||||||
|
|
||||||
Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)
|
Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)
|
||||||
|
|
||||||
|
|
||||||
ir.config_parameter options
|
ir.config_parameter options
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "web_m2x_options",
|
"name": "web_m2x_options",
|
||||||
"version": "15.0.1.1.0",
|
"version": "16.0.1.1.0",
|
||||||
"category": "Web",
|
"category": "Web",
|
||||||
"author": "initOS GmbH,"
|
"author": "initOS GmbH,"
|
||||||
"ACSONE SA/NV, "
|
"ACSONE SA/NV, "
|
||||||
|
@ -16,12 +16,6 @@
|
||||||
"website": "https://github.com/OCA/web",
|
"website": "https://github.com/OCA/web",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"depends": ["web"],
|
"depends": ["web"],
|
||||||
"assets": {
|
"assets": {"web.assets_backend": ["web_m2x_options/static/src/components/*"]},
|
||||||
"web.assets_backend": [
|
|
||||||
"web_m2x_options/static/src/js/form.js",
|
|
||||||
"web_m2x_options/static/src/js/ir_options.js",
|
|
||||||
],
|
|
||||||
"web.assets_qweb": ["web_m2x_options/static/src/xml/base.xml"],
|
|
||||||
},
|
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,3 +12,5 @@
|
||||||
* Carlos Roca
|
* Carlos Roca
|
||||||
* Bhavesh Odedra <bodedra@opensourceintegrators.com>
|
* Bhavesh Odedra <bodedra@opensourceintegrators.com>
|
||||||
* Dhara Solanki <dhara.solanki@initos.com> (http://www.initos.com)
|
* Dhara Solanki <dhara.solanki@initos.com> (http://www.initos.com)
|
||||||
|
* `Trobz <https://trobz.com>`_:
|
||||||
|
* Hoang Diep <hoang@trobz.com>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
|
||||||
|
<t
|
||||||
|
t-name="web_m2x_options.AutoComplete"
|
||||||
|
t-inherit="web.AutoComplete"
|
||||||
|
t-inherit-mode="extension"
|
||||||
|
owl="1"
|
||||||
|
>
|
||||||
|
<xpath expr="//t[@t-foreach='source.options']/li/a" position="attributes">
|
||||||
|
<attribute name="t-attf-style">{{ option.style }}</attribute>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t
|
||||||
|
t-name="web_m2x_options.Many2ManyTagsField"
|
||||||
|
t-inherit="web.Many2ManyTagsField"
|
||||||
|
t-inherit-mode="extension"
|
||||||
|
owl="1"
|
||||||
|
>
|
||||||
|
<xpath expr="//Many2XAutocomplete" position="attributes">
|
||||||
|
<attribute name="nodeOptions">props.nodeOptions</attribute>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="web_m2x_options.Many2OneField.CreateConfirmationDialog" owl="1">
|
||||||
|
<Dialog title="title" size="'md'">
|
||||||
|
<div>
|
||||||
|
You are creating a new <strong t-esc="props.value" /> as a new <t
|
||||||
|
t-esc="props.name"
|
||||||
|
/>, are you sure it does not exist yet?
|
||||||
|
</div>
|
||||||
|
<t t-set-slot="footer">
|
||||||
|
<button class="btn btn-primary" t-on-click="onCreate">Create</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
t-on-click="onCreateEdit"
|
||||||
|
>Create and Edit</button>
|
||||||
|
<button class="btn" t-on-click="() => props.close()">Discard</button>
|
||||||
|
</t>
|
||||||
|
</Dialog>
|
||||||
|
</t>
|
||||||
|
</templates>
|
|
@ -0,0 +1,418 @@
|
||||||
|
/** @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, onWillStart} = 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);
|
||||||
|
const ormService = useService("orm");
|
||||||
|
this.user_context = Component.env.session.user_context;
|
||||||
|
onWillStart(async () => {
|
||||||
|
this.ir_options = await ormService.call(
|
||||||
|
"ir.config_parameter",
|
||||||
|
"get_web_m2x_options",
|
||||||
|
[],
|
||||||
|
{context: this.user_context}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @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 = await this.model.orm.call(
|
||||||
|
"ir.config_parameter",
|
||||||
|
"get_web_m2x_options",
|
||||||
|
[],
|
||||||
|
{context: this.user_context}
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,231 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import {Many2XAutocomplete} from "@web/views/fields/relational_utils";
|
||||||
|
import {patch} from "@web/core/utils/patch";
|
||||||
|
import {sprintf} from "@web/core/utils/strings";
|
||||||
|
import {useService} from "@web/core/utils/hooks";
|
||||||
|
const {Component, onWillStart} = owl;
|
||||||
|
|
||||||
|
export function is_option_set(option) {
|
||||||
|
if (_.isUndefined(option)) 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", {
|
||||||
|
setup() {
|
||||||
|
this._super(...arguments);
|
||||||
|
const ormService = useService("orm");
|
||||||
|
this.user_context = Component.env.session.user_context;
|
||||||
|
onWillStart(async () => {
|
||||||
|
this.ir_options = await ormService.call(
|
||||||
|
"ir.config_parameter",
|
||||||
|
"get_web_m2x_options",
|
||||||
|
[],
|
||||||
|
{context: this.user_context}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadOptionsSource(request) {
|
||||||
|
if (this.lastProm) {
|
||||||
|
this.lastProm.abort(false);
|
||||||
|
}
|
||||||
|
// Add options limit used to change number of selections record
|
||||||
|
// returned.
|
||||||
|
if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) {
|
||||||
|
this.props.searchLimit = parseInt(
|
||||||
|
this.ir_options["web_m2x_options.limit"],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
this.limit = this.props.searchLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.props.nodeOptions.limit === "number") {
|
||||||
|
this.props.searchLimit = this.props.nodeOptions.limit;
|
||||||
|
this.limit = this.props.searchLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add options field_color and colors to color item(s) depending on field_color value
|
||||||
|
this.field_color = this.props.nodeOptions.field_color;
|
||||||
|
this.colors = this.props.nodeOptions.colors;
|
||||||
|
|
||||||
|
this.lastProm = this.orm.call(this.props.resModel, "name_search", [], {
|
||||||
|
name: request,
|
||||||
|
operator: "ilike",
|
||||||
|
args: this.props.getDomain(),
|
||||||
|
limit: this.props.searchLimit + 1,
|
||||||
|
context: this.props.context,
|
||||||
|
});
|
||||||
|
const records = await this.lastProm;
|
||||||
|
|
||||||
|
var options = records.map((result) => ({
|
||||||
|
value: result[0],
|
||||||
|
id: result[0],
|
||||||
|
label: result[1].split("\n")[0],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Limit results if there is a custom limit options
|
||||||
|
if (this.limit) {
|
||||||
|
options = options.slice(0, this.props.searchLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search result value colors
|
||||||
|
if (this.colors && this.field_color) {
|
||||||
|
var value_ids = options.map((result) => result.value);
|
||||||
|
const objects = await this.orm.call(
|
||||||
|
this.props.resModel,
|
||||||
|
"search_read",
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
domain: [["id", "in", value_ids]],
|
||||||
|
fields: [this.field_color],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
for (var index in objects) {
|
||||||
|
for (var index_value in options) {
|
||||||
|
if (options[index_value].id === objects[index].id) {
|
||||||
|
// Find value in values by comparing ids
|
||||||
|
var option = options[index_value];
|
||||||
|
// Find color with field value as key
|
||||||
|
var color =
|
||||||
|
this.colors[objects[index][this.field_color]] || "black";
|
||||||
|
option.style = "color:" + color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick create
|
||||||
|
// Note: Create should be before `search_more` (reserve native order)
|
||||||
|
// One more reason: when calling `onInputBlur`, native select the first option (activeSourceOption)
|
||||||
|
// which triggers m2o_dialog if m2o_dialog=true
|
||||||
|
var create_enabled =
|
||||||
|
this.props.quickCreate && !this.props.nodeOptions.no_create;
|
||||||
|
|
||||||
|
var raw_result = _.map(records, function (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"]),
|
||||||
|
m2x_create = is_option_set(this.ir_options["web_m2x_options.create"]);
|
||||||
|
var show_create =
|
||||||
|
(!this.props.nodeOptions && (m2x_create_undef || m2x_create)) ||
|
||||||
|
(this.props.nodeOptions &&
|
||||||
|
(quick_create ||
|
||||||
|
(quick_create_undef && (m2x_create_undef || m2x_create))));
|
||||||
|
if (
|
||||||
|
create_enabled &&
|
||||||
|
!this.props.nodeOptions.no_quick_create &&
|
||||||
|
request.length > 0 &&
|
||||||
|
!_.contains(raw_result, request) &&
|
||||||
|
show_create
|
||||||
|
) {
|
||||||
|
options.push({
|
||||||
|
label: sprintf(this.env._t(`Create "%s"`), request),
|
||||||
|
classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create",
|
||||||
|
action: async (params) => {
|
||||||
|
try {
|
||||||
|
await this.props.quickCreate(request, params);
|
||||||
|
} catch {
|
||||||
|
const context = this.getCreationContext(request);
|
||||||
|
return this.openMany2X({context});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search more...
|
||||||
|
// Resolution order:
|
||||||
|
// 1- check if "search_more" is set locally in node's options
|
||||||
|
// 2- if set locally, apply its value
|
||||||
|
// 3- if not set locally, check if it's set globally via ir.config_parameter
|
||||||
|
// 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)) {
|
||||||
|
search_more = is_option_set(this.props.nodeOptions.search_more);
|
||||||
|
} else if (!_.isUndefined(this.ir_options["web_m2x_options.search_more"])) {
|
||||||
|
search_more = is_option_set(this.ir_options["web_m2x_options.search_more"]);
|
||||||
|
} else {
|
||||||
|
search_more =
|
||||||
|
!this.props.noSearchMore && this.props.searchLimit < records.length;
|
||||||
|
}
|
||||||
|
if (search_more) {
|
||||||
|
options.push({
|
||||||
|
label: this.env._t("Search More..."),
|
||||||
|
action: this.onSearchMore.bind(this, request),
|
||||||
|
classList: "o_m2o_dropdown_option o_m2o_dropdown_option_search_more",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and Edit
|
||||||
|
const canCreateEdit =
|
||||||
|
"createEdit" in this.activeActions
|
||||||
|
? this.activeActions.createEdit
|
||||||
|
: this.activeActions.create;
|
||||||
|
if (
|
||||||
|
!request.length &&
|
||||||
|
!this.props.value &&
|
||||||
|
(this.props.quickCreate || canCreateEdit)
|
||||||
|
) {
|
||||||
|
options.push({
|
||||||
|
label: this.env._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"]
|
||||||
|
),
|
||||||
|
m2x_create_edit = is_option_set(
|
||||||
|
this.ir_options["web_m2x_options.create_edit"]
|
||||||
|
);
|
||||||
|
var show_create_edit =
|
||||||
|
(!this.props.nodeOptions && (m2x_create_edit_undef || m2x_create_edit)) ||
|
||||||
|
(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
|
||||||
|
) {
|
||||||
|
const context = this.getCreationContext(request);
|
||||||
|
options.push({
|
||||||
|
label: this.env._t("Create and edit..."),
|
||||||
|
classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create_edit",
|
||||||
|
action: () => this.openMany2X({context}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// No records
|
||||||
|
if (!records.length && !this.activeActions.create) {
|
||||||
|
options.push({
|
||||||
|
label: this.env._t("No records"),
|
||||||
|
classList: "o_m2o_no_result",
|
||||||
|
unselectable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Many2XAutocomplete.defaultProps = {
|
||||||
|
...Many2XAutocomplete.defaultProps,
|
||||||
|
nodeOptions: {},
|
||||||
|
};
|
|
@ -1,489 +0,0 @@
|
||||||
/* Copyright 2016 0k.io,ACSONE SA/NV
|
|
||||||
* * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
|
||||||
|
|
||||||
odoo.define("web_m2x_options.web_m2x_options", function (require) {
|
|
||||||
"use strict";
|
|
||||||
var core = require("web.core"),
|
|
||||||
data = require("web.data"),
|
|
||||||
Dialog = require("web.Dialog"),
|
|
||||||
FormView = require("web.FormView"),
|
|
||||||
view_dialogs = require("web.view_dialogs"),
|
|
||||||
relational_fields = require("web.relational_fields"),
|
|
||||||
ir_options = require("web_m2x_options.ir_options");
|
|
||||||
|
|
||||||
var _t = core._t,
|
|
||||||
FieldMany2ManyTags = relational_fields.FieldMany2ManyTags,
|
|
||||||
FieldMany2One = relational_fields.FieldMany2One,
|
|
||||||
FieldOne2Many = relational_fields.FieldOne2Many,
|
|
||||||
FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags;
|
|
||||||
|
|
||||||
function is_option_set(option) {
|
|
||||||
if (_.isUndefined(option)) return false;
|
|
||||||
if (typeof option === "string") return option === "true" || option === "True";
|
|
||||||
if (typeof option === "boolean") return option;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var M2ODialog = Dialog.extend({
|
|
||||||
template: "M2ODialog",
|
|
||||||
init: function (parent, name, value) {
|
|
||||||
this.name = name;
|
|
||||||
this.value = value;
|
|
||||||
this._super(parent, {
|
|
||||||
title: _.str.sprintf(_t("Create a %s"), this.name),
|
|
||||||
size: "medium",
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: _t("Create"),
|
|
||||||
classes: "btn-primary",
|
|
||||||
click: function () {
|
|
||||||
if (this.$("input").val()) {
|
|
||||||
this.trigger_up("quick_create", {
|
|
||||||
value: this.$("input").val(),
|
|
||||||
});
|
|
||||||
this.close(true);
|
|
||||||
} else {
|
|
||||||
this.$("input").focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: _t("Create and edit"),
|
|
||||||
classes: "btn-primary",
|
|
||||||
close: true,
|
|
||||||
click: function () {
|
|
||||||
this.trigger_up("search_create_popup", {
|
|
||||||
view_type: "form",
|
|
||||||
value: this.$("input").val(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: _t("Cancel"),
|
|
||||||
close: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
start: function () {
|
|
||||||
this.$("p").text(
|
|
||||||
_.str.sprintf(
|
|
||||||
_t(
|
|
||||||
"You are creating a new %s, are you sure it does not exist yet?"
|
|
||||||
),
|
|
||||||
this.name
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.$("input").val(this.value);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
* @param {Boolean} isSet
|
|
||||||
*/
|
|
||||||
close: function (isSet) {
|
|
||||||
this.isSet = isSet;
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
destroy: function () {
|
|
||||||
if (!this.isSet) {
|
|
||||||
this.trigger_up("closed_unset");
|
|
||||||
}
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
FieldMany2One.include({
|
|
||||||
_onInputFocusout: function () {
|
|
||||||
var m2o_dialog_opt =
|
|
||||||
is_option_set(this.nodeOptions.m2o_dialog) ||
|
|
||||||
(_.isUndefined(this.nodeOptions.m2o_dialog) &&
|
|
||||||
is_option_set(ir_options["web_m2x_options.m2o_dialog"])) ||
|
|
||||||
(_.isUndefined(this.nodeOptions.m2o_dialog) &&
|
|
||||||
_.isUndefined(ir_options["web_m2x_options.m2o_dialog"]));
|
|
||||||
if (this.can_create && this.floating && m2o_dialog_opt) {
|
|
||||||
new M2ODialog(this, this.string, this.$input.val()).open();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_search: function (search_val) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var def = new Promise((resolve) => {
|
|
||||||
// Add options limit used to change number of selections record
|
|
||||||
// returned.
|
|
||||||
if (!_.isUndefined(ir_options["web_m2x_options.limit"])) {
|
|
||||||
this.limit = parseInt(ir_options["web_m2x_options.limit"], 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof self.nodeOptions.limit === "number") {
|
|
||||||
self.limit = self.nodeOptions.limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add options field_color and colors to color item(s) depending on field_color value
|
|
||||||
self.field_color = self.nodeOptions.field_color;
|
|
||||||
self.colors = self.nodeOptions.colors;
|
|
||||||
|
|
||||||
var context = self.record.getContext(self.recordParams);
|
|
||||||
var domain = self.record.getDomain(self.recordParams);
|
|
||||||
|
|
||||||
var blacklisted_ids = self._getSearchBlacklist();
|
|
||||||
if (blacklisted_ids.length > 0) {
|
|
||||||
domain.push(["id", "not in", blacklisted_ids]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "name_search",
|
|
||||||
kwargs: {
|
|
||||||
name: search_val,
|
|
||||||
args: domain,
|
|
||||||
operator: "ilike",
|
|
||||||
limit: self.limit + 1,
|
|
||||||
context: context,
|
|
||||||
},
|
|
||||||
}).then((result) => {
|
|
||||||
// Possible selections for the m2o
|
|
||||||
var values = _.map(result, (x) => {
|
|
||||||
x[1] = self._getDisplayName(x[1]);
|
|
||||||
return {
|
|
||||||
label:
|
|
||||||
_.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
|
|
||||||
value: x[1],
|
|
||||||
name: x[1],
|
|
||||||
id: x[0],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search result value colors
|
|
||||||
if (self.colors && self.field_color) {
|
|
||||||
var value_ids = [];
|
|
||||||
for (var val_index in values) {
|
|
||||||
value_ids.push(values[val_index].id);
|
|
||||||
}
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "search_read",
|
|
||||||
fields: [self.field_color],
|
|
||||||
domain: [["id", "in", value_ids]],
|
|
||||||
}).then((objects) => {
|
|
||||||
for (var index in objects) {
|
|
||||||
for (var index_value in values) {
|
|
||||||
if (values[index_value].id === objects[index].id) {
|
|
||||||
// Find value in values by comparing ids
|
|
||||||
var value = values[index_value];
|
|
||||||
// Find color with field value as key
|
|
||||||
var color =
|
|
||||||
self.colors[
|
|
||||||
objects[index][self.field_color]
|
|
||||||
] || "black";
|
|
||||||
value.label =
|
|
||||||
'<span style="color:' +
|
|
||||||
color +
|
|
||||||
'">' +
|
|
||||||
value.label +
|
|
||||||
"</span>";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(values);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search more...
|
|
||||||
// Resolution order:
|
|
||||||
// 1- check if "search_more" is set locally in node's options
|
|
||||||
// 2- if set locally, apply its value
|
|
||||||
// 3- if not set locally, check if it's set globally via ir.config_parameter
|
|
||||||
// 4- if set globally, apply its value
|
|
||||||
// 5- if not set globally either, check if returned values are more than node's limit
|
|
||||||
if (!_.isUndefined(self.nodeOptions.search_more)) {
|
|
||||||
var search_more = is_option_set(self.nodeOptions.search_more);
|
|
||||||
} else if (
|
|
||||||
!_.isUndefined(ir_options["web_m2x_options.search_more"])
|
|
||||||
) {
|
|
||||||
var search_more = is_option_set(
|
|
||||||
ir_options["web_m2x_options.search_more"]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
var search_more = values.length > self.limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search_more) {
|
|
||||||
values = values.slice(0, self.limit);
|
|
||||||
values.push({
|
|
||||||
label: _t("Search More..."),
|
|
||||||
action: function () {
|
|
||||||
var prom = [];
|
|
||||||
if (search_val !== "") {
|
|
||||||
prom = self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "name_search",
|
|
||||||
kwargs: {
|
|
||||||
name: search_val,
|
|
||||||
args: domain,
|
|
||||||
operator: "ilike",
|
|
||||||
limit: self.SEARCH_MORE_LIMIT,
|
|
||||||
context: context,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Promise.resolve(prom).then(function (results) {
|
|
||||||
var dynamicFilters = [];
|
|
||||||
if (results) {
|
|
||||||
var ids = _.map(results, function (x) {
|
|
||||||
return x[0];
|
|
||||||
});
|
|
||||||
if (search_val) {
|
|
||||||
dynamicFilters = [
|
|
||||||
{
|
|
||||||
description: _.str.sprintf(
|
|
||||||
_t("Quick search: %s"),
|
|
||||||
search_val
|
|
||||||
),
|
|
||||||
domain: [["id", "in", ids]],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
dynamicFilters = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self._searchCreatePopup(
|
|
||||||
"search",
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
dynamicFilters
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
classname: "o_m2o_dropdown_option",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var create_enabled = self.can_create && !self.nodeOptions.no_create;
|
|
||||||
// Quick create
|
|
||||||
var raw_result = _.map(result, function (x) {
|
|
||||||
return x[1];
|
|
||||||
});
|
|
||||||
var quick_create = is_option_set(self.nodeOptions.create),
|
|
||||||
quick_create_undef = _.isUndefined(self.nodeOptions.create),
|
|
||||||
m2x_create_undef = _.isUndefined(
|
|
||||||
ir_options["web_m2x_options.create"]
|
|
||||||
),
|
|
||||||
m2x_create = is_option_set(
|
|
||||||
ir_options["web_m2x_options.create"]
|
|
||||||
);
|
|
||||||
var show_create =
|
|
||||||
(!self.nodeOptions && (m2x_create_undef || m2x_create)) ||
|
|
||||||
(self.nodeOptions &&
|
|
||||||
(quick_create ||
|
|
||||||
(quick_create_undef &&
|
|
||||||
(m2x_create_undef || m2x_create))));
|
|
||||||
if (
|
|
||||||
create_enabled &&
|
|
||||||
!self.nodeOptions.no_quick_create &&
|
|
||||||
search_val.length > 0 &&
|
|
||||||
!_.contains(raw_result, search_val) &&
|
|
||||||
show_create
|
|
||||||
) {
|
|
||||||
values.push({
|
|
||||||
label: _.str.sprintf(
|
|
||||||
_t('Create "<strong>%s</strong>"'),
|
|
||||||
$("<span />").text(search_val).html()
|
|
||||||
),
|
|
||||||
action: self._quickCreate.bind(self, search_val),
|
|
||||||
classname: "o_m2o_dropdown_option",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Create and edit ...
|
|
||||||
|
|
||||||
var create_edit =
|
|
||||||
is_option_set(self.nodeOptions.create) ||
|
|
||||||
is_option_set(self.nodeOptions.create_edit),
|
|
||||||
create_edit_undef =
|
|
||||||
_.isUndefined(self.nodeOptions.create) &&
|
|
||||||
_.isUndefined(self.nodeOptions.create_edit),
|
|
||||||
m2x_create_edit_undef = _.isUndefined(
|
|
||||||
ir_options["web_m2x_options.create_edit"]
|
|
||||||
),
|
|
||||||
m2x_create_edit = is_option_set(
|
|
||||||
ir_options["web_m2x_options.create_edit"]
|
|
||||||
);
|
|
||||||
var show_create_edit =
|
|
||||||
(!self.nodeOptions &&
|
|
||||||
(m2x_create_edit_undef || m2x_create_edit)) ||
|
|
||||||
(self.nodeOptions &&
|
|
||||||
(create_edit ||
|
|
||||||
(create_edit_undef &&
|
|
||||||
(m2x_create_edit_undef || m2x_create_edit))));
|
|
||||||
if (
|
|
||||||
create_enabled &&
|
|
||||||
!self.nodeOptions.no_create_edit &&
|
|
||||||
show_create_edit
|
|
||||||
) {
|
|
||||||
var createAndEditAction = function () {
|
|
||||||
// Clear the value in case the user clicks on discard
|
|
||||||
self.$("input").val("");
|
|
||||||
return self._searchCreatePopup(
|
|
||||||
"form",
|
|
||||||
false,
|
|
||||||
self._createContext(search_val)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
values.push({
|
|
||||||
label: _t("Create and Edit..."),
|
|
||||||
action: createAndEditAction,
|
|
||||||
classname: "o_m2o_dropdown_option",
|
|
||||||
});
|
|
||||||
} else if (values.length === 0) {
|
|
||||||
values.push({
|
|
||||||
label: _t("No results to show..."),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Check if colors specified to wait for RPC
|
|
||||||
if (!(self.field_color && self.colors)) {
|
|
||||||
resolve(values);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.orderer.add(def);
|
|
||||||
|
|
||||||
// Add options limit used to change number of selections record
|
|
||||||
// returned.
|
|
||||||
if (!_.isUndefined(ir_options["web_m2x_options.limit"])) {
|
|
||||||
this.limit = parseInt(ir_options["web_m2x_options.limit"], 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.nodeOptions.limit === "number") {
|
|
||||||
this.limit = this.nodeOptions.limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return def;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
FieldMany2ManyTags.include({
|
|
||||||
events: _.extend({}, FieldMany2ManyTags.prototype.events, {
|
|
||||||
"click .badge": "_onOpenBadge",
|
|
||||||
}),
|
|
||||||
|
|
||||||
_onDeleteTag: function (event) {
|
|
||||||
var result = this._super.apply(this, arguments);
|
|
||||||
event.stopPropagation();
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onOpenBadge: function (event) {
|
|
||||||
var self = this;
|
|
||||||
var open = self.nodeOptions && is_option_set(self.nodeOptions.open);
|
|
||||||
if (open) {
|
|
||||||
var context = self.record.getContext(self.recordParams);
|
|
||||||
var id = parseInt($(event.currentTarget).data("id"), 10);
|
|
||||||
|
|
||||||
if (self.mode === "readonly") {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "get_formview_action",
|
|
||||||
args: [[id]],
|
|
||||||
context: context,
|
|
||||||
}).then(function (action) {
|
|
||||||
self.trigger_up("do_action", {action: action});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$.when(
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "get_formview_id",
|
|
||||||
args: [[id]],
|
|
||||||
context: context,
|
|
||||||
}),
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "check_access_rights",
|
|
||||||
kwargs: {operation: "write", raise_exception: false},
|
|
||||||
})
|
|
||||||
).then(function (view_id, write_access) {
|
|
||||||
var can_write =
|
|
||||||
"can_write" in self.attrs
|
|
||||||
? JSON.parse(self.attrs.can_write)
|
|
||||||
: true;
|
|
||||||
new view_dialogs.FormViewDialog(self, {
|
|
||||||
res_model: self.field.relation,
|
|
||||||
res_id: id,
|
|
||||||
context: context,
|
|
||||||
title: _t("Open: ") + self.string,
|
|
||||||
view_id: view_id,
|
|
||||||
readonly: !can_write || !write_access,
|
|
||||||
on_saved: function (record, changed) {
|
|
||||||
if (changed) {
|
|
||||||
self._setValue(self.value.data, {
|
|
||||||
forceChange: true,
|
|
||||||
});
|
|
||||||
self.trigger_up("reload", {db_id: self.value.id});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}).open();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
FieldOne2Many.include({
|
|
||||||
_onOpenRecord: function (ev) {
|
|
||||||
var self = this;
|
|
||||||
var open = this.nodeOptions.open;
|
|
||||||
if (open && self.mode === "readonly") {
|
|
||||||
ev.stopPropagation();
|
|
||||||
var id = ev.data.id;
|
|
||||||
var res_id = self.record.data[self.name].data.filter(
|
|
||||||
(line) => line.id === id
|
|
||||||
)[0].res_id;
|
|
||||||
self._rpc({
|
|
||||||
model: self.field.relation,
|
|
||||||
method: "get_formview_action",
|
|
||||||
args: [[res_id]],
|
|
||||||
}).then(function (action) {
|
|
||||||
return self.do_action(action);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return this._super.apply(this, arguments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
FormFieldMany2ManyTags.include({
|
|
||||||
events: _.extend({}, FormFieldMany2ManyTags.prototype.events, {
|
|
||||||
"click .badge": "_onOpenBadge",
|
|
||||||
}),
|
|
||||||
|
|
||||||
_onOpenBadge: function (event) {
|
|
||||||
var open = is_option_set(this.nodeOptions.open);
|
|
||||||
var no_color_picker = is_option_set(this.nodeOptions.no_color_picker);
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
if (!open && !no_color_picker) {
|
|
||||||
this._onOpenColorPicker(event);
|
|
||||||
} else {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extending class to allow change the limit of o2m registry entries using the
|
|
||||||
// system parameter "web_m2x_options.field_limit_entries".
|
|
||||||
FormView.include({
|
|
||||||
_setSubViewLimit: function (attrs) {
|
|
||||||
this._super(attrs);
|
|
||||||
var limit = ir_options["web_m2x_options.field_limit_entries"];
|
|
||||||
if (!_.isUndefined(limit)) {
|
|
||||||
attrs.limit = parseInt(limit);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
/* Copyright 2020 Tecnativa - Carlos Roca
|
|
||||||
* * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
|
||||||
odoo.define("web_m2x_options.ir_options", function (require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var rpc = require("web.rpc");
|
|
||||||
|
|
||||||
return rpc.query({
|
|
||||||
model: "ir.config_parameter",
|
|
||||||
method: "get_web_m2x_options",
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com>
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
|
||||||
<templates xml:space="preserve">
|
|
||||||
<t t-extend="FieldMany2One">
|
|
||||||
<t t-jquery=".o_external_button" t-operation="attributes">
|
|
||||||
<attribute name="t-if">
|
|
||||||
!(widget.nodeOptions.no_open || widget.nodeOptions.no_open_edit || widget.noOpen)
|
|
||||||
</attribute>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</templates>
|
|
Loading…
Reference in New Issue