mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-27 02:52:02 +02:00
Initial commit: Hr packages
This commit is contained in:
commit
62531cd146
2820 changed files with 1432848 additions and 0 deletions
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="100%" x2="0%" y1="0%" y2="100%"><stop offset="0%" stop-color="#94B6C8"/><stop offset="100%" stop-color="#6A9EBA"/></linearGradient><path id="d" d="M28.278 17.235c5.193 0 9.404 4.271 9.404 9.54 0 5.27-4.21 9.541-9.404 9.541s-9.404-4.271-9.404-9.54c0-5.27 4.21-9.541 9.404-9.541zm6.018 19.492c0-.413-9.178 1.847-12.6-.65l-4.19 1.063c-2.512.638-4.275 2.927-4.275 5.554v2.209c0 1.58 1.264 2.862 2.822 2.862h15.466c-1.852-6.34 2.777-10.625 2.777-11.038zM45.5 33.794c-6.466 0-11.706 5.24-11.706 11.706 0 6.466 5.24 11.706 11.706 11.706 6.466 0 11.706-5.24 11.706-11.706 0-6.466-5.24-11.706-11.706-11.706zm2.695 16.525l-4.163-3.025a.57.57 0 0 1-.231-.458v-7.944c0-.312.255-.566.566-.566h2.266c.311 0 .566.254.566.566v6.5l2.997 2.18a.566.566 0 0 1 .123.793l-1.33 1.831a.57.57 0 0 1-.794.123z"/><path id="e" d="M28.278 15.235c5.193 0 9.404 4.271 9.404 9.54 0 5.27-4.21 9.541-9.404 9.541s-9.404-4.271-9.404-9.54c0-5.27 4.21-9.541 9.404-9.541zm6.018 19.492c0-.413-9.178 1.847-12.6-.65l-4.19 1.063c-2.512.638-4.275 2.927-4.275 5.554v2.209c0 1.58 1.264 2.862 2.822 2.862h15.466c-1.852-6.34 2.777-10.625 2.777-11.038zM45.5 31.794c-6.466 0-11.706 5.24-11.706 11.706 0 6.466 5.24 11.706 11.706 11.706 6.466 0 11.706-5.24 11.706-11.706 0-6.466-5.24-11.706-11.706-11.706zm2.695 16.525l-4.163-3.025a.57.57 0 0 1-.231-.458v-7.944c0-.312.255-.566.566-.566h2.266c.311 0 .566.254.566.566v6.5l2.997 2.18a.566.566 0 0 1 .123.793l-1.33 1.831a.57.57 0 0 1-.794.123z"/></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M42.488 69H4c-2 0-4-1-4-4V41.348l20.938-22.35 14.703 11.46-4.201 5.183-1.252 7.42 6.406-7.111L46 33l9.958 15.844L42.488 69z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><use fill="#000" fill-rule="nonzero" opacity=".3" xlink:href="#d"/><use fill="#FFF" fill-rule="nonzero" xlink:href="#e"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1,51 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { graphView } from "@web/views/graph/graph_view";
|
||||
import { pivotView } from "@web/views/pivot/pivot_view";
|
||||
|
||||
const viewRegistry = registry.category("views");
|
||||
|
||||
/**
|
||||
* Open the hr.attendance instead of the report list view.
|
||||
*/
|
||||
function openView(component, domain, views, context) {
|
||||
component.actionService.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: component.model.metaData.title,
|
||||
res_model: 'hr.attendance',
|
||||
views: [[false, "list"], [false, "form"]],
|
||||
view_mode: "list",
|
||||
target: "current",
|
||||
context,
|
||||
domain,
|
||||
});
|
||||
}
|
||||
|
||||
export class AttendanceReportGraphController extends graphView.Controller {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
openView(domain, views, context) {
|
||||
openView(this, domain, views, context);
|
||||
}
|
||||
}
|
||||
|
||||
viewRegistry.add("attendance_report_graph", {
|
||||
...graphView,
|
||||
Controller: AttendanceReportGraphController
|
||||
});
|
||||
|
||||
export class AttendanceReportPivotController extends pivotView.Controller {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
openView(domain, views, context) {
|
||||
openView(this, domain, views, context);
|
||||
}
|
||||
}
|
||||
|
||||
viewRegistry.add("attendance_report_pivot", {
|
||||
...pivotView,
|
||||
Controller: AttendanceReportPivotController
|
||||
});
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
odoo.define('hr_attendance.greeting_message', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var core = require('web.core');
|
||||
var time = require('web.time');
|
||||
var field_utils = require('web.field_utils');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
|
||||
var GreetingMessage = AbstractAction.extend({
|
||||
contentTemplate: 'HrAttendanceGreetingMessage',
|
||||
|
||||
events: {
|
||||
"click .o_hr_attendance_button_dismiss": function() { this.do_action(this.next_action, {clear_breadcrumbs: true}); },
|
||||
},
|
||||
|
||||
init: function(parent, action) {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.activeBarcode = true;
|
||||
this.kioskDelay = action.kiosk_delay;
|
||||
|
||||
// if no correct action given (due to an erroneous back or refresh from the browser), we set the dismiss button to return
|
||||
// to the (likely) appropriate menu, according to the user access rights
|
||||
if(!action.attendance) {
|
||||
this.activeBarcode = false;
|
||||
this.getSession().user_has_group('hr_attendance.group_hr_attendance_user').then(function(has_group) {
|
||||
if(has_group) {
|
||||
self.next_action = 'hr_attendance.hr_attendance_action_kiosk_mode';
|
||||
} else {
|
||||
self.next_action = 'hr_attendance.hr_attendance_action_my_attendances';
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.next_action = action.next_action || 'hr_attendance.hr_attendance_action_my_attendances';
|
||||
// no listening to barcode scans if we aren't coming from the kiosk mode (and thus not going back to it with next_action)
|
||||
if (this.next_action != 'hr_attendance.hr_attendance_action_kiosk_mode' && this.next_action.tag != 'hr_attendance_kiosk_mode') {
|
||||
this.activeBarcode = false;
|
||||
}
|
||||
|
||||
this.attendance = action.attendance;
|
||||
// We receive the check in/out times in UTC
|
||||
// This widget only deals with display, which should be in browser's TimeZone
|
||||
this.attendance.check_in = this.attendance.check_in && moment.utc(this.attendance.check_in).local();
|
||||
this.attendance.check_out = this.attendance.check_out && moment.utc(this.attendance.check_out).local();
|
||||
this.previous_attendance_change_date = action.previous_attendance_change_date && moment.utc(action.previous_attendance_change_date).local();
|
||||
|
||||
// check in/out times displayed in the greeting message template.
|
||||
this.format_time = time.getLangTimeFormat();
|
||||
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);
|
||||
|
||||
// extra hours amount displayed in the greeting message template.
|
||||
this.show_total_overtime = action.show_total_overtime;
|
||||
this.total_overtime_float = action.total_overtime; // Used for comparison in template
|
||||
this.total_overtime = field_utils.format.float_time(this.total_overtime_float);
|
||||
this.today_overtime_float = action.overtime_today;
|
||||
this.today_overtime = field_utils.format.float_time(this.today_overtime_float);
|
||||
|
||||
if (action.hours_today) {
|
||||
var duration = moment.duration(action.hours_today, "hours");
|
||||
this.hours_today = duration.hours() + ' hours, ' + duration.minutes() + ' minutes';
|
||||
}
|
||||
|
||||
this.employee_name = action.employee_name;
|
||||
this.attendanceBarcode = action.barcode;
|
||||
},
|
||||
|
||||
start: function() {
|
||||
if (this.attendance) {
|
||||
this.attendance.check_out ? this.farewell_message() : this.welcome_message();
|
||||
}
|
||||
if (this.activeBarcode) {
|
||||
core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
welcome_message: function() {
|
||||
var self = this;
|
||||
var now = this.attendance.check_in.clone();
|
||||
if (this.kioskDelay > 0) {
|
||||
this.return_to_main_menu = setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, this.kioskDelay);
|
||||
}
|
||||
|
||||
if (now.hours() < 5) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Good night"));
|
||||
} else if (now.hours() < 12) {
|
||||
if (now.hours() < 8 && Math.random() < 0.3) {
|
||||
if (Math.random() < 0.75) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("The early bird catches the worm"));
|
||||
} else {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("First come, first served"));
|
||||
}
|
||||
} else {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Good morning"));
|
||||
}
|
||||
} else if (now.hours() < 17){
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Good afternoon"));
|
||||
} else if (now.hours() < 23){
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Good evening"));
|
||||
} else {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Good night"));
|
||||
}
|
||||
if(this.previous_attendance_change_date){
|
||||
var last_check_out_date = this.previous_attendance_change_date.clone();
|
||||
if(now - last_check_out_date > 24*7*60*60*1000){
|
||||
this.$('.o_hr_attendance_random_message').html(_t("Glad to have you back, it's been a while!"));
|
||||
} else {
|
||||
if(Math.random() < 0.02){
|
||||
this.$('.o_hr_attendance_random_message').html(_t("If a job is worth doing, it is worth doing well!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
farewell_message: function() {
|
||||
var self = this;
|
||||
var now = this.attendance.check_out.clone();
|
||||
if (this.kioskDelay > 0) {
|
||||
this.return_to_main_menu = setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, this.kioskDelay);
|
||||
}
|
||||
|
||||
if(this.previous_attendance_change_date){
|
||||
var last_check_in_date = this.previous_attendance_change_date.clone();
|
||||
if(now - last_check_in_date > 1000*60*60*12){
|
||||
this.$('.o_hr_attendance_warning_message').show().append(_t("<b>Warning! Last check in was over 12 hours ago.</b><br/>If this isn't right, please contact Human Resource staff"));
|
||||
if (this.return_to_main_menu) {
|
||||
clearTimeout(this.return_to_main_menu);
|
||||
}
|
||||
this.activeBarcode = false;
|
||||
} else if(now - last_check_in_date > 1000*60*60*8){
|
||||
this.$('.o_hr_attendance_random_message').html(_t("Another good day's work! See you soon!"));
|
||||
}
|
||||
}
|
||||
|
||||
if (now.hours() < 12) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Have a good day!"));
|
||||
} else if (now.hours() < 14) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Have a nice lunch!"));
|
||||
if (Math.random() < 0.05) {
|
||||
this.$('.o_hr_attendance_random_message').html(_t("Eat breakfast as a king, lunch as a merchant and supper as a beggar"));
|
||||
} else if (Math.random() < 0.06) {
|
||||
this.$('.o_hr_attendance_random_message').html(_t("An apple a day keeps the doctor away"));
|
||||
}
|
||||
} else if (now.hours() < 17) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Have a good afternoon"));
|
||||
} else {
|
||||
if (now.hours() < 18 && Math.random() < 0.2) {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Early to bed and early to rise, makes a man healthy, wealthy and wise"));
|
||||
} else {
|
||||
this.$('.o_hr_attendance_message_message').append(_t("Have a good evening"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onBarcodeScanned: function(barcode) {
|
||||
var self = this;
|
||||
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);
|
||||
const kioskDelay = this.kioskDelay;
|
||||
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.displayNotification({ title: result.warning, type: 'danger' });
|
||||
if (kioskDelay > 0) {
|
||||
setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, kioskDelay);
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
if (kioskDelay > 0) {
|
||||
setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, kioskDelay);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
|
||||
clearTimeout(this.return_to_main_menu);
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('hr_attendance_greeting_message', GreetingMessage);
|
||||
|
||||
return GreetingMessage;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
odoo.define('hr_attendance.kiosk_confirm', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var core = require('web.core');
|
||||
var field_utils = require('web.field_utils');
|
||||
var QWeb = core.qweb;
|
||||
|
||||
const session = require('web.session');
|
||||
|
||||
|
||||
var KioskConfirm = AbstractAction.extend({
|
||||
events: {
|
||||
"click .o_hr_attendance_back_button": function () { this.do_action(this.next_action, {clear_breadcrumbs: true}); },
|
||||
"click .o_hr_attendance_sign_in_out_icon": _.debounce(function () {
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'attendance_manual',
|
||||
args: [[this.employee_id], this.next_action],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function(result) {
|
||||
if (result.action) {
|
||||
self.do_action(result.action);
|
||||
} else if (result.warning) {
|
||||
self.displayNotification({ title: result.warning, type: 'danger' });
|
||||
}
|
||||
});
|
||||
}, 200, true),
|
||||
'click .o_hr_attendance_pin_pad_button_0': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 0); },
|
||||
'click .o_hr_attendance_pin_pad_button_1': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 1); },
|
||||
'click .o_hr_attendance_pin_pad_button_2': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 2); },
|
||||
'click .o_hr_attendance_pin_pad_button_3': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 3); },
|
||||
'click .o_hr_attendance_pin_pad_button_4': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 4); },
|
||||
'click .o_hr_attendance_pin_pad_button_5': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 5); },
|
||||
'click .o_hr_attendance_pin_pad_button_6': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 6); },
|
||||
'click .o_hr_attendance_pin_pad_button_7': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 7); },
|
||||
'click .o_hr_attendance_pin_pad_button_8': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 8); },
|
||||
'click .o_hr_attendance_pin_pad_button_9': function() { this.$('.o_hr_attendance_PINbox').val(this.$('.o_hr_attendance_PINbox').val() + 9); },
|
||||
'click .o_hr_attendance_pin_pad_button_C': function() { this.$('.o_hr_attendance_PINbox').val(''); },
|
||||
'click .o_hr_attendance_pin_pad_button_ok': function() { this._send_pin_debounced() },
|
||||
},
|
||||
|
||||
_sendPin: function() {
|
||||
var self = this;
|
||||
this.$('.o_hr_attendance_pin_pad_button_ok').attr("disabled", "disabled");
|
||||
this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'attendance_manual',
|
||||
args: [[this.employee_id], this.next_action, this.$('.o_hr_attendance_PINbox').val()],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function(result) {
|
||||
self.pin_is_send = true
|
||||
if (result.action) {
|
||||
self.do_action(result.action);
|
||||
} else if (result.warning) {
|
||||
self.displayNotification({ title: result.warning, type: 'danger' });
|
||||
self.$('.o_hr_attendance_PINbox').val('');
|
||||
setTimeout( function() { self.$('.o_hr_attendance_pin_pad_button_ok').removeAttr("disabled"); }, 500);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
init: function (parent, action) {
|
||||
this._super.apply(this, arguments);
|
||||
this.next_action = 'hr_attendance.hr_attendance_action_kiosk_mode';
|
||||
this.employee_id = action.employee_id;
|
||||
this.employee_name = action.employee_name;
|
||||
this.employee_state = action.employee_state;
|
||||
this.employee_hours_today = field_utils.format.float_time(action.employee_hours_today);
|
||||
|
||||
this.pin_is_send = false
|
||||
this._send_pin_debounced = _.debounce(this._sendPin, 200, true);
|
||||
|
||||
window.addEventListener("keydown", (ev) => {
|
||||
const allowedKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Backspace', 'Enter', 'Delete'];
|
||||
const key = ev.key;
|
||||
|
||||
if (!allowedKeys.includes(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const pinBox = this.$('.o_hr_attendance_PINbox');
|
||||
|
||||
if (key.length == 1) {
|
||||
pinBox.val(pinBox.val() + key);
|
||||
}
|
||||
else if (key == 'Enter' && !this.pin_is_send) {
|
||||
this._send_pin_debounced();
|
||||
}
|
||||
else if (key == 'Backspace') {
|
||||
pinBox.val(pinBox.val().substring(0, pinBox.val().length - 1));
|
||||
}
|
||||
else { // Delete
|
||||
pinBox.val('');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.getSession().user_has_group('hr_attendance.group_hr_attendance_use_pin').then(function(has_group){
|
||||
self.use_pin = has_group;
|
||||
self.$el.html(QWeb.render("HrAttendanceKioskConfirm", {widget: self}));
|
||||
self.start_clock();
|
||||
});
|
||||
return self._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
start_clock: function () {
|
||||
this.clock_start = setInterval(function() {this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit', second:'2-digit'}));}, 500);
|
||||
// First clock refresh before interval to avoid delay
|
||||
this.$(".o_hr_attendance_clock").show().text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit', second:'2-digit'}));
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
clearInterval(this.clock_start);
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('hr_attendance_kiosk_confirm', KioskConfirm);
|
||||
|
||||
return KioskConfirm;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
odoo.define('hr_attendance.kiosk_mode', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var Session = require('web.session');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
|
||||
var KioskMode = AbstractAction.extend({
|
||||
events: {
|
||||
"click .o_hr_attendance_button_employees": function() {
|
||||
this.do_action('hr_attendance.hr_employee_attendance_action_kanban', {
|
||||
additional_context: {'no_group_by': true},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
|
||||
self.session = Session;
|
||||
const company_id = this.session.user_context.allowed_company_ids[0];
|
||||
var def = this._rpc({
|
||||
model: 'res.company',
|
||||
method: 'search_read',
|
||||
args: [[['id', '=', company_id]], ['name', 'attendance_kiosk_mode', 'attendance_barcode_source']],
|
||||
})
|
||||
.then(function (companies){
|
||||
self.company_name = companies[0].name;
|
||||
self.company_image_url = self.session.url('/web/image', {model: 'res.company', id: company_id, field: 'logo',});
|
||||
self.kiosk_mode = companies[0].attendance_kiosk_mode;
|
||||
self.barcode_source = companies[0].attendance_barcode_source;
|
||||
self.$el.html(QWeb.render("HrAttendanceKioskMode", {widget: self}));
|
||||
self.start_clock();
|
||||
});
|
||||
// Make a RPC call every day to keep the session alive
|
||||
self._interval = window.setInterval(this._callServer.bind(this), (60*60*1000*24));
|
||||
return Promise.all([def, this._super.apply(this, arguments)]);
|
||||
},
|
||||
|
||||
on_attach_callback: function () {
|
||||
// Stop the bus_service to avoid notifications in kiosk mode
|
||||
this.call('bus_service', 'stop');
|
||||
$('body').find('.o_ChatWindowHeader_commandClose').click();
|
||||
},
|
||||
|
||||
_onBarcodeScanned: function(barcode) {
|
||||
var self = this;
|
||||
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.displayNotification({ title: result.warning, type: 'danger' });
|
||||
core.bus.on('barcode_scanned', self, self._onBarcodeScanned);
|
||||
}
|
||||
}, function () {
|
||||
core.bus.on('barcode_scanned', self, self._onBarcodeScanned);
|
||||
});
|
||||
},
|
||||
|
||||
start_clock: function() {
|
||||
this.clock_start = setInterval(function() {this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit', second:'2-digit'}));}, 500);
|
||||
// First clock refresh before interval to avoid delay
|
||||
this.$(".o_hr_attendance_clock").show().text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit', second:'2-digit'}));
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
|
||||
clearInterval(this.clock_start);
|
||||
clearInterval(this._interval);
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
_callServer: function () {
|
||||
// Make a call to the database to avoid the auto close of the session
|
||||
return ajax.rpc("/hr_attendance/kiosk_keepalive", {});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
core.action_registry.add('hr_attendance_kiosk_mode', KioskMode);
|
||||
|
||||
return KioskMode;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
odoo.define('hr_attendance.my_attendances', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var core = require('web.core');
|
||||
var field_utils = require('web.field_utils');
|
||||
|
||||
const session = require('web.session');
|
||||
|
||||
var MyAttendances = AbstractAction.extend({
|
||||
contentTemplate: 'HrAttendanceMyMainMenu',
|
||||
events: {
|
||||
"click .o_hr_attendance_sign_in_out_icon": _.debounce(function() {
|
||||
this.update_attendance();
|
||||
}, 200, true),
|
||||
},
|
||||
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
|
||||
var def = this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'search_read',
|
||||
args: [[['user_id', '=', this.getSession().uid]], ['attendance_state', 'name', 'hours_today']],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function (res) {
|
||||
self.employee = res.length && res[0];
|
||||
if (res.length) {
|
||||
self.hours_today = field_utils.format.float_time(self.employee.hours_today);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([def, this._super.apply(this, arguments)]);
|
||||
},
|
||||
|
||||
update_attendance: function () {
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'attendance_manual',
|
||||
args: [[self.employee.id], 'hr_attendance.hr_attendance_action_my_attendances'],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function(result) {
|
||||
if (result.action) {
|
||||
self.do_action(result.action);
|
||||
} else if (result.warning) {
|
||||
self.displayNotification({ title: result.warning, type: 'danger' });
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('hr_attendance_my_attendances', MyAttendances);
|
||||
|
||||
return MyAttendances;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
.o_hr_attendance_kiosk_mode, .o_hr_attendance_clock {
|
||||
@include media-breakpoint-down(md) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.o_hr_attendance_clock {
|
||||
position: relative;
|
||||
display: none;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
@include o-position-absolute(0, 0); // Fine-tuned by margin classes
|
||||
}
|
||||
}
|
||||
|
||||
.o_hr_attendance_kiosk_mode {
|
||||
@include media-breakpoint-up(md) {
|
||||
min-width: map-get($grid-breakpoints, 'md') * .6;
|
||||
|
||||
.o_hr_attendance_user_badge {
|
||||
@include o-position-absolute(auto, $border-width * -1, 100%, $border-width * -1); // Compensate card's border
|
||||
}
|
||||
}
|
||||
|
||||
.o_hr_attendance_back_button_md {
|
||||
aspect-ratio: 1;
|
||||
transform: translate(-50%, -50%); // Once migrate to BS5, can be replaced by 'translate-middle' class.
|
||||
}
|
||||
|
||||
.o_hr_attendance_PINbox_button {
|
||||
aspect-ratio: 1.8;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<template xml:space="preserve">
|
||||
<t t-name="PresenceIndicator">
|
||||
<div id="oe_hr_attendance_status" class="fa fa-circle me-1 text-400 " role="img" aria-label="Available" title="Available">
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceCardLayout">
|
||||
<div class="o_hr_attendance_kiosk_mode_container o_home_menu_background d-flex flex-column align-items-center justify-content-center h-100 text-center">
|
||||
<span class="o_hr_attendance_kiosk_backdrop position-absolute top-0 start-0 end-0 bottom-0 bg-black-25"/>
|
||||
<div class="o_hr_attendance_clock bg-black-50 p-3 py-md-2 m-0 mt-md-5 me-md-5 h2 text-white font-monospace"/>
|
||||
<div t-attf-class="o_hr_attendance_kiosk_mode flex-grow-1 flex-md-grow-0 card pb-3 px-0 px-lg-5 {{kioskModeClasses ? kioskModeClasses : '' }}">
|
||||
<div class="card-body d-flex flex-column p-0 p-md-4">
|
||||
<t t-out="bodyContent"></t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceUserBadge">
|
||||
<div class="o_hr_attendance_user_badge o_home_menu_background d-flex align-items-end justify-content-center flex-grow-1 pt-5 pt-md-4 bg-odoo">
|
||||
<img class="img rounded-circle mb-n5" t-attf-src="/web/image?model=hr.employee.public&field=avatar_128&id=#{userId}" t-att-title="userName" height="80" t-att-alt="userName"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceCheckInOutButtons">
|
||||
<div class="flex-grow-1">
|
||||
<button t-attf-class="o_hr_attendance_sign_in_out_icon btn btn-{{ checked_in ? 'warning' : 'success' }} align-self-center px-5 py-3 mt-4 mb-2">
|
||||
<span class="align-middle fs-2 me-3 text-white" t-if="!checked_in">Check IN</span>
|
||||
<i t-attf-class="fa fa-4x fa-sign-{{ checked_in ? 'out' : 'in' }} align-middle"/>
|
||||
<span class="align-middle fs-2 ms-3" t-if="checked_in">Check OUT</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceKioskMode">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="kioskModeClasses" t-translation="off">o_barcode_main pt-5</t>
|
||||
<t t-set="bodyContent">
|
||||
<h2 class="mb-2"><small>Welcome to</small> <t t-esc="widget.company_name"/></h2>
|
||||
<img t-attf-src="{{widget.company_image_url}}" alt="Company Logo" class="o_hr_attendance_kiosk_company_image align-self-center img img-fluid mb-3" width="200"/>
|
||||
<div class="o_hr_attendance_kiosk_welcome_row d-flex flex-column pb-5">
|
||||
<div class="col-md-5 mt-5 mb-5 mb-md-0 align-self-center" t-if="widget.kiosk_mode != 'manual'">
|
||||
<img src="/barcodes/static/img/barcode.png" alt="Barcode" style="width: 115px;height: 60px"/>
|
||||
<h6 class="mt-2 text-muted">Scan your badge</h6>
|
||||
</div>
|
||||
<div class="mt-5 align-self-end" t-if="widget.kiosk_mode == 'barcode_manual'">
|
||||
<button class="o_hr_attendance_button_employees btn btn-link">
|
||||
Identify Manually
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-5 align-self-center" t-if="widget.kiosk_mode == 'manual'">
|
||||
<button class="o_hr_attendance_button_employees btn btn-primary px-5 py-3 mt-4 mb-2">
|
||||
<span class="fs-2">Identify Manually</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceMyMainMenu">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-if="widget.employee">
|
||||
<t t-set="checked_in" t-value="widget.employee.attendance_state=='checked_in'"/>
|
||||
|
||||
<t t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.employee.id"/>
|
||||
<t t-set="userName" t-value="widget.employee.name"/>
|
||||
</t>
|
||||
|
||||
<div class="flex-grow-1">
|
||||
<h1 class="mt-5" t-esc="widget.employee.name"/>
|
||||
<h3><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
|
||||
<h4 class="mt0 mb0 text-muted" t-if="checked_in">Today's work hours: <span t-esc="widget.hours_today"/></h4>
|
||||
</div>
|
||||
|
||||
<t t-call="HrAttendanceCheckInOutButtons"/>
|
||||
</t>
|
||||
<div class="alert alert-warning" t-else="">
|
||||
<b>Warning</b> : Your user should be linked to an employee to use attendance.<br/> Please contact your administrator.
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceKioskConfirm">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-set="checked_in" t-value="widget.employee_state=='checked_in'"/>
|
||||
|
||||
<button class="o_hr_attendance_back_button btn btn-block btn-secondary btn-lg d-block d-md-none py-5">
|
||||
<i class="fa fa-chevron-left me-2"/> Go back
|
||||
</button>
|
||||
|
||||
<t t-if="widget.employee_id" t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.employee_id"/>
|
||||
<t t-set="userName" t-value="widget.employee_name"/>
|
||||
</t>
|
||||
|
||||
<button class="o_hr_attendance_back_button o_hr_attendance_back_button_md btn btn-secondary d-none d-md-inline-flex align-items-center position-absolute top-0 start-0 rounded-circle">
|
||||
<i class="fa fa-2x fa-fw fa-chevron-left me-1" role="img" aria-label="Go back" title="Go back"/>
|
||||
</button>
|
||||
|
||||
<div t-if="widget.employee_id" class="flex-grow-1">
|
||||
<h1 class="mt-5 mb8"><t t-esc="widget.employee_name"/></h1>
|
||||
<h3 class="mt8 mb24"><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
|
||||
<h4 class="mt0 mb0 text-muted" t-if="checked_in">Today's work hours: <span t-esc="widget.employee_hours_today"/></h4>
|
||||
|
||||
<t t-if="!widget.use_pin" t-call="HrAttendanceCheckInOutButtons"/>
|
||||
|
||||
<t t-else="">
|
||||
<h3 class="mt-4 mb0 text-muted">Please enter your PIN to <b t-if="checked_in">check out</b><b t-else="">check in</b></h3>
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2 o_hr_attendance_pin_pad">
|
||||
<div class="row g-0" >
|
||||
<div class="col-12 mb8 mt8">
|
||||
<input class="o_hr_attendance_PINbox border-0 bg-white fs-1 text-center" type="password" disabled="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<t t-foreach="['1', '2', '3', '4', '5', '6', '7', '8', '9', ['C', 'btn-warning'], '0', ['ok', 'btn-primary']]" t-as="btn_name">
|
||||
<div class="col-4 p-1">
|
||||
<a href="#" t-attf-class="o_hr_attendance_PINbox_button btn {{btn_name[1]? btn_name[1] : 'btn-secondary border'}} btn-block btn-lg {{ 'o_hr_attendance_pin_pad_button_' + btn_name[0] }} d-flex align-items-center justify-content-center">
|
||||
<t t-esc="btn_name[0]"/>
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div t-else="" class="alert alert-danger mx-3" role="alert">
|
||||
<h4 class="alert-heading">Error: could not find corresponding employee.</h4>
|
||||
<p>Please return to the main menu.</p>
|
||||
</div>
|
||||
<a role="button" class="oe_attendance_sign_in_out" aria-label="Sign out" title="Sign out"/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceGreetingMessage">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-set="checked_in" t-value="widget.employee_state=='checked_in'"/>
|
||||
|
||||
<t t-if="widget.attendance">
|
||||
<t t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.attendance.employee_id[0]"/>
|
||||
<t t-set="userName" t-value="widget.employee_name"/>
|
||||
</t>
|
||||
|
||||
<div t-if="widget.attendance.check_out" class="flex-grow-1">
|
||||
<h1 class="mt-5">Goodbye <t t-esc="widget.employee_name"/>!</h1>
|
||||
<h2 class="o_hr_attendance_message_message mt4 mb24"/>
|
||||
<div class="alert alert-info fs-2 mx-3" role="status">
|
||||
Checked out at <b><t t-esc="widget.attendance.check_out_time"/></b>
|
||||
<br/><b><t t-esc="widget.hours_today"/></b>
|
||||
</div>
|
||||
<t t-if="widget.show_total_overtime">
|
||||
<div t-att-class="'alert ' + (widget.today_overtime_float >= 0 ? 'alert-success' : 'alert-danger') + ' h3 mx-3'" role="status">
|
||||
Extra hours today:
|
||||
<span t-esc="widget.today_overtime"/>
|
||||
</div>
|
||||
<t t-if="widget.total_overtime_float > 0">
|
||||
Total extra hours: <span t-esc="widget.total_overtime"/>
|
||||
</t>
|
||||
</t>
|
||||
<h3 class="o_hr_attendance_random_message fst-italic mb24"/>
|
||||
<div class="o_hr_attendance_warning_message mt24 alert alert-warning" style="display:none" role="alert"/>
|
||||
</div>
|
||||
<div t-else="" class="flex-grow-1">
|
||||
<h1 class="mt-5 mb0">Welcome <t t-esc="widget.employee_name"/>!</h1>
|
||||
<h2 class="o_hr_attendance_message_message mt4 mb24"/>
|
||||
<div class="alert alert-info fs-2 mx-3" role="status">
|
||||
Checked in at <b><t t-esc="widget.attendance.check_in_time"/></b>
|
||||
</div>
|
||||
<h3 class="o_hr_attendance_random_message mb24"/>
|
||||
<div class="o_hr_attendance_warning_message mt24 alert alert-warning" style="display:none" role="alert"/>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<button class="o_hr_attendance_button_dismiss align-self-center btn btn-primary btn-lg px-5 py-3">
|
||||
<span class="fs-2" t-if="widget.attendance.check_out">Goodbye</span>
|
||||
<span class="fs-2" t-else="">OK</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="flex-grow-1">
|
||||
<div class="alert alert-warning mt-5 mx-3" role="alert">
|
||||
<h4 class="alert-heading">Invalid request</h4>
|
||||
<p>Please return to the main menu.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<button class="o_hr_attendance_button_dismiss btn btn-primary btn-lg fs-2 px-5 py-3">
|
||||
<i class="fa fa-chevron-left me-2"/>
|
||||
<span class="fs-2">Go back</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
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', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
'hr.employee': {
|
||||
fields: {
|
||||
name: {string: 'Name', type: 'char'},
|
||||
attendance_state: {
|
||||
string: 'State',
|
||||
type: 'selection',
|
||||
selection: [['checked_in', "In"], ['checked_out', "Out"]],
|
||||
default: 1,
|
||||
},
|
||||
user_id: {string: 'user ID', type: 'integer'},
|
||||
barcode: {string:'barcode', type: 'integer'},
|
||||
hours_today: {string:'Hours today', type: 'float'},
|
||||
overtime: {string: 'Overtime', type: 'float'},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
name: "Employee A",
|
||||
attendance_state: 'checked_out',
|
||||
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'},
|
||||
attendance_kiosk_mode: {type: 'char'},
|
||||
attendance_barcode_source: {type: 'char'},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
name: "Company A",
|
||||
attendance_kiosk_mode: 'barcode_manual',
|
||||
attendance_barcode_source: 'front',
|
||||
}],
|
||||
},
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.module('My attendances (client action)');
|
||||
|
||||
QUnit.test('simple rendering', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
var clientAction = new MyAttendances(null, {});
|
||||
await testUtils.mock.addMockEnvironment(clientAction, {
|
||||
data: this.data,
|
||||
session: {
|
||||
uid: 1,
|
||||
},
|
||||
});
|
||||
await clientAction.appendTo($target);
|
||||
|
||||
assert.strictEqual(clientAction.$('.o_hr_attendance_kiosk_mode h1').text(), 'Employee A',
|
||||
"should have rendered the client action without crashing");
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Attendance Kiosk Mode Test', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
var self = this;
|
||||
var rpcCount = 0;
|
||||
var clientAction = new KioskMode(null, {});
|
||||
await testUtils.mock.addMockEnvironment(clientAction, {
|
||||
data: this.data,
|
||||
session: {
|
||||
uid: 1,
|
||||
user_context: {
|
||||
allowed_company_ids: [1],
|
||||
}
|
||||
},
|
||||
mockRPC: function(route, args) {
|
||||
if (args.method === 'attendance_scan' && args.model === 'hr.employee') {
|
||||
|
||||
rpcCount++;
|
||||
return Promise.resolve(self.data['hr.employee'].records[0]);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
await 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', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
var self = this;
|
||||
var rpcCount = 0;
|
||||
|
||||
var clientActions = [];
|
||||
let greetingMessageCreated;
|
||||
async 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);
|
||||
await testUtils.mock.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.
|
||||
*/
|
||||
greetingMessageCreated = createGreetingMessage (target, args.args[0]);
|
||||
return Promise.resolve({action: action});
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
await clientAction.appendTo(target);
|
||||
|
||||
clientActions.push(clientAction);
|
||||
}
|
||||
|
||||
// init - mock coming from kiosk
|
||||
await createGreetingMessage ($target, 1);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
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);
|
||||
await greetingMessageCreated;
|
||||
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);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
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);
|
||||
await greetingMessageCreated;
|
||||
assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.');
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.');
|
||||
assert.strictEqual(rpcCount, 2, 'RPC call should have been done only twice.');
|
||||
|
||||
_.each(clientActions.reverse(), function(clientAction) {
|
||||
clientAction.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue