diff --git a/frontend/src/pages/Reportes.tsx b/frontend/src/pages/Reportes.tsx index bb92877..b98a9bf 100644 --- a/frontend/src/pages/Reportes.tsx +++ b/frontend/src/pages/Reportes.tsx @@ -36,6 +36,7 @@ import { Card, Button, Input, PageHeader, Skeleton, EmptyState, toast } from '.. import { odooApi } from '../services/odoo'; import type { CommissionRow } from '../services/odoo'; import { exportToExcel } from '../lib/exporter'; +import { useAuth } from '../lib/auth'; import { REPORT_PRESETS, RangeOverrideHint, @@ -144,6 +145,8 @@ const ReportesContent: FC = () => { const familiaKey = familiaDe(tab); const familia = FAMILIAS.find((f) => f.key === familiaKey) || FAMILIAS[0]; const { favs, toggle, isFav } = useReportFavorites(); + const { hasRole } = useAuth(); + const esAdmin = hasRole('admin'); // Rango global del módulo (persistente, compartido por todas las pestañas) const { start, end, preset, setRange } = useGlobalReportRange(); const [today, setToday] = useState(() => new Date().toISOString().split('T')[0]); @@ -164,11 +167,10 @@ const ReportesContent: FC = () => { try { setLoading(true); setError(null); - const [salesRes, aptRes, cashRes, commRes] = await Promise.all([ + const [salesRes, aptRes, cashRes] = await Promise.all([ odooApi.getSalesReport(start, end), odooApi.getAppointmentsReport(start, end), odooApi.getCashReport(today), - odooApi.getCommissions(start, end), ]); if (salesRes.status === 'success') setSalesReport({ @@ -181,9 +183,21 @@ const ReportesContent: FC = () => { setAppointmentsReport({ total: aptRes.total, by_state: aptRes.by_state }); if (cashRes.status === 'success') setCashReport({ total: cashRes.total, by_method: cashRes.by_method }); - if (commRes.status === 'success') { - setCommissions(commRes.commissions || []); - setCommTotals({ base: commRes.total_base || 0, commission: commRes.total_commission || 0 }); + // Comisiones viajan en petición aparte y solo para admin: el endpoint + // exige rol admin y un 403 no debe tumbar (ni ensuciar la consola del) + // resto del tab General para los demás roles. + if (esAdmin) { + odooApi.getCommissions(start, end) + .then((commRes) => { + if (commRes.status === 'success') { + setCommissions(commRes.commissions || []); + setCommTotals({ base: commRes.total_base || 0, commission: commRes.total_commission || 0 }); + } + }) + .catch(() => { /* sin permiso: sección de comisiones queda vacía */ }); + } else { + setCommissions([]); + setCommTotals({ base: 0, commission: 0 }); } } catch (err) { setError('Error al cargar reportes'); @@ -194,7 +208,7 @@ const ReportesContent: FC = () => { } }; load(); - }, [start, end, today, tab]); + }, [start, end, today, tab, esAdmin]); const aptData = appointmentsReport ? Object.entries(appointmentsReport.by_state).map(([name, value]) => ({ name, value })) @@ -523,6 +537,7 @@ const ReportesContent: FC = () => { + {esAdmin && (
@@ -572,6 +587,7 @@ const ReportesContent: FC = () => { )} + )} )} diff --git a/odoo-addons/skeen_whatsapp/controllers/auth.py b/odoo-addons/skeen_whatsapp/controllers/auth.py index 34b0907..d67f5d6 100644 --- a/odoo-addons/skeen_whatsapp/controllers/auth.py +++ b/odoo-addons/skeen_whatsapp/controllers/auth.py @@ -152,7 +152,11 @@ def require_role(min_role='lectura'): return _json({'status': 'error', 'message': 'No autenticado'}, 401) if user.role_rank() < required: return _json({'status': 'error', 'message': 'Sin permiso'}, 403) - if user.role == 'medico' and request.httprequest.path.startswith(MEDICO_BLOCKED_PREFIXES): + if user.role == 'medico' and any( + request.httprequest.path == p or request.httprequest.path.startswith( + p if p.endswith('/') else p + '/') + for p in MEDICO_BLOCKED_PREFIXES + ): return _json({'status': 'error', 'message': 'Sin permiso'}, 403) request.frontend_user = user return func(self, *args, **kw) diff --git a/odoo-addons/skeen_whatsapp/controllers/frontend.py b/odoo-addons/skeen_whatsapp/controllers/frontend.py index b4b3755..ba3c99c 100644 --- a/odoo-addons/skeen_whatsapp/controllers/frontend.py +++ b/odoo-addons/skeen_whatsapp/controllers/frontend.py @@ -2797,6 +2797,7 @@ class SkeenFrontendController(http.Controller): 'remaining_count': max(0, count - pagados) if count else 0, } + @http.route('/skeen/frontend/v1/expedientes-recientes', type='http', auth='none', methods=['GET', 'OPTIONS'], csrf=False) @require_role('lectura') def expedientes_recientes(self, **kw): """Pacientes con visitas recientes (últimos 30 días por default)"""