You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
16 KiB

  1. /* Copyright 2016 0k.io,ACSONE SA/NV
  2. * * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
  3. odoo.define('web_m2x_options.web_m2x_options', function (require) {
  4. "use strict";
  5. var core = require('web.core'),
  6. data = require('web.data'),
  7. Dialog = require('web.Dialog'),
  8. view_dialogs = require('web.view_dialogs'),
  9. relational_fields = require('web.relational_fields'),
  10. rpc = require('web.rpc');
  11. var _t = core._t,
  12. FieldMany2ManyTags = relational_fields.FieldMany2ManyTags,
  13. FieldMany2One = relational_fields.FieldMany2One,
  14. FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags;
  15. var web_m2x_options = rpc.query({
  16. model: "ir.config_parameter",
  17. method: 'get_web_m2x_options',
  18. });
  19. var M2ODialog = Dialog.extend({
  20. template: "M2ODialog",
  21. init: function (parent, name, value) {
  22. this.name = name;
  23. this.value = value;
  24. this._super(parent, {
  25. title: _.str.sprintf(_t("Create a %s"), this.name),
  26. size: 'medium',
  27. buttons: [{
  28. text: _t('Create'),
  29. classes: 'btn-primary',
  30. click: function () {
  31. if (this.$("input").val() !== '') {
  32. this.trigger_up('quick_create', {value: this.$('input').val()});
  33. this.close(true);
  34. } else {
  35. this.$("input").focus();
  36. }
  37. },
  38. }, {
  39. text: _t('Create and edit'),
  40. classes: 'btn-primary',
  41. close: true,
  42. click: function () {
  43. this.trigger_up('search_create_popup', {
  44. view_type: 'form',
  45. value: this.$('input').val(),
  46. });
  47. },
  48. }, {
  49. text: _t('Cancel'),
  50. close: true,
  51. }],
  52. });
  53. },
  54. start: function () {
  55. this.$("p").text(_.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name));
  56. this.$("input").val(this.value);
  57. },
  58. /**
  59. * @override
  60. * @param {boolean} isSet
  61. */
  62. close: function (isSet) {
  63. this.isSet = isSet;
  64. this._super.apply(this, arguments);
  65. },
  66. /**
  67. * @override
  68. */
  69. destroy: function () {
  70. if (!this.isSet) {
  71. this.trigger_up('closed_unset');
  72. }
  73. this._super.apply(this, arguments);
  74. },
  75. });
  76. FieldMany2One.include({
  77. start: function () {
  78. this._super.apply(this, arguments);
  79. return this.get_options();
  80. },
  81. get_options: function () {
  82. var self = this;
  83. if (_.isUndefined(this.ir_options_loaded)) {
  84. this.ir_options_loaded = $.Deferred();
  85. this.ir_options = {};
  86. web_m2x_options.done(function (records) {
  87. _(records).each(function(record) {
  88. self.ir_options[record.key] = record.value;
  89. });
  90. self.ir_options_loaded.resolve();
  91. });
  92. }
  93. return $.when();
  94. },
  95. is_option_set: function (option) {
  96. if (_.isUndefined(option))
  97. return false;
  98. if (typeof option === 'string')
  99. return option === 'true' || option === 'True';
  100. if (typeof option === 'boolean')
  101. return option;
  102. return false
  103. },
  104. _onInputFocusout: function () {
  105. var m2o_dialog_opt = this.is_option_set(this.nodeOptions.m2o_dialog) || _.isUndefined(this.nodeOptions.m2o_dialog) && this.is_option_set(this.ir_options['web_m2x_options.m2o_dialog']) || _.isUndefined(this.nodeOptions.m2o_dialog) && _.isUndefined(this.ir_options['web_m2x_options.m2o_dialog']);
  106. if (this.can_create && this.floating && m2o_dialog_opt) {
  107. new M2ODialog(this, this.string, this.$input.val()).open();
  108. }
  109. },
  110. _search: function (search_val) {
  111. var self = this;
  112. var def = $.Deferred();
  113. this.orderer.add(def);
  114. // add options limit used to change number of selections record
  115. // returned.
  116. if (!_.isUndefined(this.ir_options['web_m2x_options.limit'])) {
  117. this.limit = parseInt(this.ir_options['web_m2x_options.limit'], 10);
  118. }
  119. if (typeof this.nodeOptions.limit === 'number') {
  120. this.limit = this.nodeOptions.limit;
  121. }
  122. // add options field_color and colors to color item(s) depending on field_color value
  123. this.field_color = this.nodeOptions.field_color;
  124. this.colors = this.nodeOptions.colors;
  125. var context = this.record.getContext(this.recordParams);
  126. var domain = this.record.getDomain(this.recordParams);
  127. var blacklisted_ids = this._getSearchBlacklist();
  128. if (blacklisted_ids.length > 0) {
  129. domain.push(['id', 'not in', blacklisted_ids]);
  130. }
  131. this._rpc({
  132. model: this.field.relation,
  133. method: "name_search",
  134. kwargs: {
  135. name: search_val,
  136. args: domain,
  137. operator: "ilike",
  138. limit: this.limit + 1,
  139. context: context,
  140. }
  141. }).then(function (result) {
  142. // possible selections for the m2o
  143. var values = _.map(result, function (x) {
  144. x[1] = self._getDisplayName(x[1]);
  145. return {
  146. label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
  147. value: x[1],
  148. name: x[1],
  149. id: x[0],
  150. };
  151. });
  152. // Search result value colors
  153. if (self.colors && self.field_color) {
  154. var value_ids = [];
  155. for (var index in values) {
  156. value_ids.push(values[index].id);
  157. }
  158. self._rpc({
  159. model: self.field.relation,
  160. method: 'search_read',
  161. fields: [self.field_color],
  162. domain: [['id', 'in', value_ids]]
  163. }).then(function (objects) {
  164. for (var index in objects) {
  165. for (var index_value in values) {
  166. if (values[index_value].id == objects[index].id) {
  167. // Find value in values by comparing ids
  168. var value = values[index_value];
  169. // Find color with field value as key
  170. var color = self.colors[objects[index][self.field_color]] || 'black';
  171. value.label = '<span style="color:' + color + '">' + value.label + '</span>';
  172. break;
  173. }
  174. }
  175. }
  176. def.resolve(values);
  177. })
  178. }
  179. // search more... if more results that max
  180. var can_search_more = (self.nodeOptions && self.is_option_set(self.nodeOptions.search_more)),
  181. search_more_undef = _.isUndefined(self.nodeOptions.search_more) && _.isUndefined(self.ir_options['web_m2x_options.search_more']),
  182. search_more = self.is_option_set(self.ir_options['web_m2x_options.search_more']);
  183. if (values.length > self.limit) {
  184. values = values.slice(0, self.limit);
  185. if (can_search_more || search_more_undef || search_more) {
  186. values.push({
  187. label: _t("Search More..."),
  188. action: function () {
  189. // limit = 80 for improving performance, similar
  190. // to Odoo implementation here:
  191. // https://github.com/odoo/odoo/commit/8c3cdce539d87775b59b3f2d5ceb433f995821bf
  192. self._rpc({
  193. model: self.field.relation,
  194. method: 'name_search',
  195. kwargs: {
  196. name: search_val,
  197. args: domain,
  198. operator: "ilike",
  199. limit: 80,
  200. context: context,
  201. },
  202. })
  203. .then(self._searchCreatePopup.bind(self, "search"));
  204. },
  205. classname: 'o_m2o_dropdown_option',
  206. });
  207. }
  208. }
  209. var create_enabled = self.can_create && !self.nodeOptions.no_create;
  210. // quick create
  211. var raw_result = _.map(result, function (x) { return x[1]; });
  212. var quick_create = self.is_option_set(self.nodeOptions.create),
  213. quick_create_undef = _.isUndefined(self.nodeOptions.create),
  214. m2x_create_undef = _.isUndefined(self.ir_options['web_m2x_options.create']),
  215. m2x_create = self.is_option_set(self.ir_options['web_m2x_options.create']);
  216. var show_create = (!self.nodeOptions && (m2x_create_undef || m2x_create)) || (self.nodeOptions && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create))));
  217. if (create_enabled && !self.nodeOptions.no_quick_create &&
  218. search_val.length > 0 && !_.contains(raw_result, search_val) &&
  219. show_create) {
  220. values.push({
  221. label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
  222. $('<span />').text(search_val).html()),
  223. action: self._quickCreate.bind(self, search_val),
  224. classname: 'o_m2o_dropdown_option'
  225. });
  226. }
  227. // create and edit ...
  228. var create_edit = self.is_option_set(self.nodeOptions.create) || self.is_option_set(self.nodeOptions.create_edit),
  229. create_edit_undef = _.isUndefined(self.nodeOptions.create) && _.isUndefined(self.nodeOptions.create_edit),
  230. m2x_create_edit_undef = _.isUndefined(self.ir_options['web_m2x_options.create_edit']),
  231. m2x_create_edit = self.is_option_set(self.ir_options['web_m2x_options.create_edit']);
  232. 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))));
  233. if (create_enabled && !self.nodeOptions.no_create_edit && show_create_edit) {
  234. var createAndEditAction = function () {
  235. // Clear the value in case the user clicks on discard
  236. self.$('input').val('');
  237. return self._searchCreatePopup("form", false, self._createContext(search_val));
  238. };
  239. values.push({
  240. label: _t("Create and Edit..."),
  241. action: createAndEditAction,
  242. classname: 'o_m2o_dropdown_option',
  243. });
  244. } else if (values.length === 0) {
  245. values.push({
  246. label: _t("No results to show..."),
  247. });
  248. }
  249. // Check if colors specified to wait for RPC
  250. if (!(self.field_color && self.colors)) {
  251. def.resolve(values);
  252. }
  253. });
  254. return def;
  255. }
  256. });
  257. FieldMany2ManyTags.include({
  258. events: _.extend({}, FieldMany2ManyTags.prototype.events, {
  259. 'click .badge': '_onOpenBadge',
  260. }),
  261. _onDeleteTag: function (event) {
  262. var result = this._super.apply(this, arguments);
  263. event.stopPropagation();
  264. return result;
  265. },
  266. is_option_set: function (option) {
  267. if (_.isUndefined(option))
  268. return false;
  269. if (typeof option === 'string')
  270. return option === 'true' || option === 'True';
  271. if (typeof option === 'boolean')
  272. return option;
  273. return false
  274. },
  275. _onOpenBadge: function (event) {
  276. var self = this;
  277. var open = (self.nodeOptions && self.is_option_set(self.nodeOptions.open));
  278. if (open) {
  279. var context = self.record.getContext(self.recordParams);
  280. var id = parseInt($(event.currentTarget).data('id'), 10);
  281. if (self.mode === 'readonly') {
  282. event.preventDefault();
  283. event.stopPropagation();
  284. self._rpc({
  285. model: self.field.relation,
  286. method: 'get_formview_action',
  287. args: [[id]],
  288. context: context,
  289. })
  290. .then(function (action) {
  291. self.trigger_up('do_action', {action: action});
  292. });
  293. }
  294. else {
  295. $.when(
  296. self._rpc({
  297. model: self.field.relation,
  298. method: 'get_formview_id',
  299. args: [[id]],
  300. context: context,
  301. }),
  302. self._rpc({
  303. model: self.field.relation,
  304. method: 'check_access_rights',
  305. kwargs: {operation: 'write', raise_exception: false}
  306. })
  307. ).then(function (view_id, write_access) {
  308. var can_write = 'can_write' in self.attrs ? JSON.parse(self.attrs.can_write) : true;
  309. new view_dialogs.FormViewDialog(self, {
  310. res_model: self.field.relation,
  311. res_id: id,
  312. context: context,
  313. title: _t("Open: ") + self.string,
  314. view_id: view_id,
  315. readonly: !can_write || !write_access,
  316. on_saved: function (record, changed) {
  317. if (changed) {
  318. self._setValue(self.value.data, {forceChange: true});
  319. self.trigger_up('reload', {db_id: self.value.id});
  320. }
  321. },
  322. }).open();
  323. })
  324. }
  325. }
  326. },
  327. });
  328. FormFieldMany2ManyTags.include({
  329. events: _.extend({}, FormFieldMany2ManyTags.prototype.events, {
  330. 'click .badge': '_onOpenBadge',
  331. }),
  332. _onOpenBadge: function (event) {
  333. var open = this.is_option_set(this.nodeOptions.open);
  334. var no_color_picker = this.is_option_set(
  335. this.nodeOptions.no_color_picker
  336. );
  337. this._super.apply(this, arguments);
  338. if (!open && !no_color_picker) {
  339. this._onOpenColorPicker(event);
  340. } else {
  341. event.preventDefault();
  342. event.stopPropagation();
  343. }
  344. },
  345. });
  346. });