From f20cd3db7565b04a96066aa41e08cd3679eb86c8 Mon Sep 17 00:00:00 2001 From: David Beguin Date: Wed, 19 Sep 2018 13:19:57 +0000 Subject: [PATCH] [FIX] hr_attendance : kiosk mode multi scanning - Scan a barcode for employee 1 in kiosk mode - At greetings message, scan a barcode for employee 2 2 attendance entries are created for employee 2 instead of 1. When scanning the barcode for employee 2, the event listener `_onBarcodeScanned` is still active on `KioskMode`. Therefore, the RPC call to `attendance_scan` is performed twice. We deactivate the event listener after scanning, and reactivate on `KioskMode` only if there is an issue. This is sufficient since the call to `do_action` in `GreetingMessage` will create another instance of the client action. In `GreetingMessage`, we add back the redirection to `KioskMode`. Co-authored-by: Nicolas Martinelli Linked to Task ID 1878251 opw-1878632 opw-1888402 closes odoo/odoo#28150 --- addons/hr_attendance/models/hr_employee.py | 1 + .../static/src/js/greeting_message.js | 35 +++-- .../hr_attendance/static/src/js/kiosk_mode.js | 4 + .../static/tests/hr_attendance_tests.js | 127 ++++++++++++++++++ 4 files changed, 153 insertions(+), 14 deletions(-) diff --git a/addons/hr_attendance/models/hr_employee.py b/addons/hr_attendance/models/hr_employee.py index 7e224328003..3521acb64de 100644 --- a/addons/hr_attendance/models/hr_employee.py +++ b/addons/hr_attendance/models/hr_employee.py @@ -89,6 +89,7 @@ class HrEmployee(models.Model): action_message = self.env.ref('hr_attendance.hr_attendance_action_greeting_message').read()[0] action_message['previous_attendance_change_date'] = self.last_attendance_id and (self.last_attendance_id.check_out or self.last_attendance_id.check_in) or False action_message['employee_name'] = self.name + action_message['barcode'] = self.barcode action_message['next_action'] = next_action if self.user_id: diff --git a/addons/hr_attendance/static/src/js/greeting_message.js b/addons/hr_attendance/static/src/js/greeting_message.js index 0e789670d13..c19a7875fa3 100644 --- a/addons/hr_attendance/static/src/js/greeting_message.js +++ b/addons/hr_attendance/static/src/js/greeting_message.js @@ -51,6 +51,7 @@ var GreetingMessage = Widget.extend({ this.attendance.check_in_time = this.attendance.check_in && this.attendance.check_in.format(this.format_time); this.attendance.check_out_time = this.attendance.check_out && this.attendance.check_out.format(this.format_time); this.employee_name = action.employee_name; + this.attendanceBarcode = action.barcode; }, start: function() { @@ -136,21 +137,27 @@ var GreetingMessage = Widget.extend({ _onBarcodeScanned: function(barcode) { var self = this; - if (this.return_to_main_menu) { // in case of multiple scans in the greeting message view, delete the timer, a new one will be created. - clearTimeout(this.return_to_main_menu); + if (this.attendanceBarcode !== barcode){ + if (this.return_to_main_menu) { // in case of multiple scans in the greeting message view, delete the timer, a new one will be created. + clearTimeout(this.return_to_main_menu); + } + core.bus.off('barcode_scanned', this, this._onBarcodeScanned); + this._rpc({ + model: 'hr.employee', + method: 'attendance_scan', + args: [barcode, ], + }) + .then(function (result) { + if (result.action) { + self.do_action(result.action); + } else if (result.warning) { + self.do_warn(result.warning); + setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, 5000); + } + }, function () { + setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, 5000); + }); } - this._rpc({ - model: 'hr.employee', - method: 'attendance_scan', - args: [barcode, ], - }) - .then(function (result) { - if (result.action) { - self.do_action(result.action); - } else if (result.warning) { - self.do_warn(result.warning); - } - }); }, destroy: function () { diff --git a/addons/hr_attendance/static/src/js/kiosk_mode.js b/addons/hr_attendance/static/src/js/kiosk_mode.js index 234df40fe07..58e1ad9c09b 100644 --- a/addons/hr_attendance/static/src/js/kiosk_mode.js +++ b/addons/hr_attendance/static/src/js/kiosk_mode.js @@ -35,6 +35,7 @@ var KioskMode = Widget.extend({ _onBarcodeScanned: function(barcode) { var self = this; + core.bus.off('barcode_scanned', this, this._onBarcodeScanned); this._rpc({ model: 'hr.employee', method: 'attendance_scan', @@ -45,7 +46,10 @@ var KioskMode = Widget.extend({ self.do_action(result.action); } else if (result.warning) { self.do_warn(result.warning); + core.bus.on('barcode_scanned', self, self._onBarcodeScanned); } + }, function () { + core.bus.on('barcode_scanned', self, self._onBarcodeScanned); }); }, diff --git a/addons/hr_attendance/static/tests/hr_attendance_tests.js b/addons/hr_attendance/static/tests/hr_attendance_tests.js index 0f09069048c..750fc6318d2 100644 --- a/addons/hr_attendance/static/tests/hr_attendance_tests.js +++ b/addons/hr_attendance/static/tests/hr_attendance_tests.js @@ -2,8 +2,11 @@ odoo.define('hr_attendance.tests', function (require) { "use strict"; var testUtils = require('web.test_utils'); +var core = require('web.core'); var MyAttendances = require('hr_attendance.my_attendances'); +var KioskMode = require('hr_attendance.kiosk_mode'); +var GreetingMessage = require('hr_attendance.greeting_message'); QUnit.module('HR Attendance', { @@ -19,12 +22,30 @@ QUnit.module('HR Attendance', { default: 1, }, user_id: {string: 'user ID', type: 'integer'}, + barcode: {string:'barcode', type: 'integer'}, }, records: [{ id: 1, name: "Employee A", attendance_state: 1, user_id: 1, + barcode: 1, + }, + { + id: 2, + name: "Employee B", + attendance_state: 'checked_out', + user_id: 2, + barcode: 2, + }], + }, + 'res.company': { + fields: { + name: {string: 'Name', type: 'char'}, + }, + records: [{ + id: 1, + name: "Company A", }], }, }; @@ -51,6 +72,112 @@ QUnit.module('HR Attendance', { clientAction.destroy(); }); + QUnit.test('Attendance Kiosk Mode Test', function (assert) { + assert.expect(2); + + var $target = $('#qunit-fixture'); + var self = this; + var rpcCount = 0; + var clientAction = new KioskMode(null); + testUtils.addMockEnvironment(clientAction, { + data: this.data, + session: { + uid: 1, + company_id: 1, + }, + mockRPC: function(route, args) { + if (args.method === 'attendance_scan' && args.model === 'hr.employee') { + + rpcCount++; + return $.when(self.data['hr.employee'].records[0]); + } + return this._super(route, args); + }, + }); + clientAction.appendTo($target); + core.bus.trigger('barcode_scanned', 1); + core.bus.trigger('barcode_scanned', 1); + assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.'); + + core.bus.trigger('barcode_scanned', 2); + assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.'); + + clientAction.destroy(); + }); + + QUnit.test('Attendance Greeting Message Test', function (assert) { + assert.expect(10); + + var $target = $('#qunit-fixture'); + var self = this; + var rpcCount = 0; + + var clientActions = []; + function createGreetingMessage (target, barcode){ + var action = { + attendance: { + check_in: "2018-09-20 13:41:13", + employee_id: [barcode], + }, + next_action: "hr_attendance.hr_attendance_action_kiosk_mode", + barcode: barcode, + } + var clientAction = new GreetingMessage(null, action); + testUtils.addMockEnvironment(clientAction, { + data: self.data, + session: { + uid: 1, + company_id: 1, + }, + mockRPC: function(route, args) { + if (args.method === 'attendance_scan' && args.model === 'hr.employee') { + rpcCount++; + action.attendance.employee_id = [args.args[0], 'Employee']; + /* + if rpc have been made, a new instance is created to simulate the same behaviour + as functional flow. + */ + createGreetingMessage (target, args.args[0]); + return $.when({action: action}); + } + return this._super(route, args); + }, + }); + clientAction.appendTo(target); + + clientActions.push(clientAction); + }; + + // init - mock coming from kiosk + createGreetingMessage ($target, 1); + assert.strictEqual(clientActions.length, 1, 'Number of clientAction must = 1.'); + + core.bus.trigger('barcode_scanned', 1); + /* + As action is given when instantiate GreetingMessage, we simulate that we come from the KioskMode + So rescanning the same barcode won't lead to another RPC. + */ + assert.strictEqual(clientActions.length, 1, 'Number of clientActions must = 1.'); + assert.strictEqual(rpcCount, 0, 'RPC call should not have been done.'); + + core.bus.trigger('barcode_scanned', 2); + assert.strictEqual(clientActions.length, 2, 'Number of clientActions must = 2.'); + assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.'); + core.bus.trigger('barcode_scanned', 2); + assert.strictEqual(clientActions.length, 2, 'Number of clientActions must = 2.'); + assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.'); + + core.bus.trigger('barcode_scanned', 1); + assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.'); + core.bus.trigger('barcode_scanned', 1); + assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.'); + assert.strictEqual(rpcCount, 2, 'RPC call should have been done only twice.'); + + _.each(clientActions, function(clientAction) { + clientAction.destroy(); + }); + }); + }); });