feat: grant outlet managers bypass authority for restricted POS actions and prevent order deletion when items are locked
This commit is contained in:
parent
08c4e3711b
commit
c37db8da68
@ -7,7 +7,7 @@ This module implements a robust security lock in the Odoo 19 Point of Sale syste
|
||||
Features
|
||||
========
|
||||
|
||||
* **Role-Based Security**: Only users with the "Area Manager" role (integrated with `pos_employee_role`) can delete or reduce the quantity of items already sent to the kitchen.
|
||||
* **Role-Based Security**: Only users with the "Area Manager" or "Store Manager" (technically "outlet_manager") roles can delete or reduce the quantity of items already sent to the kitchen.
|
||||
* **Persistent Locking**: Uses a database-backed field `x_locked_qty` on `pos.order.line` to ensure the lock state persists across browser refreshes, device synchronizations, and session restarts.
|
||||
* **Graceful Reduction**: Allows cashiers to freely add and remove "new" quantities on an existing line. For example, if 1 item is sent and 1 is newly added (Total 2), the cashier can safely backspace to reduce the quantity to 1, but cannot drop it below the sent amount.
|
||||
* **User-Friendly Alerts**: Displays a clear "Action Restricted" popup when an unauthorized deletion attempt is blocked.
|
||||
@ -32,5 +32,5 @@ Installation
|
||||
============
|
||||
|
||||
1. Install the module as usual.
|
||||
2. Ensure the "Area Manager" role is configured for supervisors who require bypass authority.
|
||||
2. Ensure the "Area Manager" or "Store Manager" roles are configured for supervisors who require bypass authority.
|
||||
3. Refresh the POS interface to load the new security logic.
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
'summary': 'Lock product deletion/reduction after order is sent to kitchen',
|
||||
'description': """
|
||||
This module prevents cashiers from deleting or reducing the quantity of products that have already been sent to the kitchen.
|
||||
Only users with the 'Area Manager' role can perform these actions.
|
||||
Only users with the 'Area Manager' or 'Store Manager' role can perform these actions.
|
||||
""",
|
||||
'author': "Suherdy Yacob",
|
||||
'depends': ['point_of_sale', 'pos_hr', 'pos_employee_role'],
|
||||
|
||||
@ -13,19 +13,19 @@ patch(PosOrder.prototype, {
|
||||
|
||||
removeOrderline(line) {
|
||||
const cashier = this.models["pos.session"].getFirst()?.cashier;
|
||||
const isAreaManager = cashier?.pos_role === 'area_manager';
|
||||
const isManager = cashier && ['area_manager', 'outlet_manager'].includes(cashier.pos_role);
|
||||
|
||||
// Use the same helper as the line patch
|
||||
const lockedQty = line.get_locked_qty ? line.get_locked_qty() : 0;
|
||||
|
||||
if (lockedQty > 0 && !isAreaManager) {
|
||||
if (lockedQty > 0 && !isManager) {
|
||||
if (line.qty > lockedQty) {
|
||||
line.setQuantity(lockedQty);
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
title: _t("Action Restricted"),
|
||||
body: _t("You cannot delete a product that has already been sent to the kitchen. Please call an Area Manager."),
|
||||
body: _t("You cannot delete a product that has already been sent to the kitchen. Please call an Area Manager or Store Manager."),
|
||||
};
|
||||
}
|
||||
return super.removeOrderline(...arguments);
|
||||
|
||||
@ -23,12 +23,12 @@ patch(PosOrderline.prototype, {
|
||||
|
||||
setQuantity(quantity, keep_price) {
|
||||
const cashier = this.models["pos.session"].getFirst()?.cashier;
|
||||
const isAreaManager = cashier?.pos_role === 'area_manager';
|
||||
const isManager = cashier && ['area_manager', 'outlet_manager'].includes(cashier.pos_role);
|
||||
|
||||
const lockedQty = this.get_locked_qty();
|
||||
const newQty = (typeof quantity === "number" || (typeof quantity === "string" && quantity !== "")) ? parseFloat(quantity) : 0;
|
||||
|
||||
if (lockedQty > 0 && !isAreaManager) {
|
||||
if (lockedQty > 0 && !isManager) {
|
||||
// If trying to delete or reduce below locked qty
|
||||
if (quantity === "" || isNaN(parseFloat(quantity)) || newQty < lockedQty) {
|
||||
// If there's unsent quantity, gracefully reduce it to the locked quantity
|
||||
@ -39,7 +39,7 @@ patch(PosOrderline.prototype, {
|
||||
// Otherwise, they are trying to delete the sent quantity, so block it
|
||||
return {
|
||||
title: _t("Action Restricted"),
|
||||
body: _t("You cannot reduce the quantity below what was already sent to the kitchen. Please call an Area Manager."),
|
||||
body: _t("You cannot reduce the quantity below what was already sent to the kitchen. Please call an Area Manager or Store Manager."),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
27
static/src/app/services/pos_store.js
Normal file
27
static/src/app/services/pos_store.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { PosStore } from "@point_of_sale/app/services/pos_store";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
|
||||
patch(PosStore.prototype, {
|
||||
async beforeDeleteOrder(order, options = {}) {
|
||||
const cashier = this.getCashier();
|
||||
const isManager = cashier && ['area_manager', 'outlet_manager'].includes(cashier.pos_role);
|
||||
|
||||
// Check if the order has any locked items (sent to kitchen)
|
||||
if (!isManager && order && order.lines) {
|
||||
for (const line of order.lines) {
|
||||
const lockedQty = line.get_locked_qty ? line.get_locked_qty() : 0;
|
||||
if (lockedQty > 0) {
|
||||
this.dialog.add(AlertDialog, {
|
||||
title: _t("Action Restricted"),
|
||||
body: _t("You cannot delete this order because some items have already been sent to the kitchen. Please call an Area Manager or Store Manager."),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.beforeDeleteOrder(order, options);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user