[REF] pos_hr: migration to OWL framework
This commit is contained in:
committed by
Pierre Masereel
parent
d8726778b6
commit
42b85f1115
@@ -21,5 +21,10 @@ The actual till still requires one user but an unlimited number of employees can
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'qweb': ['static/src/xml/pos.xml'],
|
||||
'qweb': [
|
||||
'static/src/xml/HeaderLockButton.xml',
|
||||
'static/src/xml/Chrome.xml',
|
||||
'static/src/xml/CashierName.xml',
|
||||
'static/src/xml/LoginScreen.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -79,3 +79,14 @@
|
||||
color: #555555;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.pos .pos-rightheader .header-button.lock-button {
|
||||
font-size: 20px;
|
||||
color: green;
|
||||
transition: all 200ms ease-in-out;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.pos .pos-rightheader .header-button.lock-button:hover {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
odoo.define('pos_hr.CashierName', function (require) {
|
||||
'use strict';
|
||||
|
||||
const CashierName = require('point_of_sale.CashierName');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const useSelectEmployee = require('pos_hr.useSelectEmployee');
|
||||
const { useBarcodeReader } = require('point_of_sale.custom_hooks');
|
||||
|
||||
const PosHrCashierName = (CashierName) =>
|
||||
class extends CashierName {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
const { selectEmployee, askPin } = useSelectEmployee();
|
||||
this.askPin = askPin;
|
||||
this.selectEmployee = selectEmployee;
|
||||
useBarcodeReader({ cashier: this._onCashierScan });
|
||||
}
|
||||
mounted() {
|
||||
this.env.pos.on('change:cashier', this.render, this);
|
||||
}
|
||||
willUnmount() {
|
||||
this.env.pos.off('change:cashier', null, this);
|
||||
}
|
||||
async selectCashier() {
|
||||
if (!this.env.pos.config.module_pos_hr) return;
|
||||
|
||||
const list = this.env.pos.employees
|
||||
.filter((employee) => employee.id !== this.env.pos.get_cashier().id)
|
||||
.map((employee) => {
|
||||
return {
|
||||
id: employee.id,
|
||||
item: employee,
|
||||
label: employee.name,
|
||||
isSelected: false,
|
||||
};
|
||||
});
|
||||
|
||||
const employee = await this.selectEmployee(list);
|
||||
if (employee) {
|
||||
this.env.pos.set_cashier(employee);
|
||||
}
|
||||
}
|
||||
async _onCashierScan(code) {
|
||||
const employee = this.env.pos.employees.find(
|
||||
(emp) => emp.barcode === Sha1.hash(code.code)
|
||||
);
|
||||
|
||||
if (!employee || employee === this.env.pos.get_cashier()) return;
|
||||
|
||||
if (!employee.pin || (await this.askPin(employee))) {
|
||||
this.env.pos.set_cashier(employee);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(CashierName, PosHrCashierName);
|
||||
|
||||
return CashierName;
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
odoo.define('pos_hr.chrome', function (require) {
|
||||
'use strict';
|
||||
|
||||
const Chrome = require('point_of_sale.Chrome');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
|
||||
const PosHrChrome = (Chrome) =>
|
||||
class extends Chrome {
|
||||
async start() {
|
||||
await super.start();
|
||||
this.env.pos.on('change:cashier', this.render, this);
|
||||
if (this.env.pos.config.module_pos_hr) this.showTempScreen('LoginScreen');
|
||||
}
|
||||
get headerButtonIsShown() {
|
||||
return this.env.pos.get('cashier').role == 'manager';
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(Chrome, PosHrChrome);
|
||||
|
||||
return Chrome;
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
odoo.define('point_of_sale.HeaderLockButton', function(require) {
|
||||
'use strict';
|
||||
|
||||
const PosComponent = require('point_of_sale.PosComponent');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const { useState } = owl;
|
||||
|
||||
class HeaderLockButton extends PosComponent {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ isUnlockIcon: true, title: 'Unlocked' });
|
||||
}
|
||||
async showLoginScreen() {
|
||||
await this.showTempScreen('LoginScreen');
|
||||
}
|
||||
onMouseOver(isMouseOver) {
|
||||
this.state.isUnlockIcon = !isMouseOver;
|
||||
this.state.title = isMouseOver ? 'Lock' : 'Unlocked';
|
||||
}
|
||||
}
|
||||
|
||||
Registries.Component.add(HeaderLockButton);
|
||||
|
||||
return HeaderLockButton;
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
odoo.define('pos_hr.LoginScreen', function (require) {
|
||||
'use strict';
|
||||
|
||||
const PosComponent = require('point_of_sale.PosComponent');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const useSelectEmployee = require('pos_hr.useSelectEmployee');
|
||||
const { useBarcodeReader } = require('point_of_sale.custom_hooks');
|
||||
|
||||
class LoginScreen extends PosComponent {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
const { selectEmployee, askPin } = useSelectEmployee();
|
||||
this.selectEmployee = selectEmployee;
|
||||
this.askPin = askPin;
|
||||
useBarcodeReader(
|
||||
{
|
||||
cashier: this._barcodeCashierAction,
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
back() {
|
||||
this.props.resolve({ confirmed: false, payload: false });
|
||||
this.trigger('close-temp-screen');
|
||||
}
|
||||
confirm() {
|
||||
this.props.resolve({ confirmed: true, payload: true });
|
||||
this.trigger('close-temp-screen');
|
||||
}
|
||||
get shopName() {
|
||||
return this.env.pos.config.name;
|
||||
}
|
||||
closeSession() {
|
||||
this.trigger('close-pos');
|
||||
}
|
||||
async selectCashier() {
|
||||
const list = this.env.pos.employees.map((employee) => {
|
||||
return {
|
||||
id: employee.id,
|
||||
item: employee,
|
||||
label: employee.name,
|
||||
isSelected: false,
|
||||
};
|
||||
});
|
||||
|
||||
const employee = await this.selectEmployee(list);
|
||||
if (employee) {
|
||||
this.env.pos.set_cashier(employee);
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
async _barcodeCashierAction(code) {
|
||||
let theEmployee;
|
||||
for (let employee of this.env.pos.employees) {
|
||||
if (employee.barcode === Sha1.hash(code.code)) {
|
||||
theEmployee = employee;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!theEmployee) return;
|
||||
|
||||
if (!theEmployee.pin || (await this.askPin(theEmployee))) {
|
||||
this.env.pos.set_cashier(theEmployee);
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
LoginScreen.template = 'LoginScreen';
|
||||
|
||||
Registries.Component.add(LoginScreen);
|
||||
|
||||
return LoginScreen;
|
||||
});
|
||||
@@ -1,124 +0,0 @@
|
||||
odoo.define('pos_hr.chrome', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var chrome = require('point_of_sale.chrome');
|
||||
var _t = core._t;
|
||||
var _lt = core._lt;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
|
||||
/* ------- The User Name Widget ------- */
|
||||
|
||||
// Displays the current cashier's name and allows
|
||||
// to switch between cashiers.
|
||||
|
||||
chrome.UsernameWidget.include({
|
||||
renderElement: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
this.$el.click(function(){
|
||||
self.click_username();
|
||||
});
|
||||
},
|
||||
click_username: function(){
|
||||
if(!this.pos.config.module_pos_hr) { return; }
|
||||
var self = this;
|
||||
this.gui.select_employee({
|
||||
'security': true,
|
||||
'current_employee': this.pos.get_cashier(),
|
||||
'title': _t('Change Cashier'),
|
||||
}).then(function(employee){
|
||||
self.pos.set_cashier(employee);
|
||||
self.chrome.widget.username.renderElement();
|
||||
self.renderElement();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var HeaderCloseButtonWidget = chrome.HeaderButtonWidget.extend({
|
||||
start: function(){
|
||||
if (this.pos.config.module_pos_hr) {
|
||||
var self = this;
|
||||
this.pos.bind('change:cashier', this._show_hide_close_button , this);
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
_show_hide_close_button: function(){
|
||||
if (this.pos.get('cashier').role == 'manager') {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var HeaderLockButtonWidget = chrome.HeaderButtonWidget.extend({
|
||||
init: function(parent, options) {
|
||||
this._super(parent, options);
|
||||
this.icon = 'fa-unlock';
|
||||
this.icon_color = 'green';
|
||||
this.icon_mouseover = 'fa-lock';
|
||||
this.icon_color_mouseover = 'red';
|
||||
},
|
||||
|
||||
start: function(){
|
||||
if (this.pos.config.module_pos_hr) {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
|
||||
renderElement: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
this.iconElement = this.$el.find("i");
|
||||
this.$el.css('font-size','20px');
|
||||
this.iconElement.addClass(this.icon);
|
||||
this.$el.css('color',this.icon_color);
|
||||
this.$el.mouseover(function(){
|
||||
self.iconElement.addClass(self.icon_mouseover).removeClass(self.icon);
|
||||
$(this).css('color', self.icon_color_mouseover);
|
||||
}).mouseleave(function(){
|
||||
self.iconElement.addClass(self.icon).removeClass(self.icon_mouseover);
|
||||
$(this).css('color', self.icon_color);
|
||||
});
|
||||
},
|
||||
});
|
||||
chrome.Chrome.include({
|
||||
lock_button_widget: {
|
||||
'name': 'lock_button',
|
||||
'widget': HeaderLockButtonWidget,
|
||||
'append': '.pos-rightheader',
|
||||
'args': {
|
||||
label: _lt('Lock'),
|
||||
action: function() {
|
||||
this.chrome.return_to_login_screen();
|
||||
}
|
||||
}
|
||||
},
|
||||
build_widgets: function() {
|
||||
var self = this;
|
||||
this.widgets.some(function(widget, index){
|
||||
if (widget.name === 'close_button'){
|
||||
widget.widget = HeaderCloseButtonWidget;
|
||||
self.widgets.splice(index, 0, self.lock_button_widget);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this._super();
|
||||
},
|
||||
return_to_login_screen: function() {
|
||||
this.gui.show_screen('login');
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
HeaderLockButtonWidget: HeaderLockButtonWidget,
|
||||
HeaderCloseButtonWidget: HeaderCloseButtonWidget,
|
||||
};
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
odoo.define('pos_hr.gui', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var gui = require('point_of_sale.gui');
|
||||
var _t = core._t;
|
||||
|
||||
gui.Gui.include({
|
||||
_show_first_screen: function () {
|
||||
if (this.pos.config.module_pos_hr) {
|
||||
this.show_screen('login');
|
||||
} else {
|
||||
this._super();
|
||||
}
|
||||
},
|
||||
select_employee: function(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
|
||||
var list = [];
|
||||
this.pos.employees.forEach(function(employee) {
|
||||
if (!options.only_managers || employee.role === 'manager') {
|
||||
list.push({
|
||||
'label': employee.name,
|
||||
'item': employee,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var prom = new Promise(function (resolve, reject) {
|
||||
self.show_popup('selection', {
|
||||
title: options.title || _t('Select User'),
|
||||
list: list,
|
||||
confirm: resolve,
|
||||
cancel: reject,
|
||||
is_selected: function (employee) {
|
||||
return employee === self.pos.get_cashier();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return prom.then(function (employee) {
|
||||
return self.ask_password(employee.pin).then(function(){
|
||||
return employee;
|
||||
});
|
||||
});
|
||||
},
|
||||
// Ask for a password, and checks if it this
|
||||
// the same as specified by the function call.
|
||||
// Returns a promise that resolves on success,
|
||||
// fails on failure.
|
||||
ask_password: function(password) {
|
||||
var self = this;
|
||||
var prom = new Promise(function (resolve, reject) {
|
||||
if (password) {
|
||||
self.show_popup('password',{
|
||||
'title': _t('Password ?'),
|
||||
confirm: function (pw) {
|
||||
if (Sha1.hash(pw) !== password) {
|
||||
self.show_popup('error', _t('Incorrect Password'));
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
return prom;
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,6 @@ odoo.define('pos_hr.employees', function (require) {
|
||||
"use strict";
|
||||
|
||||
var models = require('point_of_sale.models');
|
||||
var rpc = require('web.rpc');
|
||||
|
||||
models.load_models([{
|
||||
model: 'hr.employee',
|
||||
@@ -39,7 +38,7 @@ models.PosModel = models.PosModel.extend({
|
||||
var self = this;
|
||||
return posmodel_super.load_server_data.apply(this, arguments).then(function () {
|
||||
var employee_ids = _.map(self.employees, function(employee){return employee.id;});
|
||||
var records = rpc.query({
|
||||
var records = self.rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'get_barcodes_and_pin_hashed',
|
||||
args: [employee_ids],
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
odoo.define('pos_hr.screens', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var gui = require('point_of_sale.gui');
|
||||
var ScreenWidget = require('point_of_sale.screens').ScreenWidget;
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
ScreenWidget.include({
|
||||
|
||||
// what happens when a cashier id barcode is scanned.
|
||||
// the default behavior is the following :
|
||||
// - if there's an employee with a matching barcode, put it as the active 'cashier', go to cashier mode, and return true
|
||||
// - else : do nothing and return false. You probably want to extend this to show and appropriate error popup...
|
||||
barcode_cashier_action: function(code){
|
||||
var self = this;
|
||||
var employees = this.pos.employees;
|
||||
var prom;
|
||||
for(var i = 0, len = employees.length; i < len; i++){
|
||||
if(employees[i].barcode === Sha1.hash(code.code)){
|
||||
if (employees[i].id !== this.pos.get_cashier().id && employees[i].pin) {
|
||||
prom = this.gui.ask_password(employees[i].pin).then(function(){
|
||||
self.pos.set_cashier(employees[i]);
|
||||
self.chrome.widget.username.renderElement();
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
this.pos.set_cashier(employees[i]);
|
||||
this.chrome.widget.username.renderElement();
|
||||
prom = Promise.resolve(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!prom){
|
||||
this.barcode_error_action(code);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
else {
|
||||
return prom
|
||||
}
|
||||
},
|
||||
show: function() {
|
||||
this._super();
|
||||
this.pos.barcode_reader.set_action_callback('cashier', _.bind(this.barcode_cashier_action, this));
|
||||
},
|
||||
});
|
||||
|
||||
/*--------------------------------------*\
|
||||
| THE LOGIN SCREEN |
|
||||
\*======================================*/
|
||||
|
||||
// The login screen enables employees to log in to the PoS
|
||||
// at startup or after it was locked, with either barcode, pin, or both.
|
||||
|
||||
var LoginScreenWidget = ScreenWidget.extend({
|
||||
template: 'LoginScreenWidget',
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
show: function() {
|
||||
var self = this;
|
||||
this.$('.select-employee').click(function() {
|
||||
self.gui.select_employee({
|
||||
'security': true,
|
||||
'current_employee': self.pos.get_cashier(),
|
||||
'title':_t('Change Cashier'),})
|
||||
.then(function(employee){
|
||||
self.pos.set_cashier(employee);
|
||||
self.chrome.widget.username.renderElement();
|
||||
self.unlock_screen();
|
||||
});
|
||||
});
|
||||
|
||||
this.$('.close-session').click(function() {
|
||||
self.gui.close();
|
||||
});
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
barcode_cashier_action: function(code) {
|
||||
var self = this;
|
||||
return this._super(code).then(function () {
|
||||
self.unlock_screen();
|
||||
});
|
||||
},
|
||||
|
||||
unlock_screen: function() {
|
||||
var screen = (this.gui.pos.get_order() ? this.gui.pos.get_order().get_screen_data('previous-screen') : this.gui.startup_screen) || this.gui.startup_screen;
|
||||
this.gui.show_screen(screen);
|
||||
}
|
||||
});
|
||||
|
||||
gui.define_screen({name:'login', widget: LoginScreenWidget});
|
||||
|
||||
return {
|
||||
LoginScreenWidget: LoginScreenWidget
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
odoo.define('pos_hr.useSelectEmployee', function (require) {
|
||||
'use strict';
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
function useSelectEmployee() {
|
||||
const current = Component.current;
|
||||
|
||||
async function askPin(employee) {
|
||||
const { confirmed, payload: inputPin } = await this.showPopup('NumberPopup', {
|
||||
isPassword: true,
|
||||
title: this.env._t('Password ?'),
|
||||
startingValue: null,
|
||||
});
|
||||
|
||||
if (!confirmed) return false;
|
||||
|
||||
if (employee.pin === Sha1.hash(inputPin)) {
|
||||
return employee;
|
||||
} else {
|
||||
await this.showPopup('ErrorPopup', {
|
||||
title: this.env._t('Incorrect Password'),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function selectEmployee(selectionList) {
|
||||
const { confirmed, payload: employee } = await this.showPopup('SelectionPopup', {
|
||||
title: this.env._t('Change Cashier'),
|
||||
list: selectionList,
|
||||
});
|
||||
|
||||
if (!confirmed) return false;
|
||||
|
||||
if (!employee.pin) {
|
||||
return employee;
|
||||
}
|
||||
|
||||
return await askPin.call(current, employee);
|
||||
}
|
||||
return { askPin: askPin.bind(current), selectEmployee: selectEmployee.bind(current) };
|
||||
}
|
||||
|
||||
return useSelectEmployee;
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="CashierName" t-inherit="point_of_sale.CashierName" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div" position="attributes">
|
||||
<attribute name="t-on-click">selectCashier</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="Chrome" t-inherit="point_of_sale.Chrome" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//HeaderButton" position="replace">
|
||||
<HeaderLockButton t-if="env.pos.config.module_pos_hr" />
|
||||
<HeaderButton t-if="headerButtonIsShown" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="HeaderLockButton" owl="1">
|
||||
<div class="header-button lock-button" t-on-mouseover="onMouseOver(true)"
|
||||
t-on-click="showLoginScreen" t-on-mouseout="onMouseOver(false)">
|
||||
<span class="lock-button">
|
||||
<i class="fa"
|
||||
t-att-class="{ 'fa-unlock': state.isUnlockIcon, 'fa-lock': !state.isUnlockIcon }"
|
||||
role="img" t-att-aria-label="state.title" t-att-title="state.title"></i>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+13
-9
@@ -1,24 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="LoginScreenWidget">
|
||||
<t t-name="LoginScreen" owl="1">
|
||||
<div class="login-overlay">
|
||||
<div class="screen-login">
|
||||
<div class="login-title"><small>Log in to </small><t t-esc="widget.pos.config.name"/></div>
|
||||
<div class="login-title"><small>Log in to </small>
|
||||
<t t-esc="shopName" />
|
||||
</div>
|
||||
<div class="login-body">
|
||||
<span class="login-element">
|
||||
<img class="login-barcode-img" src="/point_of_sale/static/img/barcode.png"/>
|
||||
<img class="login-barcode-img"
|
||||
src="/point_of_sale/static/img/barcode.png" />
|
||||
<div class="login-barcode-text">Scan your badge</div>
|
||||
</span>
|
||||
<span class="login-or">or</span>
|
||||
<span class="login-element">
|
||||
<button class="login-button select-employee">Select Cashier</button>
|
||||
<button class="login-button select-employee"
|
||||
t-on-click="selectCashier">Select Cashier</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="login-footer">
|
||||
<small>
|
||||
<button class="login-button close-session">Close session</button>
|
||||
</small>
|
||||
</div>
|
||||
<div class="login-footer">
|
||||
<small>
|
||||
<button class="login-button close-session" t-on-click="closeSession">Close session</button>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
@@ -0,0 +1,43 @@
|
||||
odoo.define('point_of_sale.tour.PosHr', function (require) {
|
||||
'use strict';
|
||||
|
||||
const { PosHr } = require('pos_hr.tour.PosHrTourMethods');
|
||||
const { ProductScreen } = require('point_of_sale.tour.ProductScreenTourMethods');
|
||||
const { ErrorPopup } = require('point_of_sale.tour.ErrorPopupTourMethods');
|
||||
const { NumberPopup } = require('point_of_sale.tour.NumberPopupTourMethods');
|
||||
const { SelectionPopup } = require('point_of_sale.tour.SelectionPopupTourMethods');
|
||||
const { getSteps, startSteps } = require('point_of_sale.tour.utils');
|
||||
const Tour = require('web_tour.tour');
|
||||
|
||||
startSteps();
|
||||
|
||||
PosHr.check.loginScreenIsShown();
|
||||
PosHr.do.clickLoginButton();
|
||||
SelectionPopup.check.isShown();
|
||||
SelectionPopup.check.hasSelectionItem('Pos Employee1');
|
||||
SelectionPopup.check.hasSelectionItem('Pos Employee2');
|
||||
SelectionPopup.check.hasSelectionItem('Mitchell Admin');
|
||||
SelectionPopup.do.clickItem('Pos Employee1');
|
||||
NumberPopup.check.isShown();
|
||||
NumberPopup.do.pressNumpad('2 5 8 1');
|
||||
NumberPopup.check.inputShownIs('••••');
|
||||
NumberPopup.do.clickConfirm();
|
||||
ErrorPopup.check.isShown();
|
||||
ErrorPopup.do.clickConfirm();
|
||||
PosHr.do.clickLoginButton();
|
||||
SelectionPopup.do.clickItem('Pos Employee1');
|
||||
NumberPopup.check.isShown();
|
||||
NumberPopup.do.pressNumpad('2 5 8 0');
|
||||
NumberPopup.check.inputShownIs('••••');
|
||||
NumberPopup.do.clickConfirm();
|
||||
ProductScreen.check.isShown();
|
||||
PosHr.check.cashierNameIs('Pos Employee1');
|
||||
PosHr.do.clickCashierName();
|
||||
SelectionPopup.do.clickItem('Mitchell Admin');
|
||||
PosHr.check.cashierNameIs('Mitchell Admin');
|
||||
PosHr.do.clickLockButton();
|
||||
PosHr.exec.login('Pos Employee2', '1234');
|
||||
ProductScreen.check.isShown();
|
||||
|
||||
Tour.register('PosHrTour', { test: true, url: '/pos/web' }, getSteps());
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
odoo.define('pos_hr.tour.PosHrTourMethods', function (require) {
|
||||
'use strict';
|
||||
|
||||
const { createTourMethods } = require('point_of_sale.tour.utils');
|
||||
const { SelectionPopup } = require('point_of_sale.tour.SelectionPopupTourMethods');
|
||||
const { NumberPopup } = require('point_of_sale.tour.NumberPopupTourMethods');
|
||||
|
||||
class Do {
|
||||
clickLoginButton() {
|
||||
return [
|
||||
{
|
||||
content: 'click login button',
|
||||
trigger: '.login-overlay .login-button.select-employee',
|
||||
},
|
||||
];
|
||||
}
|
||||
clickLockButton() {
|
||||
return [
|
||||
{
|
||||
content: 'click lock button',
|
||||
trigger: '.header-button .lock-button',
|
||||
},
|
||||
];
|
||||
}
|
||||
clickCashierName() {
|
||||
return [
|
||||
{
|
||||
content: 'click cashier name',
|
||||
trigger: '.oe_status .username',
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
class Check {
|
||||
loginScreenIsShown() {
|
||||
return [
|
||||
{
|
||||
content: 'login screen is shown',
|
||||
trigger: '.login-overlay .screen-login .login-body',
|
||||
run: () => {},
|
||||
},
|
||||
];
|
||||
}
|
||||
cashierNameIs(name) {
|
||||
return [
|
||||
{
|
||||
content: `logged cashier is '${name}'`,
|
||||
trigger: `.pos .oe_status .username:contains("${name}")`,
|
||||
run: () => {},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
class Execute {
|
||||
login(name, pin) {
|
||||
const res = this._do.clickLoginButton();
|
||||
res.push(...SelectionPopup._do.clickItem(name));
|
||||
if (pin) {
|
||||
res.push(...NumberPopup._do.pressNumpad(pin.split('').join(' ')));
|
||||
res.push(...NumberPopup._do.clickConfirm());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return { Do, Check, Execute, PosHr: createTourMethods('PosHr', Do, Check, Execute) };
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_frontend
|
||||
@@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tools
|
||||
from odoo.api import Environment
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo.tests import Form, tagged, new_test_user
|
||||
from odoo.addons.point_of_sale.tests.test_frontend import TestPointOfSaleHttpCommon
|
||||
|
||||
|
||||
class TestPosHrHttpCommon(TestPointOfSaleHttpCommon):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.main_pos_config.write({"module_pos_hr": True})
|
||||
|
||||
# Admin employee
|
||||
self.env.ref("hr.employee_admin").write(
|
||||
{"name": "Mitchell Admin", "pin": False}
|
||||
)
|
||||
|
||||
# User employee
|
||||
emp1 = self.env.ref("hr.employee_han")
|
||||
emp1_user = new_test_user(
|
||||
self.env,
|
||||
login="emp1_user",
|
||||
groups="base.group_user",
|
||||
name="Pos Employee1",
|
||||
email="emp1_user@pos.com",
|
||||
)
|
||||
emp1.write({"name": "Pos Employee1", "pin": "2580", "user_id": emp1_user.id})
|
||||
|
||||
# Non-user employee
|
||||
emp2 = self.env.ref("hr.employee_jve")
|
||||
emp2.write({"name": "Pos Employee2", "pin": "1234"})
|
||||
|
||||
with Form(self.main_pos_config) as config:
|
||||
config.employee_ids.add(emp1)
|
||||
config.employee_ids.add(emp2)
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestUi(TestPosHrHttpCommon):
|
||||
def test_01_pos_hr_tour(self):
|
||||
# open a session, the /pos/web controller will redirect to it
|
||||
self.main_pos_config.open_session_cb(check_coa=False)
|
||||
|
||||
self.start_tour(
|
||||
"/pos/web?config_id=%d" % self.main_pos_config.id,
|
||||
"PosHrTour",
|
||||
login="admin",
|
||||
step_delay=50,
|
||||
)
|
||||
@@ -5,9 +5,18 @@
|
||||
<link rel="stylesheet" href="/pos_hr/static/src/css/pos.css"/>
|
||||
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/models.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/screens.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/gui.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/chrome.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/useSelectEmployee.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/Chrome.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/HeaderLockButton.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/CashierName.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/src/js/LoginScreen.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Pos Hr Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/pos_hr/static/tests/tours/PosHrTourMethods.js"></script>
|
||||
<script type="text/javascript" src="/pos_hr/static/tests/tours/PosHrTour.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user