summaryrefslogtreecommitdiff
path: root/addons/pos_hr/static/src
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/pos_hr/static/src
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/pos_hr/static/src')
-rw-r--r--addons/pos_hr/static/src/css/pos.css130
-rw-r--r--addons/pos_hr/static/src/js/CashierName.js59
-rw-r--r--addons/pos_hr/static/src/js/Chrome.js22
-rw-r--r--addons/pos_hr/static/src/js/HeaderLockButton.js25
-rw-r--r--addons/pos_hr/static/src/js/LoginScreen.js74
-rw-r--r--addons/pos_hr/static/src/js/models.js97
-rw-r--r--addons/pos_hr/static/src/js/useSelectEmployee.js46
-rw-r--r--addons/pos_hr/static/src/xml/CashierName.xml10
-rw-r--r--addons/pos_hr/static/src/xml/Chrome.xml11
-rw-r--r--addons/pos_hr/static/src/xml/HeaderLockButton.xml15
-rw-r--r--addons/pos_hr/static/src/xml/LoginScreen.xml29
11 files changed, 518 insertions, 0 deletions
diff --git a/addons/pos_hr/static/src/css/pos.css b/addons/pos_hr/static/src/css/pos.css
new file mode 100644
index 00000000..ce9be2a9
--- /dev/null
+++ b/addons/pos_hr/static/src/css/pos.css
@@ -0,0 +1,130 @@
+.pos .login-overlay{
+ position: fixed;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ width: 100%;
+ height:100%;
+ z-index:1000;
+ background: linear-gradient(to right bottom, #77717e, #c9a8a9);
+}
+.pos .login-overlay:before {
+ content: '';
+ background-image: url(../../img/login-bg-overlay.svg);
+ background-color: rgba(0, 0, 0, 0.3);
+ position: fixed;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ width: 100%;
+ height:100%;
+}
+.pos .screen-login{
+ position: absolute;
+ top: 0; left: 0; right: 0; bottom: 0;
+ margin: auto;
+ max-width: 550px;
+ width:100%;
+ height:300px;
+ text-align:center;
+ font-size:20px;
+ font-weight:bold;
+ background-color: #F0EEEE;
+ border-radius: 3px;
+ z-index:1200;
+ font-family: 'Lato';
+}
+.pos .login-title{
+ height: 40%;
+ vertical-align: middle;
+ line-height: 5;
+ font-size: larger;
+}
+.pos .login-body{
+ display: flex;
+ justify-content: center;
+ height:37%;
+}
+
+.pos .login-footer{
+ height:18%;
+}
+
+.pos .login-element{
+ float: left;
+ width: 40%;
+ height: 60%;
+}
+.pos .login-barcode-img{
+ width: 80px;
+ height: 55px;
+ background: white;
+ border: 0px;
+}
+.pos .login-barcode-text{
+ color: #999999;
+ font-size: 13px;
+ padding-top: 0.2em;
+}
+.pos .login-or{
+ font-size: 15px;
+ font-style: italic;
+ float: left;
+ width: 10%;
+ height: 100%;
+ line-height: 5;
+}
+.pos .login-button{
+ font-size: initial;
+ height: 100%;
+ color: #555555;
+ border-radius: 5px;
+}
+@media screen and (max-width: 576px) {
+ .pos .screen-login {
+ font-family: 'Lato', sans-serif;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ background-color: #fff;
+ overflow: hidden;
+ }
+ .pos .login-body {
+ flex-direction: column;
+ align-items: center;
+ }
+ .pos .login-button {
+ color: #fff;
+ background-color: #00A09D;
+ border-color: #00A09D;
+ height: 38px;
+ }
+ .pos .popups .popup-selection {
+ width: 90%;
+ }
+ .pos .login-barcode-text {
+ color: #adb5bd;
+ margin-top: 8px;
+ margin-bottom: 0;
+ font-size: 15px;
+ font-weight: 400;
+ line-height: 1.2;
+ }
+ .pos .login-element .o_barcode_mobile_container .o_mobile_barcode {
+ top:0;
+ height: 55px;
+ }
+}
+
+.pos .pos-rightheader .header-button.lock-button {
+ font-size: 20px;
+ color: rgb(94, 185, 55);
+ transition: all 200ms ease-in-out;
+ width: 18px;
+}
+
+.pos .pos-rightheader .header-button.lock-button:hover {
+ color: rgb(197, 52, 0);
+}
diff --git a/addons/pos_hr/static/src/js/CashierName.js b/addons/pos_hr/static/src/js/CashierName.js
new file mode 100644
index 00000000..76283720
--- /dev/null
+++ b/addons/pos_hr/static/src/js/CashierName.js
@@ -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;
+});
diff --git a/addons/pos_hr/static/src/js/Chrome.js b/addons/pos_hr/static/src/js/Chrome.js
new file mode 100644
index 00000000..eacc9561
--- /dev/null
+++ b/addons/pos_hr/static/src/js/Chrome.js
@@ -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.config.module_pos_hr || this.env.pos.get('cashier').role == 'manager';
+ }
+ };
+
+ Registries.Component.extend(Chrome, PosHrChrome);
+
+ return Chrome;
+});
diff --git a/addons/pos_hr/static/src/js/HeaderLockButton.js b/addons/pos_hr/static/src/js/HeaderLockButton.js
new file mode 100644
index 00000000..3ad0ee9f
--- /dev/null
+++ b/addons/pos_hr/static/src/js/HeaderLockButton.js
@@ -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;
+});
diff --git a/addons/pos_hr/static/src/js/LoginScreen.js b/addons/pos_hr/static/src/js/LoginScreen.js
new file mode 100644
index 00000000..bf93abeb
--- /dev/null
+++ b/addons/pos_hr/static/src/js/LoginScreen.js
@@ -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;
+});
diff --git a/addons/pos_hr/static/src/js/models.js b/addons/pos_hr/static/src/js/models.js
new file mode 100644
index 00000000..9b4c8f4a
--- /dev/null
+++ b/addons/pos_hr/static/src/js/models.js
@@ -0,0 +1,97 @@
+odoo.define('pos_hr.employees', function (require) {
+ "use strict";
+
+var models = require('point_of_sale.models');
+
+models.load_models([{
+ model: 'hr.employee',
+ fields: ['name', 'id', 'user_id'],
+ domain: function(self){
+ return self.config.employee_ids.length > 0
+ ? [
+ '&',
+ ['company_id', '=', self.config.company_id[0]],
+ '|',
+ ['user_id', '=', self.user.id],
+ ['id', 'in', self.config.employee_ids],
+ ]
+ : [['company_id', '=', self.config.company_id[0]]];
+ },
+ loaded: function(self, employees) {
+ if (self.config.module_pos_hr) {
+ self.employees = employees;
+ self.employee_by_id = {};
+ self.employees.forEach(function(employee) {
+ self.employee_by_id[employee.id] = employee;
+ var hasUser = self.users.some(function(user) {
+ if (user.id === employee.user_id[0]) {
+ employee.role = user.role;
+ return true;
+ }
+ return false;
+ });
+ if (!hasUser) {
+ employee.role = 'cashier';
+ }
+ });
+ }
+ }
+}]);
+
+var posmodel_super = models.PosModel.prototype;
+models.PosModel = models.PosModel.extend({
+ load_server_data: function () {
+ 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 = self.rpc({
+ model: 'hr.employee',
+ method: 'get_barcodes_and_pin_hashed',
+ args: [employee_ids],
+ });
+ return records.then(function (employee_data) {
+ self.employees.forEach(function (employee) {
+ var data = _.findWhere(employee_data, {'id': employee.id});
+ if (data !== undefined){
+ employee.barcode = data.barcode;
+ employee.pin = data.pin;
+ }
+ });
+ });
+ });
+ },
+ set_cashier: function(employee) {
+ posmodel_super.set_cashier.apply(this, arguments);
+ const selectedOrder = this.get_order();
+ if (selectedOrder && !selectedOrder.get_orderlines().length) {
+ // Order without lines can be considered to be un-owned by any employee.
+ // We set the employee on that order to the currently set employee.
+ selectedOrder.employee = employee;
+ }
+ }
+});
+
+var super_order_model = models.Order.prototype;
+models.Order = models.Order.extend({
+ initialize: function (attributes, options) {
+ super_order_model.initialize.apply(this, arguments);
+ if (!options.json) {
+ this.employee = this.pos.get_cashier();
+ }
+ },
+ init_from_JSON: function (json) {
+ super_order_model.init_from_JSON.apply(this, arguments);
+ if (this.pos.config.module_pos_hr) {
+ this.employee = this.pos.employee_by_id[json.employee_id];
+ }
+ },
+ export_as_JSON: function () {
+ const json = super_order_model.export_as_JSON.apply(this, arguments);
+ if (this.pos.config.module_pos_hr) {
+ json.employee_id = this.employee ? this.employee.id : false;
+ }
+ return json;
+ },
+});
+
+});
diff --git a/addons/pos_hr/static/src/js/useSelectEmployee.js b/addons/pos_hr/static/src/js/useSelectEmployee.js
new file mode 100644
index 00000000..ef8e58a5
--- /dev/null
+++ b/addons/pos_hr/static/src/js/useSelectEmployee.js
@@ -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;
+});
diff --git a/addons/pos_hr/static/src/xml/CashierName.xml b/addons/pos_hr/static/src/xml/CashierName.xml
new file mode 100644
index 00000000..9caddada
--- /dev/null
+++ b/addons/pos_hr/static/src/xml/CashierName.xml
@@ -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>
diff --git a/addons/pos_hr/static/src/xml/Chrome.xml b/addons/pos_hr/static/src/xml/Chrome.xml
new file mode 100644
index 00000000..430ed75d
--- /dev/null
+++ b/addons/pos_hr/static/src/xml/Chrome.xml
@@ -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>
diff --git a/addons/pos_hr/static/src/xml/HeaderLockButton.xml b/addons/pos_hr/static/src/xml/HeaderLockButton.xml
new file mode 100644
index 00000000..d24a9f74
--- /dev/null
+++ b/addons/pos_hr/static/src/xml/HeaderLockButton.xml
@@ -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>
diff --git a/addons/pos_hr/static/src/xml/LoginScreen.xml b/addons/pos_hr/static/src/xml/LoginScreen.xml
new file mode 100644
index 00000000..5d96f2b1
--- /dev/null
+++ b/addons/pos_hr/static/src/xml/LoginScreen.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates id="template" xml:space="preserve">
+ <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="shopName" />
+ </div>
+ <div class="login-body">
+ <span class="login-element">
+ <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"
+ t-on-click="selectCashier">Select Cashier</button>
+ </span>
+ </div>
+ <div class="login-footer">
+ <small>
+ <button class="login-button close-session" t-on-click="closeSession">Close session</button>
+ </small>
+ </div>
+ </div>
+ </div>
+ </t>
+</templates>