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.

280 lines
10 KiB

8 years ago
8 years ago
8 years ago
  1. /* Copyright 2015 igallyamov <https://github.com/igallyamov>
  2. Copyright 2016 ufaks <https://github.com/ufaks>
  3. Copyright 2016 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
  4. Copyright 2019 Kolushov Alexandr <https://it-projects.info/team/kolushovalexandr>
  5. Copyright 2019 ssaid <https://github.com/ssaid>
  6. Copyright 2019 raulovallet <https://github.com/raulovallet>
  7. License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). */
  8. /* eslint-disable complexity */
  9. odoo.define('pos_keyboard.pos', function (require) {
  10. "use strict";
  11. var core = require('web.core');
  12. var gui = require('point_of_sale.gui');
  13. var models = require('point_of_sale.models');
  14. var screens = require('point_of_sale.screens');
  15. var PopupWidget = require('point_of_sale.popups');
  16. gui.Gui.prototype.popup_classes.filter(function(c){
  17. return c.name === 'password';
  18. })[0].widget.include({
  19. init: function(parent, args) {
  20. this._super(parent, args);
  21. this.popup_type = 'password';
  22. },
  23. });
  24. PopupWidget.include({
  25. keypad_action: function(data){
  26. var type = this.pos.keypad.type;
  27. if (data.type === type.numchar){
  28. this.click_keyboard(data.val);
  29. } else if (data.type === type.backspace){
  30. this.click_keyboard('BACKSPACE');
  31. } else if (data.type === type.enter){
  32. // some pop-ups might throw an error due to lack of some income data
  33. try {
  34. return this.click_confirm();
  35. } catch (error){
  36. return;
  37. }
  38. } else if (data.type === type.escape){
  39. this.click_cancel();
  40. }
  41. },
  42. click_keyboard: function(value){
  43. if (typeof this.inputbuffer === 'undefined') {
  44. return;
  45. }
  46. var newbuf = this.gui.numpad_input(
  47. this.inputbuffer,
  48. value,
  49. {'firstinput': this.firstinput});
  50. this.firstinput = (newbuf.length === 0);
  51. var $value = this.$('.value');
  52. if (newbuf !== this.inputbuffer) {
  53. this.inputbuffer = newbuf;
  54. $value.text(this.inputbuffer);
  55. }
  56. if (this.popup_type === 'password' && newbuf) {
  57. $value.text($value.text().replace(/./g, '•'));
  58. }
  59. },
  60. show: function(options){
  61. this._super(options);
  62. this.$('input,textarea').focus();
  63. },
  64. });
  65. screens.NumpadWidget.include({
  66. keypad_action: function(data){
  67. var type = this.pos.keypad.type;
  68. if (data.type === type.numchar){
  69. this.state.appendNewChar(data.val);
  70. } else if (data.type === type.bmode) {
  71. this.state.changeMode(data.val);
  72. } else if (data.type === type.sign){
  73. this.clickSwitchSign();
  74. } else if (data.type === type.backspace){
  75. this.clickDeleteLastChar();
  76. }
  77. }
  78. });
  79. screens.PaymentScreenWidget.include({
  80. show: function(){
  81. this._super();
  82. this.pos.keypad.disconnect();
  83. },
  84. hide: function(){
  85. this._super();
  86. this.pos.keypad.connect();
  87. }
  88. });
  89. // this module mimics a keypad-only cash register. Use connect() and
  90. // disconnect() to activate and deactivate it.
  91. var Keypad = core.Class.extend({
  92. init: function(attributes){
  93. this.pos = attributes.pos;
  94. /*this.pos_widget = this.pos.pos_widget;*/
  95. this.type = {
  96. numchar: 'number, dot',
  97. bmode: 'quantity, discount, price',
  98. sign: '+, -',
  99. backspace: 'backspace',
  100. enter: 'enter',
  101. escape: 'escape',
  102. };
  103. this.data = {
  104. // type: undefined,
  105. // val: undefined
  106. };
  107. delete this.action_callback;
  108. this.active = false;
  109. },
  110. save_callback: function(){
  111. this.saved_callback_stack.push(this.action_callback);
  112. },
  113. restore_callback: function(){
  114. if (this.saved_callback_stack.length > 0) {
  115. this.action_callback = this.saved_callback_stack.pop();
  116. }
  117. },
  118. set_action_callback: function(callback){
  119. this.action_callback = callback;
  120. },
  121. //remove action callback
  122. reset_action_callback: function(){
  123. delete this.action_callback;
  124. },
  125. // starts catching keyboard events and tries to interpret keystrokes,
  126. // calling the callback when needed.
  127. connect: function(){
  128. var self = this;
  129. if (self.active) {
  130. return;
  131. }
  132. // --- additional keyboard ---//
  133. // KeyCode: + or - (Keypad '+')
  134. var KC_PLU = 107;
  135. // KeyCode: Quantity (Keypad '/')
  136. var KC_QTY = 111;
  137. // KeyCode: Price (Keypad '*')
  138. var KC_AMT = 106;
  139. // KeyCode: Discount Percentage [0..100] (Keypad '-')
  140. var KC_DISC = 109;
  141. // --- basic keyboard --- //
  142. // KeyCode: sign + or - (Keypad 's')
  143. var KC_PLU_1 = 83;
  144. // KeyCode: Quantity (Keypad 'q')
  145. var KC_QTY_1 = 81;
  146. // KeyCode: Price (Keypad 'p')
  147. var KC_AMT_1 = 80;
  148. // KeyCode: Discount Percentage [0..100] (Keypad 'd')
  149. var KC_DISC_1 = 68;
  150. // KeyCode: Backspace (Keypad 'backspace')
  151. var KC_BACKSPACE = 8;
  152. // KeyCode: Enter (Keypad 'enter')
  153. var KC_ENTER = 13;
  154. // KeyCode: Escape (Keypad 'esc')
  155. var KC_ESCAPE = 27;
  156. var kc_lookup = {
  157. 48: '0', 49: '1', 50: '2', 51: '3', 52: '4',
  158. 53: '5', 54: '6', 55: '7', 56: '8', 57: '9',
  159. 80: 'p', 83: 's', 68: 'd', 190: '.', 81: 'q',
  160. 96: '0', 97: '1', 98: '2', 99: '3', 100: '4',
  161. 101: '5', 102: '6', 103: '7', 104: '8', 105: '9',
  162. 106: '*', 107: '+', 109: '-', 110: '.', 111: '/'
  163. };
  164. //usb keyboard keyup event
  165. var rx = /INPUT|SELECT|TEXTAREA/i;
  166. var ok = false;
  167. var timeStamp = 0;
  168. $('body').on('keyup', '', function (e){
  169. var statusHandler = !rx.test(e.target.tagName) ||
  170. e.target.disabled || e.target.readOnly;
  171. if (statusHandler){
  172. var is_number = false;
  173. var type = self.type;
  174. var buttonMode = {
  175. qty: 'quantity',
  176. disc: 'discount',
  177. price: 'price'
  178. };
  179. var token = e.keyCode;
  180. if (((token >= 96 && token <= 105) || token === 110) ||
  181. ((token >= 48 && token <= 57) || token === 190)) {
  182. self.data.type = type.numchar;
  183. self.data.val = kc_lookup[token];
  184. is_number = true;
  185. ok = true;
  186. } else if (token === KC_PLU || token === KC_PLU_1) {
  187. self.data.type = type.sign;
  188. ok = true;
  189. } else if (token === KC_QTY || token === KC_QTY_1) {
  190. self.data.type = type.bmode;
  191. self.data.val = buttonMode.qty;
  192. ok = true;
  193. } else if (token === KC_AMT || token === KC_AMT_1) {
  194. self.data.type = type.bmode;
  195. self.data.val = buttonMode.price;
  196. ok = true;
  197. } else if (token === KC_DISC || token === KC_DISC_1) {
  198. self.data.type = type.bmode;
  199. self.data.val = buttonMode.disc;
  200. ok = true;
  201. } else if (token === KC_BACKSPACE) {
  202. self.data.type = type.backspace;
  203. ok = true;
  204. } else if (token === KC_ENTER) {
  205. self.data.type = type.enter;
  206. ok = true;
  207. } else if (token === KC_ESCAPE) {
  208. self.data.type = type.escape;
  209. ok = true;
  210. } else {
  211. delete self.data.type;
  212. delete self.data.val;
  213. ok = false;
  214. }
  215. if (is_number) {
  216. if (timeStamp + 50 > new Date().getTime()) {
  217. ok = false;
  218. }
  219. }
  220. timeStamp = new Date().getTime();
  221. setTimeout(function(){
  222. if (ok) {
  223. self.action_callback(self.data);
  224. }
  225. }, 50);
  226. }
  227. });
  228. self.active = true;
  229. },
  230. // stops catching keyboard events
  231. disconnect: function(){
  232. $('body').off('keyup', '');
  233. this.active = false;
  234. }
  235. });
  236. var _super_posmodel = models.PosModel.prototype;
  237. models.PosModel = models.PosModel.extend({
  238. initialize: function (session, attributes) {
  239. var self = this;
  240. this.keypad = new Keypad({'pos': this});
  241. _super_posmodel.initialize.call(this, session, attributes);
  242. this.ready.then(function(){
  243. self.keypad.set_action_callback(function(data){
  244. var current_screen = self.gui.current_screen;
  245. var current_popup = self.gui.current_popup;
  246. if (current_popup) {
  247. current_popup.keypad_action(data);
  248. } else if (current_screen.numpad && current_screen.numpad.keypad_action) {
  249. current_screen.numpad.keypad_action(data);
  250. }
  251. });
  252. });
  253. }
  254. });
  255. return {
  256. Keypad: Keypad
  257. };
  258. });