mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 15:33:17 +00:00
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Resumen Implementa las observaciones de facturas, nómina e IMSS sobre el catálogo de proveedores y la nómina semanal que ya existían. ## Qué queda disponible - Proveedores: régimen fiscal, código postal, estatus activo/inactivo y filtro por nombre o RFC. - Facturas recibidas y emitidas: Excel, UUID, revisión si el RFC no está en el catálogo, clasificación a obra/concepto y gasto `source = invoice` solo para la recibida clasificada. - Nómina: bono, descuento con observación, horas y día festivo dentro del armado semanal. La semana pagada no acepta cambios. - Préstamos: edición (libre sin pagos; con pagos solo nota y cuota) y estado de cuenta con saldo = entregado + comisión − pagos. - Finiquitos y liquidaciones: conceptos, neto descontando descuentos vigentes y saldo de préstamo, y gasto al pagar. - Dispersión: corte semanal, corrección antes de definitiva y comprobante. - Historial de alta, baja y reingreso en la ficha, más movimientos IMSS. - Reporte por periodo en Excel y PDF. - Jornal IMSS distinto del jornal real y consulta de cuotas exportable a Excel. ## Verificación `deno check` de la API pasó. En este entorno no hay Postgres, así que `api/pendientes_test.ts` queda listo para correr cuando la base tenga las migraciones 042–050 y el permiso IAM 007. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-5e23dc3b-921e-4091-9650-7091d3a52bbf?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-5e23dc3b-921e-4091-9650-7091d3a52bbf&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
68 lines
2 KiB
TypeScript
68 lines
2 KiB
TypeScript
const ROUTE_PERMISSIONS: Record<string, string> = {
|
|
"/": "reports.view",
|
|
"/proyectos": "projects.view",
|
|
"/obras": "projects.view",
|
|
"/presupuesto": "budget.view",
|
|
"/programa-obra": "work_program.view",
|
|
"/control-presupuesto": "cost_control.view",
|
|
"/facturas": "invoices.view",
|
|
"/gastos": "expenses.view",
|
|
"/almacen": "warehouse.view",
|
|
"/kanban": "kanban.view",
|
|
"/padron": "workers.view",
|
|
"/gafetes": "documents.view",
|
|
"/nomina": "payroll.view",
|
|
"/configuracion": "settings.view",
|
|
"/usuarios": "users.view",
|
|
};
|
|
|
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
if (to.path === "/login") return;
|
|
const { user, fetchMe } = useAuth();
|
|
const { api } = useApi();
|
|
try {
|
|
if (!user.value) {
|
|
await Promise.race([
|
|
fetchMe(),
|
|
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 4000)),
|
|
]);
|
|
}
|
|
} catch {
|
|
user.value = null;
|
|
}
|
|
if (!user.value) return navigateTo("/login");
|
|
|
|
if (user.value.role_code === "platform_admin" && user.value.realm === "platform") {
|
|
user.value = null;
|
|
try {
|
|
await api("/v1/auth/logout", { method: "POST" });
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
if (import.meta.client) {
|
|
window.location.href = "http://localhost:3001/login";
|
|
return abortNavigation();
|
|
}
|
|
return navigateTo("/login");
|
|
}
|
|
|
|
if (user.value.must_change_password && to.path !== "/cambiar-password") {
|
|
return navigateTo("/cambiar-password");
|
|
}
|
|
if (!user.value.must_change_password && to.path === "/cambiar-password") {
|
|
return navigateTo("/");
|
|
}
|
|
|
|
const perms = usePermissions();
|
|
const userScope = useScope();
|
|
if (!perms.loaded.value) await perms.load();
|
|
if (!userScope.loaded.value) await userScope.load();
|
|
|
|
const required = Object.entries(ROUTE_PERMISSIONS).find(([path]) =>
|
|
path === "/" ? to.path === "/" : to.path === path || to.path.startsWith(`${path}/`),
|
|
)?.[1];
|
|
|
|
if (required && !perms.can(required)) {
|
|
return navigateTo("/");
|
|
}
|
|
});
|