screen.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. if (partner) {
  200. self.new_client = partner;
  201. self.toggle_save_button();
  202. self.display_client_details('show',partner);
  203. } else {
  204. // should never happen, because create_from_ui must return the id of the partner it
  205. // has created, and reload_partner() must have loaded the newly created partner.
  206. self.display_client_details('hide');
  207. }
  208. });
  209. },
  210. // resizes an image, keeping the aspect ratio intact,
  211. // the resize is useful to avoid sending 12Mpixels jpegs
  212. // over a wireless connection.
  213. resize_image_to_dataurl: function(img, maxwidth, maxheight, callback){
  214. img.onload = function(){
  215. var png = new Image();
  216. var canvas = document.createElement('canvas');
  217. var ctx = canvas.getContext('2d');
  218. var ratio = 1;
  219. if (img.width > maxwidth) {
  220. ratio = maxwidth / img.width;
  221. }
  222. if (img.height * ratio > maxheight) {
  223. ratio = maxheight / img.height;
  224. }
  225. var width = Math.floor(img.width * ratio);
  226. var height = Math.floor(img.height * ratio);
  227. canvas.width = width;
  228. canvas.height = height;
  229. ctx.drawImage(img,0,0,width,height);
  230. var dataurl = canvas.toDataURL();
  231. callback(dataurl);
  232. }
  233. },
  234. // Loads and resizes a File that contains an image.
  235. // callback gets a dataurl in case of success.
  236. load_image_file: function(file, callback){
  237. var self = this;
  238. if (!file.type.match(/image.*/)) {
  239. this.pos_widget.screen_selector.show_popup('error',{
  240. message:_t('Unsupported File Format'),
  241. comment:_t('Only web-compatible Image formats such as .png or .jpeg are supported'),
  242. });
  243. return;
  244. }
  245. var reader = new FileReader();
  246. reader.onload = function(event){
  247. var dataurl = event.target.result;
  248. var img = new Image();
  249. img.src = dataurl;
  250. self.resize_image_to_dataurl(img,800,600,callback);
  251. }
  252. reader.onerror = function(){
  253. self.pos_widget.screen_selector.show_popup('error',{
  254. message:_t('Could Not Read Image'),
  255. comment:_t('The provided file could not be read due to an unknown error'),
  256. });
  257. };
  258. reader.readAsDataURL(file);
  259. },
  260. // This fetches partner changes on the server, and in case of changes,
  261. // rerenders the affected views
  262. reload_partners: function(){
  263. var self = this;
  264. return this.pos.load_new_partners().then(function(){
  265. self.render_list(self.pos.db.get_partners_sorted(1000));
  266. // update the currently assigned client if it has been changed in db.
  267. var curr_client = self.pos.get_order().get_client();
  268. if (curr_client) {
  269. self.pos.get_order().set_client(self.pos.db.get_partner_by_id(curr_client.id));
  270. }
  271. });
  272. },
  273. // Shows,hides or edit the customer details box :
  274. // visibility: 'show', 'hide' or 'edit'
  275. // partner: the partner object to show or edit
  276. // clickpos: the height of the click on the list (in pixel), used
  277. // to maintain consistent scroll.
  278. display_client_details: function(visibility,partner,clickpos){
  279. var self = this;
  280. var contents = this.$('.client-details-contents');
  281. var parent = this.$('.client-list').parent();
  282. var scroll = parent.scrollTop();
  283. var height = contents.height();
  284. contents.off('click','.button.edit');
  285. contents.off('click','.button.save');
  286. contents.off('click','.button.undo');
  287. contents.on('click','.button.edit',function(){ self.edit_client_details(partner); });
  288. contents.on('click','.button.save',function(){ self.save_client_details(partner); });
  289. contents.on('click','.button.undo',function(){ self.undo_client_details(partner); });
  290. this.editing_client = false;
  291. this.uploaded_picture = null;
  292. if(visibility === 'show'){
  293. contents.empty();
  294. contents.append($(QWeb.render('ClientDetails',{widget:this,partner:partner})));
  295. var new_height = contents.height();
  296. if(!this.details_visible){
  297. if(clickpos < scroll + new_height + 20 ){
  298. parent.scrollTop( clickpos - 20 );
  299. }else{
  300. parent.scrollTop(parent.scrollTop() + new_height);
  301. }
  302. }else{
  303. parent.scrollTop(parent.scrollTop() - height + new_height);
  304. }
  305. this.details_visible = true;
  306. this.toggle_save_button();
  307. } else if (visibility === 'edit') {
  308. this.editing_client = true;
  309. contents.empty();
  310. contents.append($(QWeb.render('ClientDetailsEdit',{widget:this,partner:partner})));
  311. this.toggle_save_button();
  312. contents.find('.image-uploader').on('change',function(event){
  313. self.load_image_file(event.target.files[0],function(res){
  314. if (res) {
  315. contents.find('.client-picture img, .client-picture .fa').remove();
  316. contents.find('.client-picture').append("<img src='"+res+"'>");
  317. contents.find('.detail.picture').remove();
  318. self.uploaded_picture = res;
  319. }
  320. });
  321. });
  322. } else if (visibility === 'hide') {
  323. contents.empty();
  324. if( height > scroll ){
  325. contents.css({height:height+'px'});
  326. contents.animate({height:0},400,function(){
  327. contents.css({height:''});
  328. });
  329. }else{
  330. parent.scrollTop( parent.scrollTop() - height);
  331. }
  332. this.details_visible = false;
  333. this.toggle_save_button();
  334. }
  335. },
  336. close: function(){
  337. this._super();
  338. },
  339. });
  340. module.ReceiptScreenWidget = module.ScreenWidget.extend({
  341. template: 'ReceiptScreenWidget',
  342. show_numpad: false,
  343. show_leftpane: false,
  344. show: function(){
  345. this._super();
  346. var self = this;
  347. var print_button = this.add_action_button({
  348. label: _t('Print'),
  349. icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
  350. click: function(){ self.print(); },
  351. });
  352. var finish_button = this.add_action_button({
  353. label: _t('Next Order'),
  354. icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
  355. click: function() { self.finishOrder(); },
  356. });
  357. this.refresh();
  358. finish_button.set_disabled(true);
  359. setTimeout(function(){
  360. finish_button.set_disabled(false);
  361. }, 2000);
  362. },
  363. print: function() {
  364. this.pos.get('selectedOrder')._printed = true;
  365. window.print();
  366. },
  367. finishOrder: function() {
  368. this.pos.get('selectedOrder').destroy();
  369. },
  370. refresh: function() {
  371. var order = this.pos.get('selectedOrder');
  372. $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{
  373. widget:this,
  374. order: order,
  375. orderlines: order.get('orderLines').models,
  376. paymentlines: order.get('paymentLines').models,
  377. logo: order.pos.company_logo_base64
  378. }));
  379. },
  380. close: function(){
  381. this._super();
  382. }
  383. });
  384. }