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.

340 lines
12 KiB

  1. odoo.define('web_timeline.TimelineController', function (require) {
  2. "use strict";
  3. var AbstractController = require('web.AbstractController');
  4. var dialogs = require('web.view_dialogs');
  5. var core = require('web.core');
  6. var time = require('web.time');
  7. var Dialog = require('web.Dialog');
  8. var _t = core._t;
  9. var TimelineController = AbstractController.extend({
  10. custom_events: _.extend({}, AbstractController.prototype.custom_events, {
  11. onGroupClick: '_onGroupClick',
  12. onUpdate: '_onUpdate',
  13. onRemove: '_onRemove',
  14. onMove: '_onMove',
  15. onAdd: '_onAdd',
  16. }),
  17. /**
  18. * @constructor
  19. * @override
  20. */
  21. init: function (parent, model, renderer, params) {
  22. this._super.apply(this, arguments);
  23. this.open_popup_action = params.open_popup_action;
  24. this.date_start = params.date_start;
  25. this.date_stop = params.date_stop;
  26. this.date_delay = params.date_delay;
  27. this.context = params.actionContext;
  28. this.moveQueue = [];
  29. this.debouncedInternalMove = _.debounce(this.internalMove, 0);
  30. },
  31. /**
  32. * @override
  33. */
  34. update: function (params, options) {
  35. var res = this._super.apply(this, arguments);
  36. if (_.isEmpty(params)){
  37. return res;
  38. }
  39. var defaults = _.defaults({}, options, {
  40. adjust_window: true
  41. });
  42. var self = this;
  43. var domains = params.domain;
  44. var contexts = params.context;
  45. var group_bys = params.groupBy;
  46. this.last_domains = domains;
  47. this.last_contexts = contexts;
  48. // select the group by
  49. var n_group_bys = [];
  50. if (this.renderer.arch.attrs.default_group_by) {
  51. n_group_bys = this.renderer.arch.attrs.default_group_by.split(',');
  52. }
  53. if (group_bys.length) {
  54. n_group_bys = group_bys;
  55. }
  56. this.renderer.last_group_bys = n_group_bys;
  57. this.renderer.last_domains = domains;
  58. var fields = this.renderer.fieldNames;
  59. fields = _.uniq(fields.concat(n_group_bys));
  60. return $.when(
  61. res,
  62. self._rpc({
  63. model: self.model.modelName,
  64. method: 'search_read',
  65. kwargs: {
  66. fields: fields,
  67. domain: domains,
  68. },
  69. context: self.getSession().user_context,
  70. }).then(function (data) {
  71. return self.renderer.on_data_loaded(data, n_group_bys, defaults.adjust_window);
  72. })
  73. );
  74. },
  75. /**
  76. * Gets triggered when a group in the timeline is clicked (by the TimelineRenderer).
  77. *
  78. * @private
  79. * @returns {jQuery.Deferred}
  80. */
  81. _onGroupClick: function (event) {
  82. var groupField = this.renderer.last_group_bys[0];
  83. return this.do_action({
  84. type: 'ir.actions.act_window',
  85. res_model: this.renderer.view.fields[groupField].relation,
  86. res_id: event.data.item.group,
  87. target: 'new',
  88. views: [[false, 'form']]
  89. });
  90. },
  91. /**
  92. * Opens a form view of a clicked timeline item (triggered by the TimelineRenderer).
  93. *
  94. * @private
  95. */
  96. _onUpdate: function (event) {
  97. var self = this;
  98. this.renderer = event.data.renderer;
  99. var rights = event.data.rights;
  100. var item = event.data.item;
  101. var id = item.evt.id;
  102. var title = item.evt.__name;
  103. if (this.open_popup_action) {
  104. new dialogs.FormViewDialog(this, {
  105. res_model: this.model.modelName,
  106. res_id: parseInt(id, 10).toString() === id ? parseInt(id, 10) : id,
  107. context: this.getSession().user_context,
  108. title: title,
  109. view_id: Number(this.open_popup_action),
  110. on_saved: function () {
  111. self.write_completed();
  112. },
  113. }).open();
  114. } else {
  115. var mode = 'readonly';
  116. if (rights.write) {
  117. mode = 'edit';
  118. }
  119. this.trigger_up('switch_view', {
  120. view_type: 'form',
  121. res_id: parseInt(id, 10).toString() === id ? parseInt(id, 10) : id,
  122. mode: mode,
  123. model: this.model.modelName,
  124. });
  125. }
  126. },
  127. /**
  128. * Gets triggered when a timeline item is moved (triggered by the TimelineRenderer).
  129. *
  130. * @private
  131. */
  132. _onMove: function (event) {
  133. var item = event.data.item;
  134. var view = this.renderer.view;
  135. var fields = view.fields;
  136. var event_start = item.start;
  137. var event_end = item.end;
  138. var group = false;
  139. if (item.group !== -1) {
  140. group = item.group;
  141. }
  142. var data = {};
  143. // In case of a move event, the date_delay stay the same, only date_start and stop must be updated
  144. data[this.date_start] = time.auto_date_to_str(event_start, fields[this.date_start].type);
  145. if (this.date_stop) {
  146. // In case of instantaneous event, item.end is not defined
  147. if (event_end) {
  148. data[this.date_stop] = time.auto_date_to_str(event_end, fields[this.date_stop].type);
  149. } else {
  150. data[this.date_stop] = data[this.date_start];
  151. }
  152. }
  153. if (this.date_delay && event_end) {
  154. var diff_seconds = Math.round((event_end.getTime() - event_start.getTime()) / 1000);
  155. data[this.date_delay] = diff_seconds / 3600;
  156. }
  157. if (this.renderer.last_group_bys && this.renderer.last_group_bys instanceof Array) {
  158. data[this.renderer.last_group_bys[0]] = group;
  159. }
  160. this.moveQueue.push({
  161. id: event.data.item.id,
  162. data: data,
  163. event: event
  164. });
  165. this.debouncedInternalMove();
  166. },
  167. /**
  168. * Write enqueued moves to Odoo. After all writes are finished it updates the view once
  169. * (prevents flickering of the view when multiple timeline items are moved at once).
  170. *
  171. * @returns {jQuery.Deferred}
  172. */
  173. internalMove: function () {
  174. var self = this;
  175. var queue = this.moveQueue.slice();
  176. this.moveQueue = [];
  177. var defers = [];
  178. _.each(queue, function(item) {
  179. defers.push(self._rpc({
  180. model: self.model.modelName,
  181. method: 'write',
  182. args: [
  183. [item.event.data.item.id],
  184. item.data,
  185. ],
  186. context: self.getSession().user_context,
  187. }).then(function() {
  188. item.event.data.callback(item.event.data.item);
  189. }));
  190. });
  191. return $.when.apply($, defers).done(function() {
  192. self.write_completed({
  193. adjust_window: false
  194. });
  195. });
  196. },
  197. /**
  198. * Triggered when a timeline item gets removed from the view.
  199. * Requires user confirmation before it gets actually deleted.
  200. *
  201. * @private
  202. * @returns {jQuery.Deferred}
  203. */
  204. _onRemove: function (e) {
  205. var self = this;
  206. function do_it(event) {
  207. return self._rpc({
  208. model: self.model.modelName,
  209. method: 'unlink',
  210. args: [
  211. [event.data.item.id],
  212. ],
  213. context: self.getSession().user_context,
  214. }).then(function () {
  215. var unlink_index = false;
  216. for (var i = 0; i < self.model.data.data.length; i++) {
  217. if (self.model.data.data[i].id === event.data.item.id) {
  218. unlink_index = i;
  219. }
  220. }
  221. if (!isNaN(unlink_index)) {
  222. self.model.data.data.splice(unlink_index, 1);
  223. }
  224. event.data.callback(event.data.item);
  225. });
  226. }
  227. var message = _t("Are you sure you want to delete this record?");
  228. var def = $.Deferred();
  229. Dialog.confirm(this, message, {
  230. title: _t("Warning"),
  231. confirm_callback: function() {
  232. do_it(e)
  233. .done(def.resolve.bind(def, true))
  234. .fail(def.reject.bind(def));
  235. },
  236. });
  237. return def.promise();
  238. },
  239. /**
  240. * Triggered when a timeline item gets added and opens a form view.
  241. *
  242. * @private
  243. */
  244. _onAdd: function (event) {
  245. var self = this;
  246. var item = event.data.item;
  247. // Initialize default values for creation
  248. var default_context = {};
  249. default_context['default_'.concat(this.date_start)] = item.start;
  250. if (this.date_delay) {
  251. default_context['default_'.concat(this.date_delay)] = 1;
  252. }
  253. if (this.date_start) {
  254. default_context['default_'.concat(this.date_start)] = moment(item.start).add(1, 'hours').format(
  255. 'YYYY-MM-DD HH:mm:ss'
  256. );
  257. }
  258. if (this.date_stop && item.end) {
  259. default_context['default_'.concat(this.date_stop)] = moment(item.end).add(1, 'hours').format(
  260. 'YYYY-MM-DD HH:mm:ss'
  261. );
  262. }
  263. if (item.group > 0) {
  264. default_context['default_'.concat(this.renderer.last_group_bys[0])] = item.group;
  265. }
  266. // Show popup
  267. new dialogs.FormViewDialog(this, {
  268. res_model: this.model.modelName,
  269. res_id: null,
  270. context: _.extend(default_context, this.context),
  271. view_id: Number(this.open_popup_action),
  272. on_saved: function (record) {
  273. self.create_completed([record.res_id]);
  274. },
  275. }).open().on('closed', this, function () {
  276. event.data.callback();
  277. });
  278. return false;
  279. },
  280. /**
  281. * Triggered upon completion of a new record.
  282. * Updates the timeline view with the new record.
  283. *
  284. * @returns {jQuery.Deferred}
  285. */
  286. create_completed: function (id) {
  287. var self = this;
  288. return this._rpc({
  289. model: this.model.modelName,
  290. method: 'read',
  291. args: [
  292. id,
  293. this.model.fieldNames,
  294. ],
  295. context: this.context,
  296. })
  297. .then(function (records) {
  298. var new_event = self.renderer.event_data_transform(records[0]);
  299. var items = self.renderer.timeline.itemsData;
  300. items.add(new_event);
  301. self.renderer.timeline.setItems(items);
  302. self.reload();
  303. });
  304. },
  305. /**
  306. * Triggered upon completion of writing a record.
  307. */
  308. write_completed: function (options) {
  309. var params = {
  310. domain: this.renderer.last_domains,
  311. context: this.context,
  312. groupBy: this.renderer.last_group_bys,
  313. };
  314. this.update(params, options);
  315. },
  316. });
  317. return TimelineController;
  318. });