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.

270 lines
11 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. odoo.define('pos_barcode_tare.screens', function (require) {
  2. "use strict";
  3. var chrome = require('point_of_sale.chrome');
  4. var core = require('web.core');
  5. var devices = require('point_of_sale.devices');
  6. var gui = require('point_of_sale.gui');
  7. var models = require('point_of_sale.models');
  8. var screens = require('point_of_sale.screens');
  9. var utils = require('web.utils');
  10. var QWeb = core.qweb;
  11. var _t = core._t;
  12. var round_pr = utils.round_precision;
  13. var tare_barcode_type = "tare";
  14. // Define functions used to do unit operation.
  15. // Get unit search for unit based on unit name.
  16. var get_unit = function (pos, unit_name) {
  17. return pos.units.filter(
  18. function (u) {
  19. return u.name === unit_name;
  20. })[0];
  21. };
  22. // Convert mass using the reference UOM as pivot unit.
  23. var convert_mass = function (mass, from_unit, to_unit) {
  24. // There is no conversion from one category to another.
  25. if (from_unit.category_id[0] !== to_unit.category_id[0]) {
  26. return;
  27. }
  28. // No need to convert as weights are measured in same unit.
  29. if (from_unit.id === to_unit.id) {
  30. return mass;
  31. }
  32. // Converts "from_unit" to reference unit of measure.
  33. var result = mass;
  34. if (from_unit.uom_type === "bigger") {
  35. result /= from_unit.factor;
  36. } else {
  37. result *= from_unit.factor_inv;
  38. }
  39. // Converts reference unit of measure to "to_unit".
  40. if (to_unit.uom_type === "bigger") {
  41. result *= to_unit.factor;
  42. } else {
  43. result /= to_unit.factor_inv;
  44. }
  45. // Round the result.
  46. return round_pr(result || 0, to_unit.rounding);
  47. };
  48. // This configures read action for tare barcode. A tare barcode contains a
  49. // fake product ID and the weight to be subtracted from the product in the
  50. // latest order line.
  51. screens.ScreenWidget.include(
  52. {
  53. barcode_tare_action: function (code) {
  54. var order = this.pos.get_order();
  55. // Computes the paid weight
  56. var last_order_line = order.get_last_orderline();
  57. var total_weight = last_order_line.get_quantity();
  58. var tare = code.value;
  59. var product_unit = last_order_line.get_unit();
  60. // Try to convert tare in KG into product UOM.
  61. var kg_unit = get_unit(this.pos, "kg");
  62. var tare_in_product_uom =
  63. convert_mass(tare, kg_unit, product_unit);
  64. // Alert when mass conversion failed.
  65. if (typeof tare_in_product_uom === 'undefined') {
  66. this.gui.show_popup('error',
  67. {'title': _t('Mismatch in units of measure'),
  68. 'body':
  69. _.str.sprintf(
  70. _t('You scanned a tare barcode. ' +
  71. 'The tare should apply to "%s %s of %s". '+
  72. 'We do not know how to convert kg to %s. ' +
  73. 'We can not apply the tare to this product.'),
  74. total_weight,
  75. product_unit.name,
  76. last_order_line.product.display_name,
  77. product_unit.name)});
  78. return;
  79. }
  80. // Computes the paid (net) weight.
  81. var paid_weight = total_weight - tare_in_product_uom;
  82. // Throws a warning popup if the price is negative.
  83. if (paid_weight <= 0) {
  84. this.gui.show_popup('confirm',
  85. {'title': _t('Negative weight'),
  86. 'body': _t('The calculated weight is negative. ' +
  87. 'Did you scan the correct tare label?'),
  88. confirm: function () {
  89. // Operator can choose to ignore the warning.
  90. last_order_line.set_quantity(paid_weight);
  91. }});
  92. } else {
  93. // Updates the prices.
  94. last_order_line.set_quantity(paid_weight);
  95. }
  96. },
  97. // Setup the callback action for the "weight" barcodes.
  98. show: function () {
  99. this._super();
  100. this.pos.barcode_reader.set_action_callback(
  101. 'tare',
  102. _.bind(this.barcode_tare_action, this));
  103. },
  104. });
  105. // This create a new button on top of action widget. This button links to
  106. // the barcode label printing screen defined below.
  107. var TareScreenButton = screens.ActionButtonWidget.extend({
  108. template: 'TareScreenButton',
  109. button_click: function () {
  110. this.gui.show_screen('tare');
  111. },
  112. });
  113. screens.define_action_button({
  114. 'name': 'tareScreenButton',
  115. 'widget': TareScreenButton,
  116. });
  117. // This is a new screen that reads weight from the electronic scale and
  118. // create a barcode label encoding the weight. The screen shows a preview
  119. // of the label. The user is expected to check if the preview matches what's
  120. // measured on the scale. The barcode image is generated by the report
  121. // module.
  122. var TareScreenWidget = screens.ScreenWidget.extend({
  123. template: 'TareScreenWidget',
  124. next_screen: 'products',
  125. previous_screen: 'products',
  126. default_tare_value: 0.0,
  127. weight_barcode_prefix: null,
  128. show: function () {
  129. this._super();
  130. // Fetch the unit of measure used to save the tare
  131. this.kg_unit = get_unit(this.pos, "kg");
  132. // Fetch the barcode prefix from POS barcode parser rules.
  133. this.weight_barcode_prefix = this.get_barcode_prefix();
  134. // Setup the proxy
  135. var queue = this.pos.proxy_queue;
  136. // The pooling of the scale starts here.
  137. var self = this;
  138. queue.schedule(function () {
  139. return self.pos.proxy.scale_read().then(function (weight) {
  140. self.set_weight(weight);
  141. });
  142. }, {duration:150, repeat: true});
  143. // Shows a barcode whose weight might be zero, but this is preferred
  144. // for UI/UX reasons.
  145. this.render_receipt();
  146. this.lock_screen(true);
  147. },
  148. get_barcode_prefix: function () {
  149. var barcode_pattern = this.get_barcode_pattern();
  150. return barcode_pattern.substr(0, 2);
  151. },
  152. get_barcode_pattern: function () {
  153. var rules = this.get_barcode_rules();
  154. var rule = rules.filter(
  155. function (r) {
  156. // We select the first (smallest sequence ID) barcode rule
  157. // with the expected type.
  158. return r.type === tare_barcode_type;
  159. })[0];
  160. return rule.pattern;
  161. },
  162. get_barcode_rules: function () {
  163. return this.pos.barcode_reader.barcode_parser.nomenclature.rules;
  164. },
  165. set_weight: function (scale_measure) {
  166. var weight = scale_measure.weight;
  167. var unit = get_unit(this.pos, scale_measure.unit);
  168. if (weight > 0) {
  169. this.weight_in_kg = convert_mass(weight, unit, this.kg_unit);
  170. this.render_receipt();
  171. this.lock_screen(false);
  172. }
  173. },
  174. get_weight: function () {
  175. if (typeof this.weight_in_kg === 'undefined') {
  176. return this.default_tare_value;
  177. }
  178. return this.weight_in_kg;
  179. },
  180. barcode_data: function (weight) {
  181. // We use EAN13 barcode, it looks like 07 00000 12345 x. First there
  182. // is the prefix, here 07, that is used to decide which type of
  183. // barcode we're dealing with. A weight barcode has then two groups
  184. // of five digits. The first group encodes the product id. Here the
  185. // product id is 00000. The second group encodes the weight in
  186. // grams. Here the weight is 12.345kg. The last digit of the barcode
  187. // is a checksum, here symbolized by x.
  188. var padding_size = 5;
  189. var void_product_id = '0'.repeat(padding_size);
  190. var weight_in_gram = weight * 10e2;
  191. // Weight has to be padded with zeros.
  192. var weight_with_padding = '0'.repeat(padding_size) + weight_in_gram;
  193. var padded_weight = weight_with_padding.substr(
  194. weight_with_padding.length - padding_size);
  195. // Builds the barcode using a placeholder checksum.
  196. var barcode = this.weight_barcode_prefix
  197. .concat(void_product_id, padded_weight)
  198. .concat(0);
  199. // Compute checksum
  200. var barcode_parser = this.pos.barcode_reader.barcode_parser;
  201. var checksum = barcode_parser.ean_checksum(barcode);
  202. // Replace checksum placeholder by the actual checksum.
  203. return barcode.substr(0, 12).concat(checksum);
  204. },
  205. get_barcode_data: function () {
  206. return this.barcode_data(this.get_weight());
  207. },
  208. lock_screen: function (locked) {
  209. this._locked = locked;
  210. if (locked) {
  211. this.$('.print-label').addClass('disabled');
  212. } else {
  213. this.$('.print-label').removeClass('disabled');
  214. }
  215. },
  216. print_web: function () {
  217. window.print();
  218. this.pos.get_order()._printed = true;
  219. },
  220. print: function () {
  221. // See comment in print function of ReceiptScreenWidget
  222. this.lock_screen(true);
  223. var self = this;
  224. setTimeout(function () {
  225. self.lock_screen(false);
  226. }, 1000);
  227. this.print_web();
  228. this.click_back();
  229. },
  230. click_back: function () {
  231. this.close();
  232. this.gui.show_screen(this.previous_screen);
  233. },
  234. renderElement: function () {
  235. this._super();
  236. var self = this;
  237. this.$('.back').click(function () {
  238. self.click_back();
  239. });
  240. this.$('.print-label').click(function () {
  241. if (!self._locked) {
  242. self.print();
  243. }
  244. });
  245. },
  246. render_receipt: function () {
  247. this.$('.pos-tare-label-container').html(
  248. QWeb.render('PosTareLabel', {widget:this}));
  249. },
  250. close: function () {
  251. this._super();
  252. delete this.weight;
  253. this.pos.proxy_queue.clear();
  254. },
  255. });
  256. gui.define_screen({name:'tare', widget: TareScreenWidget});
  257. });