diff --git a/README.md b/README.md index 9dae9aa..22b409d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This Odoo 19 module enhances the security and auditability of the Point of Sale ## 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 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. - **Role-Based Payment Gating**: Cross-checks employee roles (using `pos_employee_role`) to prevent roles like 'waiter' from processing payments. - **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 "Transfer Course" button on 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 diff --git a/static/src/app/models/pos_order.js b/static/src/app/models/pos_order.js new file mode 100644 index 0000000..3ad37de --- /dev/null +++ b/static/src/app/models/pos_order.js @@ -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; + } +}); diff --git a/static/src/app/screens/product_screen/action_pad/action_pad.xml b/static/src/app/screens/product_screen/action_pad/action_pad.xml index 5baaf78..62c0ed1 100644 --- a/static/src/app/screens/product_screen/action_pad/action_pad.xml +++ b/static/src/app/screens/product_screen/action_pad/action_pad.xml @@ -4,6 +4,9 @@ pos.canPay + + + diff --git a/static/src/app/screens/ticket_screen/ticket_screen.js b/static/src/app/screens/ticket_screen/ticket_screen.js new file mode 100644 index 0000000..72f84f2 --- /dev/null +++ b/static/src/app/screens/ticket_screen/ticket_screen.js @@ -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); + } +}); diff --git a/static/src/app/services/pos_store.js b/static/src/app/services/pos_store.js index c53e29a..1c2964d 100644 --- a/static/src/app/services/pos_store.js +++ b/static/src/app/services/pos_store.js @@ -67,6 +67,39 @@ patch(PosStore.prototype, { 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() { if (!this.config.module_pos_hr) { return this.getCashier(); diff --git a/static/src/app/utils/order_payment_validation.js b/static/src/app/utils/order_payment_validation.js new file mode 100644 index 0000000..afaf506 --- /dev/null +++ b/static/src/app/utils/order_payment_validation.js @@ -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; + } + }, +});