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.

385 lines
14 KiB

  1. /******************************************************************************
  2. * Point Of Sale - Product Template module for Odoo
  3. * Copyright (C) 2014-Today Akretion (http://www.akretion.com)
  4. * @author Sylvain Calador (sylvain.calador@akretion.com)
  5. * @author Sylvain Le Gal (https://twitter.com/legalsylvain)
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as
  8. * published by the Free Software Foundation, either version 3 of the
  9. * License, or (at your option) any later version.
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *****************************************************************************/
  17. odoo.define('pos_order_load', function (require) {
  18. "use strict";
  19. var PosBaseWidget = require('point_of_sale.BaseWidget');
  20. var chrome = require('point_of_sale.chrome');
  21. var gui = require('point_of_sale.gui');
  22. var models = require('point_of_sale.models');
  23. var screens = require('point_of_sale.screens');
  24. var rpc = require('web.rpc');
  25. var utils = require('web.utils');
  26. var round_pr = utils.round_precision;
  27. var core = require('web.core');
  28. var QWeb = core.qweb;
  29. var _t = core._t;
  30. /*************************************************************************
  31. Extend Model Order:
  32. * Add getter and setter function for field 'order_id';
  33. */
  34. var _super_order = models.Order.prototype;
  35. models.Order = models.Order.extend({
  36. set_order_id: function(id) {
  37. this.set({
  38. order_id: id,
  39. });
  40. },
  41. get_order_id: function() {
  42. return this.get('order_id');
  43. },
  44. });
  45. /*************************************************************************
  46. New Widget LoadButtonWidget:
  47. * On click, display a new screen to select draft orders;
  48. */
  49. var LoadButtonWidget = PosBaseWidget.extend({
  50. template: 'LoadButtonWidget',
  51. renderElement: function() {
  52. var self = this;
  53. this._super();
  54. this.$el.click(function(){
  55. self.gui.show_screen('orderlist');
  56. });
  57. },
  58. });
  59. /*************************************************************************
  60. New Widget SaveButtonWidget:
  61. * On click, save the current draft order;
  62. */
  63. var SaveButtonWidget = PosBaseWidget.extend({
  64. template: 'SaveButtonWidget',
  65. renderElement: function() {
  66. var self = this;
  67. this._super();
  68. this.$el.click(function(){
  69. self.gui.show_popup('confirm',{
  70. 'title': _t('Save The current Order ?'),
  71. 'body': _t('This operation will save the current order in a draft state. You\'ll have to mark it as paid after.'),
  72. confirm: function(){
  73. var currentOrder = this.pos.get('selectedOrder');
  74. this.pos.push_order(currentOrder);
  75. self.pos.get('selectedOrder').destroy();
  76. },
  77. });
  78. });
  79. },
  80. });
  81. /*************************************************************************
  82. Extend PosWidget:
  83. * Create new screen;
  84. * Add load and save button;
  85. */
  86. chrome.Chrome.include({
  87. build_widgets: function() {
  88. this._super();
  89. this.load_button = new LoadButtonWidget(this, {});
  90. this.load_button.appendTo(this.$('div.order-empty'));
  91. this.save_button = new SaveButtonWidget(this, {});
  92. },
  93. });
  94. /*************************************************************************
  95. * Extend OrderWidget:
  96. */
  97. screens.OrderWidget.include({
  98. renderElement: function(scrollbottom){
  99. this._super(scrollbottom);
  100. if (this.chrome.load_button) {
  101. this.chrome.load_button.appendTo(
  102. this.chrome.$('div.order-empty')
  103. );
  104. }
  105. if (this.pos.get_order()) {
  106. if (this.chrome.save_button && (this.pos.get_order().get_orderlines().length > 0)) {
  107. this.chrome.save_button.appendTo(
  108. this.chrome.$('div.summary')
  109. );
  110. }
  111. }
  112. }
  113. });
  114. /*************************************************************************
  115. * New ScreenWidget OrderListScreenWidget:
  116. * On show, display all draft orders;
  117. * on click on an order, display the content;
  118. * on click on 'validate', allow to use this POS Order;
  119. * on click on 'cancel', display the preview screen;
  120. */
  121. //
  122. var OrderListScreenWidget = screens.ScreenWidget.extend({
  123. template: 'OrderListScreenWidget',
  124. show_leftpane: true,
  125. model: 'pos.order',
  126. current_order_id: 0,
  127. init: function(parent, options){
  128. this._super(parent, options);
  129. },
  130. reset_order: function(order) {
  131. order.set_client(undefined);
  132. order.set_order_id(undefined);
  133. order.orderlines.reset();
  134. return order;
  135. },
  136. start: function() {
  137. var self = this;
  138. this._super();
  139. this.$el.find('span.button.back').click(function(){
  140. var order = self.pos.get('selectedOrder');
  141. self.reset_order(order);
  142. self.chrome.screens.products.order_widget.change_selected_order();
  143. self.gui.show_screen('products');
  144. });
  145. this.$el.find('span.button.validate').click(function(){
  146. var orderModel = rpc.query({
  147. model: 'pos.order',
  148. method: 'unlink',
  149. args: [self.current_order_id],
  150. });
  151. return orderModel.then(function (result) {
  152. self.gui.show_screen('products');
  153. }).fail(function (error, event){
  154. if (parseInt(error.code) === 200) {
  155. // Business Logic Error, not a connection problem
  156. self.gui.show_popup(
  157. 'error-traceback', {
  158. message: error.data.message,
  159. comment: error.data.debug
  160. });
  161. }
  162. else{
  163. self.gui.show_popup('error',{
  164. message: _t('Connection error'),
  165. comment: _t('Can not load the Selected Order because the POS is currently offline'),
  166. });
  167. }
  168. event.preventDefault();
  169. });
  170. });
  171. var search_timeout = null;
  172. this.$('.searchbox input').on('keyup',function(event){
  173. clearTimeout(search_timeout);
  174. var query = this.value;
  175. search_timeout = setTimeout(function(){
  176. self.perform_search(query);
  177. },70);
  178. });
  179. this.$('.searchbox .search-clear').click(function(){
  180. self.clear_search();
  181. });
  182. },
  183. // to override if necessary
  184. add_product_attribute: function(product, key, orderline){
  185. return product;
  186. },
  187. load_order_fields: function(order, fields) {
  188. order.set_order_id(fields.id);
  189. var partner = this.pos.db.get_partner_by_id(
  190. fields.partner_id);
  191. order.set_client(partner || undefined);
  192. return order;
  193. },
  194. prepare_orderline_options: function(orderline) {
  195. return {
  196. quantity: orderline.qty,
  197. price: orderline.price_unit,
  198. discount: orderline.discount,
  199. };
  200. },
  201. load_order: function(order_id) {
  202. var self = this;
  203. var orderModel = rpc.query({
  204. model: this.model,
  205. method: 'load_order',
  206. args: [order_id],
  207. });
  208. return orderModel.then(function (result) {
  209. var order = self.pos.get('selectedOrder');
  210. order = self.load_order_fields(order, result);
  211. order.orderlines.reset();
  212. var orderlines = result.orderlines || [];
  213. var unknown_products = [];
  214. for (var i=0, len=orderlines.length; i<len; i++) {
  215. var orderline = orderlines[i];
  216. var product_id = orderline.product_id[0];
  217. var product_name = orderline.product_id[1];
  218. var product = self.pos.db.get_product_by_id(product_id);
  219. if (_.isUndefined(product)) {
  220. unknown_products.push(product_name);
  221. continue;
  222. }
  223. for (var key in orderline) {
  224. if (!key.indexOf('product__')) {
  225. product = self.add_product_attribute(
  226. product, key, orderline
  227. );
  228. }
  229. }
  230. order.add_product(product,
  231. self.prepare_orderline_options(orderline)
  232. );
  233. var last_orderline = order.get_last_orderline();
  234. last_orderline = jQuery.extend(last_orderline, orderline);
  235. }
  236. // Forbid POS Order loading if some products are unknown
  237. if (unknown_products.length > 0){
  238. self.gui.show_popup(
  239. 'error-traceback', {
  240. message: _t('Unknown Products'),
  241. comment: _t('Unable to load some order lines because the ' +
  242. 'products are not available in the POS cache.\n\n' +
  243. 'Please check that lines :\n\n * ') + unknown_products.join("; \n *")
  244. });
  245. self.$el.find('span.button.validate').hide();
  246. }
  247. else{
  248. self.$el.find('span.button.validate').show();
  249. }
  250. }).fail(function (error, event){
  251. if (parseInt(error.code) === 200) {
  252. // Business Logic Error, not a connection problem
  253. self.gui.show_popup(
  254. 'error-traceback', {
  255. message: error.data.message,
  256. comment: error.data.debug
  257. });
  258. }
  259. else{
  260. self.gui.show_popup('error',{
  261. message: _t('Connection error'),
  262. comment: _t('Can not execute this action because the POS is currently offline'),
  263. });
  264. }
  265. event.preventDefault();
  266. });
  267. },
  268. load_orders: function(query) {
  269. var self = this;
  270. var orderModel = rpc.query({
  271. model: this.model,
  272. method: 'search_read_orders',
  273. args: [query || ''],
  274. })
  275. return orderModel.then(function (result) {
  276. self.render_list(result);
  277. }).fail(function (error, event){
  278. if (parseInt(error.code) === 200) {
  279. // Business Logic Error, not a connection problem
  280. self.gui.show_popup(
  281. 'error-traceback', {
  282. message: error.data.message,
  283. comment: error.data.debug
  284. }
  285. );
  286. }
  287. else{
  288. self.gui.show_popup('error',{
  289. message: _t('Connection error'),
  290. comment: _t('Can not execute this action because the POS is currently offline'),
  291. });
  292. }
  293. event.preventDefault();
  294. });
  295. },
  296. show: function() {
  297. this._super();
  298. if (this.gui.get_current_screen() == 'orderlist') {
  299. this.load_orders();
  300. }
  301. },
  302. on_click_draft_order: function(event){
  303. this.$('.order-list .highlight').removeClass('highlight');
  304. this.current_order_id = parseInt(event.target.parentNode.dataset.orderId);
  305. this.load_order(this.current_order_id);
  306. $(event.target.parentNode).addClass('highlight');
  307. },
  308. render_list: function(orders){
  309. var self = this;
  310. var contents = this.$el[0].querySelector('.order-list-contents');
  311. contents.innerHTML = "";
  312. for (var i = 0, len = orders.length; i < len; i++){
  313. var order = orders[i];
  314. var orderline_html = QWeb.render('LoadOrderLine',
  315. {widget: this, order:orders[i]});
  316. var orderline = document.createElement('tbody');
  317. orderline.innerHTML = orderline_html;
  318. orderline = orderline.childNodes[1];
  319. orderline.addEventListener('click', this.on_click_draft_order);
  320. contents.appendChild(orderline);
  321. }
  322. },
  323. perform_search: function(query){
  324. this.load_orders(query);
  325. },
  326. clear_search: function(){
  327. this.load_orders();
  328. this.$('.searchbox input')[0].value = '';
  329. this.$('.searchbox input').focus();
  330. },
  331. });
  332. gui.define_screen({'name': 'orderlist', 'widget': OrderListScreenWidget});
  333. return {
  334. LoadButtonButton: LoadButtonWidget,
  335. SaveButtonButton: SaveButtonWidget,
  336. OrderListScreenWidget: OrderListScreenWidget,
  337. };
  338. });