screen.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. function pos_screens(instance, module){ //module is instance.point_of_sale
  2. var QWeb = instance.web.qweb,
  3. _t = instance.web._t;
  4. var round_pr = instance.web.round_precision
  5. module.ClientListScreenWidget = module.ScreenWidget.extend({
  6. template: 'ClientListScreenWidget',
  7. init: function(parent, options){
  8. this._super(parent, options);
  9. },
  10. show_leftpane: false,
  11. auto_back: true,
  12. show: function(){
  13. var self = this;
  14. this._super();
  15. this.renderElement();
  16. this.details_visible = false;
  17. this.old_client = this.pos.get('selectedOrder').get('client');
  18. this.new_client = this.old_client;
  19. this.$('.back').click(function(){
  20. self.pos_widget.screen_selector.back();
  21. });
  22. this.$('.next').click(function(){
  23. self.save_changes();
  24. self.pos_widget.screen_selector.back();
  25. });
  26. this.$('.new-customer').click(function(){
  27. self.display_client_details('edit',{
  28. 'country_id': self.pos.company.country_id,
  29. });
  30. });
  31. var partners = this.pos.db.get_partners_sorted(1000);
  32. this.render_list(partners);
  33. this.reload_partners();
  34. if( this.old_client ){
  35. this.display_client_details('show',this.old_client,0);
  36. }
  37. this.$('.client-list-contents').delegate('.client-line','click',function(event){
  38. self.line_select(event,$(this),parseInt($(this).data('id')));
  39. });
  40. var search_timeout = null;
  41. if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
  42. this.pos_widget.onscreen_keyboard.connect(this.$('.searchbox input'));
  43. }
  44. this.$('.searchbox input').on('keyup',function(event){
  45. clearTimeout(search_timeout);
  46. var query = this.value;
  47. search_timeout = setTimeout(function(){
  48. self.perform_search(query,event.which === 13);
  49. },70);
  50. });
  51. this.$('.searchbox .search-clear').click(function(){
  52. self.clear_search();
  53. });
  54. },
  55. barcode_client_action: function(code){
  56. if (this.editing_client) {
  57. this.$('.detail.barcode').val(code.code);
  58. } else if (this.pos.db.get_partner_by_ean13(code.code)) {
  59. this.display_client_details('show',this.pos.db.get_partner_by_ean13(code.code));
  60. }
  61. },
  62. perform_search: function(query, associate_result){
  63. var customers;
  64. if(query){
  65. customers = this.pos.db.search_partner(query);
  66. this.display_client_details('hide');
  67. if ( associate_result && customers.length === 1){
  68. this.new_client = customers[0];
  69. this.save_changes();
  70. this.pos_widget.screen_selector.back();
  71. }
  72. this.render_list(customers);
  73. }else{
  74. customers = this.pos.db.get_partners_sorted();
  75. this.render_list(customers);
  76. }
  77. },
  78. clear_search: function(){
  79. var customers = this.pos.db.get_partners_sorted(1000);
  80. this.render_list(customers);
  81. this.$('.searchbox input')[0].value = '';
  82. this.$('.searchbox input').focus();
  83. },
  84. render_list: function(partners){
  85. var contents = this.$el[0].querySelector('.client-list-contents');
  86. contents.innerHTML = "";
  87. for(var i = 0, len = Math.min(partners.length,1000); i < len; i++){
  88. var partner = partners[i];
  89. var clientline_html = QWeb.render('ClientLine',{widget: this, partner:partners[i]});
  90. var clientline = document.createElement('tbody');
  91. clientline.innerHTML = clientline_html;
  92. clientline = clientline.childNodes[1];
  93. if( partners === this.new_client ){
  94. clientline.classList.add('highlight');
  95. }else{
  96. clientline.classList.remove('highlight');
  97. }
  98. contents.appendChild(clientline);
  99. }
  100. },
  101. save_changes: function(){
  102. if( this.has_client_changed() ){
  103. this.pos.get('selectedOrder').set_client(this.new_client);
  104. }
  105. },
  106. has_client_changed: function(){
  107. if( this.old_client && this.new_client ){
  108. return this.old_client.id !== this.new_client.id;
  109. }else{
  110. return !!this.old_client !== !!this.new_client;
  111. }
  112. },
  113. toggle_save_button: function(){
  114. var $button = this.$('.button.next');
  115. if (this.editing_client) {
  116. $button.addClass('oe_hidden');
  117. return;
  118. } else if( this.new_client ){
  119. if( !this.old_client){
  120. $button.text(_t('Set Customer'));
  121. }else{
  122. $button.text(_t('Change Customer'));
  123. }
  124. }else{
  125. $button.text(_t('Deselect Customer'));
  126. }
  127. $button.toggleClass('oe_hidden',!this.has_client_changed());
  128. },
  129. line_select: function(event,$line,id){
  130. var partner = this.pos.db.get_partner_by_id(id);
  131. this.$('.client-list .lowlight').removeClass('lowlight');
  132. if ( $line.hasClass('highlight') ){
  133. $line.removeClass('highlight');
  134. $line.addClass('lowlight');
  135. this.display_client_details('hide',partner);
  136. this.new_client = null;
  137. this.toggle_save_button();
  138. }else{
  139. this.$('.client-list .highlight').removeClass('highlight');
  140. $line.addClass('highlight');
  141. var y = event.pageY - $line.parent().offset().top
  142. this.display_client_details('show',partner,y);
  143. this.new_client = partner;
  144. this.toggle_save_button();
  145. }
  146. },
  147. partner_icon_url: function(id){
  148. return '/web/binary/image?model=res.partner&id='+id+'&field=image_small';
  149. },
  150. // ui handle for the 'edit selected customer' action
  151. edit_client_details: function(partner) {
  152. this.display_client_details('edit',partner);
  153. },
  154. // ui handle for the 'cancel customer edit changes' action
  155. undo_client_details: function(partner) {
  156. if (!partner.id) {
  157. this.display_client_details('hide');
  158. } else {
  159. this.display_client_details('show',partner);
  160. }
  161. },
  162. // what happens when we save the changes on the client edit form -> we fetch the fields, sanitize them,
  163. // send them to the backend for update, and call saved_client_details() when the server tells us the
  164. // save was successfull.
  165. save_client_details: function(partner) {
  166. var self = this;
  167. var fields = {}
  168. this.$('.client-details-contents .detail').each(function(idx,el){
  169. fields[el.name] = el.value;
  170. });
  171. if (!fields.name) {
  172. this.pos_widget.screen_selector.show_popup('error',{
  173. message: _t('A Customer Name Is Required'),
  174. });
  175. return;
  176. }
  177. if (this.uploaded_picture) {
  178. fields.image = this.uploaded_picture;
  179. }
  180. fields.id = partner.id || false;
  181. fields.country_id = fields.country_id || false;
  182. fields.ean13 = fields.ean13 ? this.pos.barcode_reader.sanitize_ean(fields.ean13) : false;
  183. new instance.web.Model('res.partner').call('create_from_ui',[fields]).then(function(partner_id){
  184. self.saved_client_details(partner_id, fields);
  185. },function(err,event){
  186. event.preventDefault();
  187. self.pos_widget.screen_selector.show_popup('error',{
  188. 'message':_t('Error: Could not Save Changes'),
  189. 'comment':_t('Your Internet connection is probably down.'),
  190. });
  191. });
  192. },
  193. // what happens when we've just pushed modifications for a partner of id partner_id
  194. saved_client_details: function(partner_id, fields){
  195. var self = this;
  196. this.reload_partners().then(function(){
  197. var partner = self.pos.db.get_partner_by_id(partner_id);
  198. partner['ruc'] = fields.ruc;
  199. partner['mobile'] = fields.mobile;
  200. partner['street'] = fields.street;
  201. if (partner) {
  202. self.new_client = partner;
  203. self.toggle_save_button();
  204. self.display_client_details('show',partner);
  205. } else {
  206. // should never happen, because create_from_ui must return the id of the partner it
  207. // has created, and reload_partner() must have loaded the newly created partner.
  208. self.display_client_details('hide');
  209. }
  210. });
  211. },
  212. // resizes an image, keeping the aspect ratio intact,
  213. // the resize is useful to avoid sending 12Mpixels jpegs
  214. // over a wireless connection.
  215. resize_image_to_dataurl: function(img, maxwidth, maxheight, callback){
  216. img.onload = function(){
  217. var png = new Image();
  218. var canvas = document.createElement('canvas');
  219. var ctx = canvas.getContext('2d');
  220. var ratio = 1;
  221. if (img.width > maxwidth) {
  222. ratio = maxwidth / img.width;
  223. }
  224. if (img.height * ratio > maxheight) {
  225. ratio = maxheight / img.height;
  226. }
  227. var width = Math.floor(img.width * ratio);
  228. var height = Math.floor(img.height * ratio);
  229. canvas.width = width;
  230. canvas.height = height;
  231. ctx.drawImage(img,0,0,width,height);
  232. var dataurl = canvas.toDataURL();
  233. callback(dataurl);
  234. }
  235. },
  236. // Loads and resizes a File that contains an image.
  237. // callback gets a dataurl in case of success.
  238. load_image_file: function(file, callback){
  239. var self = this;
  240. if (!file.type.match(/image.*/)) {
  241. this.pos_widget.screen_selector.show_popup('error',{
  242. message:_t('Unsupported File Format'),
  243. comment:_t('Only web-compatible Image formats such as .png or .jpeg are supported'),
  244. });
  245. return;
  246. }
  247. var reader = new FileReader();
  248. reader.onload = function(event){
  249. var dataurl = event.target.result;
  250. var img = new Image();
  251. img.src = dataurl;
  252. self.resize_image_to_dataurl(img,800,600,callback);
  253. }
  254. reader.onerror = function(){
  255. self.pos_widget.screen_selector.show_popup('error',{
  256. message:_t('Could Not Read Image'),
  257. comment:_t('The provided file could not be read due to an unknown error'),
  258. });
  259. };
  260. reader.readAsDataURL(file);
  261. },
  262. // This fetches partner changes on the server, and in case of changes,
  263. // rerenders the affected views
  264. reload_partners: function(){
  265. var self = this;
  266. return this.pos.load_new_partners().then(function(){
  267. self.render_list(self.pos.db.get_partners_sorted(1000));
  268. // update the currently assigned client if it has been changed in db.
  269. var curr_client = self.pos.get_order().get_client();
  270. if (curr_client) {
  271. self.pos.get_order().set_client(self.pos.db.get_partner_by_id(curr_client.id));
  272. }
  273. });
  274. },
  275. // Shows,hides or edit the customer details box :
  276. // visibility: 'show', 'hide' or 'edit'
  277. // partner: the partner object to show or edit
  278. // clickpos: the height of the click on the list (in pixel), used
  279. // to maintain consistent scroll.
  280. display_client_details: function(visibility,partner,clickpos){
  281. var self = this;
  282. var contents = this.$('.client-details-contents');
  283. var parent = this.$('.client-list').parent();
  284. var scroll = parent.scrollTop();
  285. var height = contents.height();
  286. contents.off('click','.button.edit');
  287. contents.off('click','.button.save');
  288. contents.off('click','.button.undo');
  289. contents.on('click','.button.edit',function(){ self.edit_client_details(partner); });
  290. contents.on('click','.button.save',function(){ self.save_client_details(partner); });
  291. contents.on('click','.button.undo',function(){ self.undo_client_details(partner); });
  292. this.editing_client = false;
  293. this.uploaded_picture = null;
  294. if(visibility === 'show'){
  295. contents.empty();
  296. contents.append($(QWeb.render('ClientDetails',{widget:this,partner:partner})));
  297. var new_height = contents.height();
  298. if(!this.details_visible){
  299. if(clickpos < scroll + new_height + 20 ){
  300. parent.scrollTop( clickpos - 20 );
  301. }else{
  302. parent.scrollTop(parent.scrollTop() + new_height);
  303. }
  304. }else{
  305. parent.scrollTop(parent.scrollTop() - height + new_height);
  306. }
  307. this.details_visible = true;
  308. this.toggle_save_button();
  309. } else if (visibility === 'edit') {
  310. this.editing_client = true;
  311. contents.empty();
  312. contents.append($(QWeb.render('ClientDetailsEdit',{widget:this,partner:partner})));
  313. this.toggle_save_button();
  314. contents.find('.image-uploader').on('change',function(event){
  315. self.load_image_file(event.target.files[0],function(res){
  316. if (res) {
  317. contents.find('.client-picture img, .client-picture .fa').remove();
  318. contents.find('.client-picture').append("<img src='"+res+"'>");
  319. contents.find('.detail.picture').remove();
  320. self.uploaded_picture = res;
  321. }
  322. });
  323. });
  324. } else if (visibility === 'hide') {
  325. contents.empty();
  326. if( height > scroll ){
  327. contents.css({height:height+'px'});
  328. contents.animate({height:0},400,function(){
  329. contents.css({height:''});
  330. });
  331. }else{
  332. parent.scrollTop( parent.scrollTop() - height);
  333. }
  334. this.details_visible = false;
  335. this.toggle_save_button();
  336. }
  337. },
  338. close: function(){
  339. this._super();
  340. },
  341. });
  342. module.ReceiptScreenWidget = module.ScreenWidget.extend({
  343. template: 'ReceiptScreenWidget',
  344. show_numpad: false,
  345. show_leftpane: false,
  346. show: function(){
  347. this._super();
  348. var self = this;
  349. var print_button = this.add_action_button({
  350. label: _t('Print'),
  351. icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
  352. click: function(){ self.print(); },
  353. });
  354. var finish_button = this.add_action_button({
  355. label: _t('Next Order'),
  356. icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
  357. click: function() { self.finishOrder(); },
  358. });
  359. this.refresh();
  360. finish_button.set_disabled(true);
  361. setTimeout(function(){
  362. finish_button.set_disabled(false);
  363. }, 2000);
  364. },
  365. print: function() {
  366. this.pos.get('selectedOrder')._printed = true;
  367. window.print();
  368. },
  369. finishOrder: function() {
  370. this.pos.get('selectedOrder').destroy();
  371. },
  372. refresh: function() {
  373. var order = this.pos.get('selectedOrder');
  374. $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{
  375. widget:this,
  376. order: order,
  377. orderlines: order.get('orderLines').models,
  378. paymentlines: order.get('paymentLines').models,
  379. logo: order.pos.company_logo_base64
  380. }));
  381. },
  382. close: function(){
  383. this._super();
  384. }
  385. });
  386. }