feat: enforce PIN on order load and improve employee attribution logic for POS orders
This commit is contained in:
parent
1d7162cf61
commit
16e5e25c76
@ -5,6 +5,7 @@ This Odoo 19 module enhances the security and auditability of the Point of Sale
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Mandatory PIN on Table Selection**: Prevents unauthorized access to tables. Every table selection triggers a PIN prompt to identify the employee taking the order.
|
- **Mandatory PIN on Table Selection**: Prevents unauthorized access to tables. Every table selection triggers a PIN prompt to identify the employee taking the order.
|
||||||
|
- **Mandatory PIN on Load Order**: Enforces PIN entry when a draft order is loaded/resumed from the Orders tab (`TicketScreen`).
|
||||||
- **Mandatory PIN on Payment**: Requires PIN authentication before processing payments to ensure the transaction is handled by an authorized employee.
|
- **Mandatory PIN on Payment**: Requires PIN authentication before processing payments to ensure the transaction is handled by an authorized employee.
|
||||||
- **Role-Based Payment Gating**: Cross-checks employee roles (using `pos_employee_role`) to prevent roles like 'waiter' from processing payments.
|
- **Role-Based Payment Gating**: Cross-checks employee roles (using `pos_employee_role`) to prevent roles like 'waiter' from processing payments.
|
||||||
- **Order Attribution**:
|
- **Order Attribution**:
|
||||||
@ -15,6 +16,7 @@ This Odoo 19 module enhances the security and auditability of the Point of Sale
|
|||||||
- Hides the "Course" button on the POS UI.
|
- Hides the "Course" button on the POS UI.
|
||||||
- Hides the "Transfer Course" button on the POS UI.
|
- Hides the "Transfer Course" button on the POS UI.
|
||||||
- Hides the save order for later (upload icon) button from the POS UI.
|
- Hides the save order for later (upload icon) button from the POS UI.
|
||||||
|
- Hides the "Set Table" button from Register/Direct Sale orders.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
|
|||||||
21
static/src/app/models/pos_order.js
Normal file
21
static/src/app/models/pos_order.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { PosOrder } from "@point_of_sale/app/models/pos_order";
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
patch(PosOrder.prototype, {
|
||||||
|
setup(vals) {
|
||||||
|
super.setup(...arguments);
|
||||||
|
if (vals.employee_id) {
|
||||||
|
this.original_employee_id = this.models["hr.employee"].get(vals.employee_id.id || vals.employee_id) || vals.employee_id;
|
||||||
|
} else if (this.employee_id) {
|
||||||
|
this.original_employee_id = this.employee_id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
serializeForORM(opts = {}) {
|
||||||
|
const data = super.serializeForORM(opts);
|
||||||
|
if (this.original_employee_id) {
|
||||||
|
data.employee_id = this.original_employee_id.id || this.original_employee_id;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -4,6 +4,9 @@
|
|||||||
<xpath expr="//button[hasclass('pay-order-button')]" position="attributes">
|
<xpath expr="//button[hasclass('pay-order-button')]" position="attributes">
|
||||||
<attribute name="t-if">pos.canPay</attribute>
|
<attribute name="t-if">pos.canPay</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//button[hasclass('set-table')]" position="replace">
|
||||||
|
<!-- Hidden "Set Table" button on Register order -->
|
||||||
|
</xpath>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-name="pos_custom_access.ProductScreen" t-inherit="point_of_sale.ProductScreen" t-inherit-mode="extension">
|
<t t-name="pos_custom_access.ProductScreen" t-inherit="point_of_sale.ProductScreen" t-inherit-mode="extension">
|
||||||
|
|||||||
26
static/src/app/screens/ticket_screen/ticket_screen.js
Normal file
26
static/src/app/screens/ticket_screen/ticket_screen.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/** @odoo-module */
|
||||||
|
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
import { TicketScreen } from "@point_of_sale/app/screens/ticket_screen/ticket_screen";
|
||||||
|
|
||||||
|
patch(TicketScreen.prototype, {
|
||||||
|
async setOrder(order) {
|
||||||
|
// Enforce PIN entry when loading/selecting an order from the TicketScreen (Orders tab)
|
||||||
|
const cashier = await this.pos._selectCashierByPin();
|
||||||
|
if (!cashier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pos.setCashier(cashier);
|
||||||
|
|
||||||
|
// Ensure the order has its original employee id preserved when loading
|
||||||
|
if (order) {
|
||||||
|
if (!order.original_employee_id) {
|
||||||
|
order.original_employee_id = order.employee_id || cashier;
|
||||||
|
}
|
||||||
|
order.uiState.is_authorized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.setOrder(order);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -67,6 +67,39 @@ patch(PosStore.prototype, {
|
|||||||
return super.pay();
|
return super.pay();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createNewOrder() {
|
||||||
|
const order = super.createNewOrder(...arguments);
|
||||||
|
if (order && this.config.module_pos_hr) {
|
||||||
|
order.original_employee_id = this.getCashier();
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
},
|
||||||
|
|
||||||
|
setCashier(employee) {
|
||||||
|
super.setCashier(...arguments);
|
||||||
|
const order = this.getOrder();
|
||||||
|
if (order && !order.getOrderlines().length) {
|
||||||
|
order.original_employee_id = employee;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addLineToCurrentOrder(vals, opt = {}, configure = true) {
|
||||||
|
const order = this.getOrder();
|
||||||
|
if (order && !order.original_employee_id) {
|
||||||
|
order.original_employee_id = order.employee_id || this.getCashier();
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = super.addLineToCurrentOrder(...arguments);
|
||||||
|
|
||||||
|
if (order && order.original_employee_id) {
|
||||||
|
order.employee_id = order.original_employee_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async _selectCashierByPin() {
|
async _selectCashierByPin() {
|
||||||
if (!this.config.module_pos_hr) {
|
if (!this.config.module_pos_hr) {
|
||||||
return this.getCashier();
|
return this.getCashier();
|
||||||
|
|||||||
14
static/src/app/utils/order_payment_validation.js
Normal file
14
static/src/app/utils/order_payment_validation.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import OrderPaymentValidation from "@point_of_sale/app/utils/order_payment_validation";
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
patch(OrderPaymentValidation.prototype, {
|
||||||
|
async validateOrder(isForceValidate) {
|
||||||
|
const originalEmployeeId = this.order.original_employee_id || this.order.employee_id;
|
||||||
|
|
||||||
|
await super.validateOrder(...arguments);
|
||||||
|
|
||||||
|
if (originalEmployeeId) {
|
||||||
|
this.order.employee_id = originalEmployeeId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user