fix(auditoria completa): bloqueos de Mi Dia, cobranza (saldos y fechas), captura (departamento y rol), contactos (privacidad), cotizador (QR) y validaciones de API

This commit is contained in:
2026-09-21 07:18:39 +00:00
parent 75956a709f
commit 5e9b2ac456
16 changed files with 259 additions and 71 deletions

View File

@@ -1,10 +1,31 @@
import type { NextFunction, Request, Response } from 'express'
import { z } from 'zod'
import * as capturaService from '../services/captura-departamento.service.js'
import { DEPARTAMENTOS } from '../config/campos-departamento.js'
import { CAMPOS_POR_DEPARTAMENTO, DEPARTAMENTOS } from '../config/campos-departamento.js'
import { AppError, BadRequestError } from '../utils/errors.js'
import { esAdminOManager } from '../utils/roles.js'
// ── Validación de campos por departamento ────────────────────────────
/**
* Cada campo debe pertenecer al departamento que se está guardando: si el
* cliente manda un campo de otro departamento (p. ej. una captura de Obra
* enviada como Ventas) se rechaza en vez de crear una fila basura.
*/
function validarCamposDelDepartamento(
departamento: string,
registros: { unidad: string; campo: string; valor: string | null }[],
): void {
const permitidos = new Set((CAMPOS_POR_DEPARTAMENTO[departamento] ?? []).map((c) => c.campo))
const invalidos = registros.filter((r) => !permitidos.has(r.campo))
if (invalidos.length > 0) {
const lista = invalidos.slice(0, 3).map((r) => `${r.campo} (unidad ${r.unidad})`).join(', ')
throw new BadRequestError(
`El campo no pertenece al departamento ${departamento}: ${lista}${invalidos.length > 3 ? '…' : ''}`,
)
}
}
// ── Middleware admin ─────────────────────────────────────────────────
function requireAdmin(req: Request, _res: Response, next: NextFunction): void {
@@ -68,6 +89,7 @@ export async function guardarEndpoint(req: Request, res: Response): Promise<void
if (!DEPARTAMENTOS.includes(body.departamento as typeof DEPARTAMENTOS[number])) {
throw new BadRequestError(`Departamento inválido: ${body.departamento}`)
}
validarCamposDelDepartamento(body.departamento, [{ unidad: body.unidadCodigo, campo: body.campo, valor: body.valor ?? null }])
await capturaService.guardarCampo(
body.desarrollo,
@@ -102,6 +124,10 @@ export async function bulkEndpoint(req: Request, res: Response): Promise<void> {
if (!DEPARTAMENTOS.includes(body.departamento as typeof DEPARTAMENTOS[number])) {
throw new BadRequestError(`Departamento inválido: ${body.departamento}`)
}
validarCamposDelDepartamento(
body.departamento,
body.registros.map((r) => ({ unidad: r.unidad, campo: r.campo, valor: r.valor ?? null })),
)
await capturaService.guardarBulk(
body.desarrollo,