Royal Clean CRM · Documentación
Calidad

Estado del sistema

Qué está hecho, qué falta y qué encontró la auditoría de calidad.

Última actualización: 31 de agosto de 2026.


Completado

Fundación

  • Next.js 16 · React 19 · TypeScript estricto · Tailwind 4
  • Prisma 7 con PostgreSQL 17: 18 modelos, migraciones versionadas
  • Invariantes de dominio como restricciones CHECK e índices parciales
  • Validación de entorno con Zod, perezosa (el build no necesita secretos)
  • docker compose con PostgreSQL y MinIO para desarrollo
  • Semilla de desarrollo con datos realistas de Guanacaste

Autenticación y autorización

  • Sesiones opacas en base de datos con revocación inmediata
  • Argon2id, política de contraseñas, contraseñas temporales
  • Rate limiting por usuario y por IP, en base de datos
  • Hash señuelo para no filtrar qué usuarios existen
  • Guardas centrales; un recurso ajeno devuelve 404
  • Cambio forzado de contraseña tras un restablecimiento

Directorio

  • Clientes: alta, edición, archivado en cascada
  • Propiedades: alta, edición, información de acceso protegida, archivado
  • Plantillas de tareas reordenables
  • Empleados: alta con contraseña temporal, tarifa propia, desactivación
  • Búsqueda, filtros y paginación en todos los listados del directorio, y búsqueda global que cruza clientes, propiedades, personal y servicios

Servicios

  • Creación con snapshot de tareas y congelado de precio
  • Máquina de estados centralizada
  • Asignación múltiple con congelado de tarifa y aviso de cruces de agenda
  • Listado con filtros en la URL, calendario mensual y semanal, y tablero Kanban
  • Detalle administrativo con tiempos, evidencia, incidentes, insumos y cifras
  • Corrección de horarios con motivo obligatorio y auditoría
  • Cancelación con motivo y marcado para revisión si había trabajo, por cualquiera de las dos vías (ficha y tablero)

Flujo del personal de campo

  • Pantallas mobile-first: hoy, próximos, historial, perfil
  • Detalle de servicio con cronómetro en vivo, sin escribir en la base
  • Datos de acceso protegidos por estado y tapados hasta pulsar «Mostrar»
  • Tareas con evidencia antes y después, notas y reapertura
  • Iniciar, finalizar participación, finalizar servicio y retomar trabajo
  • Reporte de incidentes

Evidencia

  • Compresión y borrado de EXIF en el navegador
  • Subida directa a R2 con URL firmada; confirmación verificada contra el bucket
  • Visualización mediante ruta autorizada que redirige a URL firmada
  • Retención configurable y limpieza de subidas abandonadas
  • Progreso, reintento y cancelación por fotografía

Insumos

  • Gestión completa desde el detalle de servicio: crear, editar, eliminar
  • Aprobación y rechazo de los registrados por el personal
  • Pantalla del personal que anota qué y cuánto repuso, sin ver importes
  • Los pendientes no cuentan en las cifras hasta aprobarse

Finanzas

  • Aritmética monetaria en céntimos enteros
  • Rentabilidad por servicio, propiedad, cliente y período
  • Cifras parciales explícitas cuando faltan costos
  • Nómina con desglose por servicio y tarifa congelada
  • Pagos con desglose congelado y reversión auditada

Reportes

  • Reporte PDF con marca, evidencia incrustada y paginación real
  • Test que verifica que no filtra tarifas ni márgenes
  • Reportes internos de rentabilidad por propiedad y cliente

Sistema

  • Incidentes: reporte, bandeja, resolución con notificación
  • Notificaciones internas con contador
  • Auditoría legible campo a campo, con filtros
  • Configuración global con advertencias de efecto histórico
  • Apariencia clara, oscura o automática, guardada por dispositivo
  • Línea de tiempo derivada de la auditoría

Infraestructura

  • Dockerfile multi-etapa, usuario sin privilegios, healthcheck (~640 MB)
  • Migraciones aplicadas al arrancar; el contenedor no arranca si fallan
  • Trabajo de mantenimiento idempotente, por CLI o endpoint con token
  • GitHub Actions: typecheck, lint, tests, integración, build y arranque
  • PWA instalable con manifiesto e iconos

Pruebas

  • 134 tests unitarios sobre los módulos de cálculo
  • 74 tests de integración contra PostgreSQL y almacenamiento reales
  • 26 recorridos de extremo a extremo con Playwright, sobre la aplicación compilada, en escritorio y en móvil

Auditoría de calidad (31 de agosto de 2026)

Cinco revisiones independientes —seguridad, corrección de negocio, interfaz, cobertura de especificación y calidad de código— sobre el sistema terminado. Resultado: 38 defectos reales corregidos en cuatro entregas.

Lo que encontró

Seguridad. La evidencia se autorizaba contra un servicio que elegía el cliente: un empleado podía borrar una foto de otro servicio suyo ya cerrado. La portada del panel era la única página sin guarda propia. x-forwarded-for se leía por el extremo que escribe el cliente, lo que permitía evadir el límite de intentos de acceso. El personal podía fijar el precio de cobro de un insumo.

Dinero. Lo pendiente de nómina se contaba por identidad de servicio y no por importe, así que el trabajo añadido con «Retomar trabajo» a un servicio ya pagado era imposible de cobrar. Un servicio compartido perdía del panel al empleado no pagado. Un pago podía registrarse dos veces. Y reasignar a alguien recongelaba su tarifa, reescribiendo el valor de horas ya trabajadas.

Estado. El tablero cancelaba sin motivo, sin marcar para revisión y sin avisar al personal; y reabrir dejaba puesta la fecha de finalización, de la que depende la ventana de acceso a los códigos de la propiedad.

Interfaz. No existía ninguna pantalla de error: un fallo al renderizar mostraba la de Next.js, en inglés. El buscador entraba en un bucle de navegación con un espacio al final. La marca «Requiere revisión» no se podía quitar nunca. El cajón de navegación era el único modal sin trampa de foco.

Cobertura. Faltaba la búsqueda global. Las advertencias de archivado se calculaban y se descartaban. La paginación de la auditoría perdía los filtros.

Lo que confirmó

  • Ninguna consulta del personal de campo arrastra tarifas, salarios, precios ni márgenes: se comprobó campo a campo sobre los nueve campos monetarios.
  • Las 60 Server Actions siguen el mismo patrón, sin una sola excepción, y ninguna puede propagar un error crudo de Prisma.
  • Cero any, cero @ts-ignore, cero TODO sin clasificar en todo el código escrito a mano.
  • Cero colores fuera del sistema de tokens y cero textos en inglés.
  • El PDF del cliente no selecciona siquiera los campos que no debe mostrar.

Verificación sobre la imagen de producción

Comprobado el 1 de septiembre de 2026 contra la imagen real, con PostgreSQL 17 en su propia red de Docker y NODE_ENV=production.

ComprobaciónResultado
Arranca y aplica migraciones6 segundos
/api/health{"status":"ok","database":"ok"}
/admin sin sesión307 al inicio de sesión
Ruta inventada307, no filtra si existe
POST /api/jobs/run sin token401
Con token incorrecto401
Con token correcto200 con el recuento del mantenimiento
Sin JOBS_SECRET configurado503, no queda abierto
Usuario del contenedoruid=1001(nextjs), no root
Cabeceras de seguridadX-Frame-Options, X-Content-Type-Options, Referrer-Policy
X-Powered-ByAusente
Secretos en los registrosNinguno
Primer administrador desde el contenedorCreado, con cambio de contraseña forzado

Pendiente

Subida real de fotografías desde el navegador

Los recorridos comprueban que no se puede completar una tarea sin evidencia, pero no suben una fotografía de verdad desde el navegador. El ciclo completo —firmar, subir, confirmar— sí está cubierto a nivel de integración contra el almacenamiento real.

Impacto: bajo.

Vista de servicio para el personal con el servicio archivado

Menor: un servicio archivado no aparece en las listas del personal, pero su URL directa muestra la pantalla normal.

Correo electrónico

Sin integración. La aplicación funciona íntegramente sin correo, según lo acordado. Ver PENDING_BUSINESS_RULES.md #6.

Vídeo como evidencia

El modelo lo soporta con límites definidos, pero la interfaz sólo ofrece fotografías. Ver PENDING_BUSINESS_RULES.md #8.

Logotipo real

El logotipo es un SVG propio en src/components/brand/logo.tsx. El repositorio no contenía assets de marca. Sustituirlo es editar ese archivo y regenerar los iconos con node scripts/generate-icons.mjs.


Problemas conocidos

La tipografía depende de la plataforma

Decisión deliberada para no depender de Google Fonts al compilar (DECISIONS.md DEC-015). La aplicación se ve ligeramente distinta en Windows, macOS y Android.

La compresión depende del navegador

Navegadores antiguos sin soporte de WebP caen a JPEG. HEIC de iPhone se decodifica con el respaldo de <img>, que funciona en Safari reciente pero puede fallar en versiones viejas.

El panel no se actualiza solo

Por diseño (DECISIONS.md DEC-014). Los datos pueden tener unos segundos de antigüedad.


Decisiones registradas

20 decisiones técnicas documentadas en DECISIONS.md, incluidas tres que sólo aparecieron al construir la imagen de producción:

  • DEC-016 — El cliente Prisma se creaba al importar el módulo, lo que hacía que next build exigiera un DATABASE_URL real.
  • DEC-017 — La CLI de Prisma quedaba sin sus dependencias transitivas por los enlaces simbólicos de pnpm.
  • DEC-015 — El build descargaba la tipografía de Google Fonts, lo que rompe cualquier compilación sin salida a Internet.

Y dos que destaparon tests de integración:

  • DEC-018 — El SDK de S3 usaba direccionamiento virtual-host, incompatible con el endpoint de R2 y con MinIO.
  • El saneado de auditoría recorría campo a campo los Decimal de Prisma y alcanzaba sus métodos, con lo que Prisma rechazaba la escritura y tumbaba la operación auditada entera. Ahora las instancias de clase se convierten a texto, con un test que lo fija.

Preguntas abiertas para Royal Clean

Detalle en PENDING_BUSINESS_RULES.md.

  1. ¿Se redondean las horas de la nómina?
  2. ¿Existen horas extra con tarifa distinta?
  3. ¿Hay recargo por domingo, feriado o nocturnidad?
  4. ¿Es correcto el umbral de 6 horas para la alerta de servicio abierto?
  5. ¿Debe el sistema enviar correos?
  6. ¿Puede el personal registrar insumos, o sólo administración? (implementado con revisión obligatoria y un interruptor para desactivarlo)
  7. ¿Se admitirá vídeo como evidencia?
  8. ¿Qué pasa con la evidencia al expirar: se archiva o se elimina?
  9. ¿Con qué frecuencia y hacia dónde se respalda?

En todos los casos se implementó la opción más conservadora y el sistema quedó preparado para cambiarla sin reescribir nada.

On this page