diff --git a/pos/blueprints/config_bp.py b/pos/blueprints/config_bp.py
index 51c087b..8d276ce 100644
--- a/pos/blueprints/config_bp.py
+++ b/pos/blueprints/config_bp.py
@@ -1066,6 +1066,7 @@ def get_modules():
'marketplace': _bool('module_marketplace'),
'meli': _bool('module_meli'),
'catalog': _bool('module_catalog'),
+ 'workshop': _bool('module_workshop'),
})
@@ -1082,6 +1083,7 @@ def update_modules():
'module_marketplace': 'true' if data.get('marketplace') else 'false',
'module_meli': 'true' if data.get('meli') else 'false',
'module_catalog': 'true' if data.get('catalog') else 'false',
+ 'module_workshop': 'true' if data.get('workshop') else 'false',
}
for key, value in settings.items():
@@ -1098,6 +1100,8 @@ def update_modules():
'whatsapp': data.get('whatsapp'),
'marketplace': data.get('marketplace'),
'meli': data.get('meli'),
+ 'catalog': data.get('catalog'),
+ 'workshop': data.get('workshop'),
}})
diff --git a/pos/static/js/app-init.js b/pos/static/js/app-init.js
index 6971348..7d71349 100644
--- a/pos/static/js/app-init.js
+++ b/pos/static/js/app-init.js
@@ -183,13 +183,20 @@
};
// ─── Page guard based on role + permissions ───
+ function moduleEnabled(key) {
+ try {
+ var modules = JSON.parse(localStorage.getItem('pos_modules') || '{}');
+ return modules[key] !== false;
+ } catch(e) { return true; }
+ }
+
function isPageAllowed(pagePath, userRole, userPerms) {
if (userRole === 'owner' || userRole === 'admin') return true;
// Restricted roles (workshop/mechanic/counter/cashier) see modules based on permissions.
if (['workshop', 'mechanic', 'counter', 'cashier'].indexOf(userRole) !== -1) {
var allowed = [];
- if (userRole === 'workshop' || userRole === 'mechanic') {
+ if ((userRole === 'workshop' || userRole === 'mechanic') && moduleEnabled('workshop')) {
allowed = ['/pos/workshop'];
}
var permMap = {
@@ -208,6 +215,8 @@
};
for (var p in permMap) {
if (userPerms.indexOf(p) !== -1 && allowed.indexOf(permMap[p]) === -1) {
+ // Hide workshop if its module is disabled
+ if (permMap[p] === '/pos/workshop' && !moduleEnabled('workshop')) continue;
allowed.push(permMap[p]);
}
}
@@ -229,7 +238,9 @@
fallback = '/pos/dashboard';
} else if (['workshop', 'mechanic', 'counter', 'cashier'].indexOf(userRole) !== -1) {
var allowed = [];
- if (userRole === 'workshop' || userRole === 'mechanic') allowed = ['/pos/workshop'];
+ if ((userRole === 'workshop' || userRole === 'mechanic') && moduleEnabled('workshop')) {
+ allowed = ['/pos/workshop'];
+ }
var permMap = {
'pos.sell': '/pos/sale',
'pos.view': '/pos/sale',
@@ -246,6 +257,7 @@
};
for (var p in permMap) {
if (userPerms.indexOf(p) !== -1 && allowed.indexOf(permMap[p]) === -1) {
+ if (permMap[p] === '/pos/workshop' && !moduleEnabled('workshop')) continue;
allowed.push(permMap[p]);
}
}
diff --git a/pos/static/js/config.js b/pos/static/js/config.js
index ddbc70f..da035bd 100644
--- a/pos/static/js/config.js
+++ b/pos/static/js/config.js
@@ -956,10 +956,12 @@ const Config = (() => {
var cbMp = document.getElementById('cfg-module-marketplace');
var cbMeli = document.getElementById('cfg-module-meli');
var cbCat = document.getElementById('cfg-module-catalog');
+ var cbWork = document.getElementById('cfg-module-workshop');
if (cbWa) cbWa.checked = data.whatsapp !== false;
if (cbMp) cbMp.checked = data.marketplace !== false;
if (cbMeli) cbMeli.checked = data.meli !== false;
if (cbCat) cbCat.checked = data.catalog !== false;
+ if (cbWork) cbWork.checked = data.workshop !== false;
localStorage.setItem('pos_modules', JSON.stringify(data));
} catch (e) {
console.error('Config.loadModules:', e);
@@ -980,14 +982,16 @@ const Config = (() => {
var cbMp = document.getElementById('cfg-module-marketplace');
var cbMeli = document.getElementById('cfg-module-meli');
var cbCat = document.getElementById('cfg-module-catalog');
+ var cbWork = document.getElementById('cfg-module-workshop');
var cbCr = document.getElementById('cfg-module-counter-remission');
- if (!cbWa && !cbMp && !cbMeli && !cbCat && !cbCr) return;
+ if (!cbWa && !cbMp && !cbMeli && !cbCat && !cbWork && !cbCr) return;
try {
var data = {
whatsapp: cbWa ? cbWa.checked : true,
marketplace: cbMp ? cbMp.checked : true,
meli: cbMeli ? cbMeli.checked : true,
catalog: cbCat ? cbCat.checked : true,
+ workshop: cbWork ? cbWork.checked : true,
};
var res = await fetch(API + '/modules', {
method: 'PUT',
diff --git a/pos/static/js/pos.js b/pos/static/js/pos.js
index 6b496af..7890ff2 100644
--- a/pos/static/js/pos.js
+++ b/pos/static/js/pos.js
@@ -34,6 +34,7 @@ const POS = (() => {
let counterRemissionEnabled = false;
let allowZeroPriceSales = true;
let allowNegativeStock = false;
+ let workshopModuleEnabled = true;
let currentPerms = [];
let receiptConfig = {};
let couriers = [];
@@ -93,7 +94,7 @@ const POS = (() => {
}
if (!canDiscount) hide('[onclick="POS.applyDiscount()"]');
if (!canEditPrice) hide('[onclick="POS.modifyPrice()"]');
- if (!canCreateWorkshopOrder) {
+ if (!canCreateWorkshopOrder || !workshopModuleEnabled) {
hide('[onclick="POS.createServiceOrder()"]');
hide('[title="Orden de servicio"]');
}
@@ -153,6 +154,14 @@ const POS = (() => {
allowZeroPriceSales = true;
allowNegativeStock = false;
}
+ // Module toggles (from app-init preloaded modules)
+ try {
+ var modules = window.POS_USER && window.POS_USER.modules;
+ if (!modules) modules = JSON.parse(localStorage.getItem('pos_modules') || '{}');
+ workshopModuleEnabled = modules.workshop !== false;
+ } catch (e) {
+ workshopModuleEnabled = true;
+ }
// Counter remission workflow applies only to the counter role.
canCreateRemission = counterRemissionEnabled && employeeRole === 'counter' && perms.includes('pos.remission');
@@ -1390,6 +1399,7 @@ const POS = (() => {
// ─── Service Order from POS ──────────
function createServiceOrder() {
+ if (!workshopModuleEnabled) { showToast('El módulo de taller no está habilitado'); return; }
if (!canCreateWorkshopOrder) { showToast('No tienes permiso para ordenes de taller'); return; }
if (cart.length === 0) { showToast('Carrito vacio'); return; }
const zeroItem = findZeroPriceItem();
diff --git a/pos/static/js/sidebar.js b/pos/static/js/sidebar.js
index 393eff0..4527484 100644
--- a/pos/static/js/sidebar.js
+++ b/pos/static/js/sidebar.js
@@ -80,7 +80,7 @@ window.renderSidebar = function(modulesOverride) {
{ label: _t('nav_management'), items: [
{ id: 'customers', name: _t('customers'), href: '/pos/customers', icon: '