feat(captura): módulo de captura por departamentos

Backend: tabla CapturaDepartamento, 5 departamentos, 7 desarrollos,
service CRUD + importador Excel + 6 endpoints.
Frontend: tabla editable inline, tabs por dept, resumen avance,
paginación, filtro estatus, permisos por rol, dark mode.
746 API tests + 42 landing tests.
This commit is contained in:
2026-09-07 07:59:14 +00:00
parent 7d19e0da38
commit c2a8f4e929
21 changed files with 2496 additions and 7 deletions

View File

@@ -0,0 +1,19 @@
-- Tabla de captura por departamentos.
-- Patrón manual: sqlite3 + _prisma_migrations. NUNCA migrate dev.
-- CreateTable CapturaDepartamento
CREATE TABLE IF NOT EXISTS "CapturaDepartamento" (
"id" TEXT NOT NULL PRIMARY KEY,
"desarrollo" TEXT NOT NULL,
"unidadCodigo" TEXT NOT NULL,
"departamento" TEXT NOT NULL,
"campo" TEXT NOT NULL,
"valor" TEXT,
"tipo" TEXT NOT NULL DEFAULT 'text',
"usuarioId" TEXT NOT NULL,
"capturadoEn" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"actualizadoEn" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS "CapturaDepartamento_desarrollo_unidadCodigo_departamento_campo_key" ON "CapturaDepartamento"("desarrollo", "unidadCodigo", "departamento", "campo");
CREATE INDEX IF NOT EXISTS "CapturaDepartamento_desarrollo_idx" ON "CapturaDepartamento"("desarrollo");
CREATE INDEX IF NOT EXISTS "CapturaDepartamento_departamento_idx" ON "CapturaDepartamento"("departamento");

View File

@@ -412,6 +412,25 @@ model AuditoriaLog {
@@index([createdAt])
}
// Captura por departamentos: cada campo de cada unidad se registra como fila.
// Unique constraint: desarrollo + unidadCodigo + departamento + campo.
model CapturaDepartamento {
id String @id @default(uuid())
desarrollo String
unidadCodigo String
departamento String
campo String
valor String?
tipo String @default("text")
usuarioId String
capturadoEn DateTime @default(now())
actualizadoEn DateTime @updatedAt
@@unique([desarrollo, unidadCodigo, departamento, campo])
@@index([desarrollo])
@@index([departamento])
}
// Suscripciones push de los navegadores (Web Push API)
model PushSubscription {
id String @id @default(uuid())