OCA reporting engine fork for dev and update.
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.

305 lines
12 KiB

  1. /* Copyright 2015-2018 Onestein (<http://www.onestein.eu>)
  2. * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
  3. odoo.define('bi_view_editor.FieldList', function(require) {
  4. "use strict";
  5. var core = require('web.core');
  6. var qweb = core.qweb;
  7. var Widget = require('web.Widget');
  8. var FieldListContextMenu = Widget.extend({
  9. start: function() {
  10. var res = this._super.apply(this, arguments);
  11. this.$el.mouseleave(function() {
  12. $(this).addClass('hidden');
  13. });
  14. return res;
  15. },
  16. open: function(x, y) {
  17. this.$el.css({
  18. 'left': x + 'px',
  19. 'top': y + 'px'
  20. });
  21. this.$el.removeClass('hidden');
  22. return _.extend({}, window.Backbone.Events);
  23. }
  24. });
  25. var FieldListFieldContextMenu = FieldListContextMenu.extend({
  26. template: 'bi_view_editor.FieldList.FieldContextMenu',
  27. open: function(x, y, field) {
  28. this.$el.find('.checkbox-column').prop('checked', field.column);
  29. this.$el.find('.checkbox-row').prop('checked', field.row);
  30. this.$el.find('.checkbox-measure').prop('checked', field.measure);
  31. this.$el.find('.checkbox-list').prop('checked', field.list);
  32. var measureable =
  33. field.type === "float" ||
  34. field.type === "integer" ||
  35. field.type === "monetary"
  36. ;
  37. this.$el.find('.checkbox-column').attr('disabled', measureable);
  38. this.$el.find('.checkbox-row').attr('disabled', measureable);
  39. this.$el.find('.checkbox-measure').attr('disabled', !measureable);
  40. this.$el.find('.checkbox-list').attr('disabled', false);
  41. var events = this._super(x, y, field);
  42. this.$el.find('input').unbind('change');
  43. this.$el.find('input').change(function() {
  44. var $checkbox = $(this);
  45. var property = $checkbox.attr('data-for');
  46. field[property] = $checkbox.is(':checked');
  47. events.trigger('change', field);
  48. });
  49. return events;
  50. }
  51. });
  52. var FieldListJoinContextMenu = FieldListContextMenu.extend({
  53. template: 'bi_view_editor.FieldList.JoinContextMenu',
  54. open: function(x, y, node) {
  55. this.$el.find('.checkbox-join-left').prop('checked', node.join_left);
  56. var events = this._super(x, y, node);
  57. this.$el.find('input').unbind('change');
  58. this.$el.find('input').change(function() {
  59. var $checkbox = $(this);
  60. var property = $checkbox.attr('data-for');
  61. node[property] = $checkbox.is(':checked');
  62. events.trigger('change', node);
  63. });
  64. return events;
  65. }
  66. });
  67. var FieldList = Widget.extend({
  68. template: 'bi_view_editor.FieldList',
  69. events: {
  70. 'click .delete-button': 'removeClicked',
  71. 'keyup input[name="description"]': 'keyupDescription'
  72. },
  73. start: function() {
  74. var res = this._super.apply(this, arguments);
  75. this.contextmenu = new FieldListFieldContextMenu(this);
  76. this.contextmenu.appendTo(this.$el);
  77. this.contextmenu_join = new FieldListJoinContextMenu(this);
  78. this.contextmenu_join.appendTo(this.$el);
  79. this.$table = this.$el.find('tbody');
  80. this.mode = null;
  81. return res;
  82. },
  83. setMode: function(mode) {
  84. if(mode === 'readonly') {
  85. this.$el.find('input[type="text"]').attr('disabled', true);
  86. this.$el.find(".delete-button:last").addClass('hidden');
  87. } else {
  88. this.$el.find('input[type="text"]').removeAttr('disabled');
  89. this.$el.find(".delete-button:last").removeClass('hidden');
  90. }
  91. this.mode = mode;
  92. },
  93. get: function() {
  94. return $.makeArray(this.$el.find("tbody tr").map(function () {
  95. var field = $(this).data('field');
  96. field.description = $(this).find('input[name="description"]').val();
  97. return field;
  98. }));
  99. },
  100. getModelIds: function () {
  101. var model_ids = {};
  102. this.$el.find("tbody tr").each(function () {
  103. var data = $(this).data('field');
  104. model_ids[data.table_alias] = data.model_id;
  105. });
  106. return model_ids;
  107. },
  108. getModelData: function () {
  109. var model_data = {};
  110. this.$el.find("tbody tr").each(function () {
  111. var data = $(this).data('field');
  112. model_data[data.table_alias] = {
  113. model_id: data.model_id,
  114. model_name: data.model_name
  115. };
  116. });
  117. return model_data;
  118. },
  119. add: function(field) {
  120. var self = this;
  121. field.row = typeof field.row === 'undefined' ? false : field.row;
  122. field.column = typeof field.column === 'undefined' ? false : field.column;
  123. field.measure = typeof field.measure === 'undefined' ? false : field.measure;
  124. field.list = typeof field.list === 'undefined' ? true : field.list;
  125. field._id = typeof field._id === 'undefined' ? _.uniqueId('node_') : field._id;
  126. if(field.join_node) {
  127. field.join_left = typeof field.join_left === 'undefined' ? false : field.join_left;
  128. }
  129. var i = 0;
  130. var name = field.name;
  131. while (this.get().filter(function(item) {
  132. return item.name === field.name;
  133. }).length > 0) {
  134. field.name = name + '_' + i;
  135. i++;
  136. }
  137. // Render table row
  138. var $html = $(qweb.render(field.join_node ? 'bi_view_editor.JoinListItem' : 'bi_view_editor.FieldListItem', {
  139. 'field': field
  140. })).data('field', field).contextmenu(function(e) {
  141. var $item = $(this);
  142. if(self.mode === 'readonly') {
  143. return;
  144. }
  145. e.preventDefault();
  146. self.openContextMenu($item, e.pageX, e.pageY);
  147. });
  148. this.$el.find('tbody').append($html);
  149. this.$el.find(".delete-button").addClass('hidden');
  150. this.$el.find(".delete-button:last").removeClass('hidden');
  151. this.order();
  152. },
  153. remove: function(id) {
  154. var $item = this.$el.find('tr[data-id="' + id + '"]');
  155. $item.remove();
  156. this.cleanJoinNodes();
  157. this.$el.find(".delete-button").addClass('hidden');
  158. this.$el.find(".delete-button:last").removeClass('hidden');
  159. this.trigger('removed', id);
  160. },
  161. set: function(fields) {
  162. var set_fields = fields;
  163. if (!set_fields) {
  164. set_fields = [];
  165. }
  166. this.$el.find('tbody tr').remove();
  167. for(var i = 0; i < set_fields.length; i++) {
  168. this.add(set_fields[i]);
  169. }
  170. this.$el.find(".delete-button").addClass('hidden');
  171. this.$el.find(".delete-button:last").removeClass('hidden');
  172. },
  173. openContextMenu: function($item, x, y) {
  174. var field = $item.data('field');
  175. var contextmenu = field.join_node ? this.contextmenu_join : this.contextmenu;
  176. // Temporary disable contextmenu for join node (until left join is implemented)
  177. if (field.join_node) {
  178. return;
  179. }
  180. contextmenu.open(x - 20, y - 20, $item.data('field')).on('change', function(f) {
  181. $item.data('field', f);
  182. this.refreshItem($item);
  183. this.trigger('updated');
  184. }.bind(this));
  185. },
  186. refreshItem: function($item) {
  187. var data = $item.data('field');
  188. var $attributes = $item.find('span[data-for], img[data-for]');
  189. $.each($attributes, function() {
  190. var $attribute = $(this);
  191. var value = data[$attribute.attr('data-for')];
  192. if(value) {
  193. $attribute.removeClass('hidden');
  194. } else {
  195. $attribute.addClass('hidden');
  196. }
  197. });
  198. },
  199. removeClicked: function(e) {
  200. var $button = $(e.currentTarget);
  201. var id = $button.attr('data-id');
  202. this.remove(id);
  203. },
  204. keyupDescription: function() {
  205. this.trigger('updated');
  206. },
  207. cleanJoinNodes: function() {
  208. var aliases = $.makeArray(this.$el.find("tbody tr").map(function () {
  209. var data = $(this).data('field');
  210. return data.table_alias.localeCompare(data.join_node) > 0 ? data.join_node : data.table_alias;
  211. }));
  212. this.$el.find("tbody tr").each(function () {
  213. var data = $(this).data('field');
  214. if (typeof data.join_node === 'undefined') {
  215. return;
  216. }
  217. var no_alias = data.table_alias.localeCompare(data.join_node) > 0 &&
  218. aliases.indexOf(data.table_alias) === -1;
  219. if (no_alias ||
  220. aliases.indexOf(data.join_node) === -1) {
  221. $(this).remove();
  222. }
  223. });
  224. },
  225. getOrder: function() {
  226. var items = this.get();
  227. var ordered = items.sort(function(a, b) {
  228. var res = a.table_alias.localeCompare(b.table_alias);
  229. if (res === 0) {
  230. var both_join_node = a.join_node && b.join_node;
  231. var both_not_join_node = !a.join_node && !b.join_node;
  232. if(both_join_node || both_not_join_node) {
  233. return 0;
  234. } else if(!a.join_node && b.join_node) {
  235. if(b.table_alias.localeCompare(b.join_node) > 0) {
  236. return 1;
  237. }
  238. return -1;
  239. } else if(a.join_node && !b.join_node) {
  240. if(a.table_alias.localeCompare(a.join_node) > 0) {
  241. return -1;
  242. }
  243. return 1;
  244. }
  245. }
  246. return res;
  247. });
  248. var res = [];
  249. _.each(ordered, function(item) {
  250. var already_exists = _.findIndex(res, function(f) {
  251. return f._id === item._id;
  252. }) !== -1;
  253. if(already_exists) {
  254. return;
  255. }
  256. res.push(item);
  257. if(item.join_node) {
  258. var join_node_fields = _.filter(ordered, function(f) {
  259. return f.table_alias === item.join_node && !f.join_node;
  260. });
  261. res = _.union(res, join_node_fields);
  262. }
  263. });
  264. return res;
  265. },
  266. order: function() {
  267. var order = this.getOrder();
  268. var $rows = this.$el.find("tbody tr");
  269. $rows.sort(function(a, b) {
  270. var a_index = _.findIndex(order, function(item) {
  271. return item._id === $(a).data('field')._id;
  272. });
  273. var b_index = _.findIndex(order, function(item) {
  274. return item._id === $(b).data('field')._id;
  275. });
  276. return a_index - b_index;
  277. }).appendTo(this.$el.find("tbody"));
  278. }
  279. });
  280. return {
  281. 'FieldList': FieldList,
  282. 'FieldListContextMenu': FieldListContextMenu,
  283. 'FieldListFieldContextMenu': FieldListFieldContextMenu,
  284. 'FieldListJoinContextMenu': FieldListJoinContextMenu
  285. };
  286. });