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.

320 lines
12 KiB

  1. /** ***************************************************************************
  2. Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
  3. @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
  4. License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  5. ******************************************************************************/
  6. odoo.define('pos_picking_load.widget', function (require) {
  7. "use strict";
  8. var core = require('web.core');
  9. var framework = require('web.framework');
  10. var rpc = require('web.rpc');
  11. var gui = require('point_of_sale.gui');
  12. var screens = require('point_of_sale.screens');
  13. var QWeb = core.qweb;
  14. var _t = core._t;
  15. /** **********************************************************************
  16. New ScreenWidget LoadPickingScreenWidget:
  17. * On show, display all pickings;
  18. * on click on a picking, display the content;
  19. * on click on 'validate', allow to use this picking;
  20. * on click on 'cancel', display the preview screen;
  21. */
  22. var LoadPickingScreenWidget = screens.ScreenWidget.extend({
  23. template: 'LoadPickingScreenWidget',
  24. auto_back: true,
  25. current_picking_id: false,
  26. current_picking_name: false,
  27. show: function () {
  28. var self = this;
  29. this._super();
  30. this.renderElement();
  31. // Bind functions
  32. this.$('.back').click(_.bind(this.clickBackButton, this));
  33. this.$('.validate').click(_.bind(this.clickValidateButton, this));
  34. // Initialize display
  35. this.$('.validate').hide();
  36. this.search_pickings();
  37. this.$('.picking-list-contents').delegate(
  38. '.picking-line', 'click', function (event) {
  39. self.select_picking(event);
  40. });
  41. // Handle search
  42. var search_timeout = null;
  43. this.$('.searchbox input').on('keyup', function () {
  44. clearTimeout(search_timeout);
  45. var query = this.value;
  46. search_timeout = setTimeout(function () {
  47. self.perform_search(query);
  48. }, 70);
  49. });
  50. this.$('.searchbox .search-clear').click(function () {
  51. self.clear_search();
  52. });
  53. },
  54. select_picking: function (event) {
  55. var origin_picking_id = parseInt(
  56. event.target.parentNode.dataset.pickingId, 10);
  57. var self = this;
  58. this.current_picking_data = false;
  59. this.$('span.button.validate').hide();
  60. this.$('.picking-list .highlight').removeClass('highlight');
  61. framework.blockUI();
  62. var params = {
  63. model: 'stock.picking',
  64. method: 'load_picking_for_pos',
  65. args: [origin_picking_id],
  66. };
  67. rpc.query(params)
  68. .then(function (picking_data) {
  69. framework.unblockUI();
  70. if (self.check_picking(picking_data)) {
  71. self.current_picking_data = picking_data;
  72. $(event.target.parentNode).addClass('highlight');
  73. self.$('span.button.validate').show();
  74. }
  75. }).fail(function (error, fail_event) {
  76. framework.unblockUI();
  77. self.handle_errors(error, fail_event);
  78. });
  79. },
  80. check_picking: function (picking_data) {
  81. var self = this;
  82. var picking_selectable = true;
  83. // Forbid POS Order loading if some products are unknown
  84. var unknown_products = [];
  85. picking_data.line_ids.forEach(function (picking_line_data) {
  86. var line_name = picking_line_data.name.replace('\n', ' ');
  87. var product = self.pos.db.get_product_by_id(
  88. picking_line_data.product_id);
  89. if (_.isUndefined(product)) {
  90. unknown_products.push(line_name);
  91. }
  92. });
  93. if (unknown_products.length > 0) {
  94. self.gui.show_popup(
  95. 'error-traceback', {
  96. 'title': _t('Unknown Products'),
  97. 'body': _t(
  98. "Unable to load some picking lines because the" +
  99. " products are not available in the POS" +
  100. " cache.\n\n" +
  101. "Please check that lines :\n\n * ") +
  102. unknown_products.join("; \n *"),
  103. });
  104. picking_selectable = false;
  105. }
  106. // Check if the partner is unknown
  107. var partner = self.pos.db.get_partner_by_id(
  108. picking_data.partner_id);
  109. if (_.isUndefined(partner)) {
  110. self.gui.show_popup(
  111. 'error-traceback', {
  112. 'title': _t('Unknown Partner'),
  113. 'body': _t(
  114. "Unable to load this picking because the partner" +
  115. " is not known in the Point Of Sale" +
  116. " as a customer"),
  117. });
  118. picking_selectable = false;
  119. }
  120. // Check if the picking is still loaded in another PoS tab
  121. self.pos.db.get_unpaid_orders().forEach(function (order) {
  122. if (order.origin_picking_id === picking_data.id) {
  123. self.gui.show_popup(
  124. 'error-traceback', {
  125. 'title': _t('Picking Still Loaded'),
  126. 'body': _t(
  127. "Unable to load this picking because it has" +
  128. " been loaded in another draft" +
  129. " PoS Order :\n\n") +
  130. order.name,
  131. });
  132. picking_selectable = false;
  133. }
  134. });
  135. // Check if the picking has still been handled in another PoS Order
  136. self.pos.db.get_orders().forEach(function (order) {
  137. if (order.origin_picking_id === picking_data.id) {
  138. self.gui.show_popup(
  139. 'error-traceback', {
  140. 'title': _t('Picking Still Loaded'),
  141. 'body': _t(
  142. "Unable to load this picking because it has" +
  143. " been loaded in another confirmed" +
  144. " PoS Order :\n\n") +
  145. order.name,
  146. });
  147. picking_selectable = false;
  148. }
  149. });
  150. return picking_selectable;
  151. },
  152. perform_search: function (query) {
  153. this.search_pickings(query);
  154. },
  155. clear_search: function () {
  156. this.search_pickings();
  157. this.$('.searchbox input')[0].value = '';
  158. this.$('.searchbox input').focus();
  159. },
  160. search_pickings: function (query) {
  161. var self = this;
  162. var params = {
  163. model: 'stock.picking',
  164. method: 'search_pickings_for_pos',
  165. args: [query || '', this.pos.pos_session.id],
  166. };
  167. rpc.query(params)
  168. .then(function (result) {
  169. self.render_list(result);
  170. }).fail(function (error, event) {
  171. self.handle_errors(error, event);
  172. });
  173. },
  174. render_list: function (pickings) {
  175. var contents = this.$el[0].querySelector('.picking-list-contents');
  176. contents.innerHTML = "";
  177. var line_list = document.createDocumentFragment();
  178. _.each(pickings, function (picking) {
  179. var picking_line_html = QWeb.render(
  180. 'LoadPickingLine', {widget: this, picking:picking});
  181. var picking_line = document.createElement('tbody');
  182. picking_line.innerHTML = picking_line_html;
  183. picking_line = picking_line.childNodes[1];
  184. line_list.appendChild(picking_line);
  185. });
  186. contents.appendChild(line_list);
  187. },
  188. // User Event
  189. clickBackButton: function () {
  190. this.gui.back();
  191. },
  192. clickValidateButton: function () {
  193. var order = this.pos.get_order();
  194. order.load_from_picking_data(this.current_picking_data);
  195. this.gui.show_screen('products');
  196. },
  197. handle_errors: function (error, event) {
  198. var self = this;
  199. if (parseInt(error.code, 10) === 200) {
  200. // Business Logic Error, not a connection problem
  201. self.gui.show_popup('error-traceback', {
  202. 'title': error.data.message || _t("Server Error"),
  203. 'body': error.data.debug || _t(
  204. "The server encountered an error while" +
  205. " receiving your order."),
  206. });
  207. } else {
  208. self.gui.show_popup('error', {
  209. 'title': _t('Connection error'),
  210. 'body': _t(
  211. "Can not execute this action because the POS" +
  212. " is currently offline"),
  213. });
  214. }
  215. event.preventDefault();
  216. },
  217. });
  218. gui.define_screen({
  219. 'name': 'load_picking',
  220. 'widget': LoadPickingScreenWidget,
  221. 'condition': function () {
  222. return this.pos.config.iface_load_picking;
  223. },
  224. });
  225. /** **********************************************************************
  226. New Widget LoadPickingButtonWidget:
  227. * On click, display a new screen to select a picking;
  228. */
  229. var LoadPickingButtonWidget = screens.ActionButtonWidget.extend({
  230. template: 'LoadPickingButtonWidget',
  231. button_click: function () {
  232. if (_.isUndefined(this.pos.get_order().get('origin_picking_id'))) {
  233. this.gui.show_screen('load_picking');
  234. } else {
  235. this.gui.show_popup('error', {
  236. 'title': _t('Pending Picking'),
  237. 'body': _t(
  238. "A picking is still loaded. You can not load" +
  239. " another picking. Please create a new order."),
  240. });
  241. }
  242. },
  243. button_text: function () {
  244. if (! this.pos.get_order() ||
  245. _.isUndefined(
  246. this.pos.get_order().get('origin_picking_id'))) {
  247. return _t("Load Picking");
  248. }
  249. return this.pos.get_order().get('origin_picking_name');
  250. },
  251. is_visible: function () {
  252. if (this.pos.get_order()) {
  253. return (
  254. this.pos.get_order().get_orderlines().length === 0 ||
  255. ! _.isUndefined(
  256. this.pos.get_order().get('origin_picking_id')));
  257. }
  258. return false;
  259. },
  260. });
  261. screens.define_action_button({
  262. 'name': 'load_picking',
  263. 'widget': LoadPickingButtonWidget,
  264. 'condition': function () {
  265. return this.pos.config.iface_load_picking;
  266. },
  267. });
  268. screens.OrderWidget.include({
  269. update_summary: function () {
  270. this._super();
  271. if (this.getParent().action_buttons &&
  272. this.getParent().action_buttons.load_picking) {
  273. this.getParent().action_buttons.load_picking.renderElement();
  274. }
  275. },
  276. });
  277. });