diff --git a/deploy/nginx-host.conf b/deploy/nginx-host.conf index be2aecb..40e22ed 100644 --- a/deploy/nginx-host.conf +++ b/deploy/nginx-host.conf @@ -4,8 +4,8 @@ # index.html siempre se revalida (los assets de Vite llevan hash en el nombre, # así que solo el HTML necesita no-cache para que los deploys se vean al momento) map $uri $frasa_cache_control { - ~*\.html$ "no-cache"; - default ""; + ~*\.html$ "no-cache, no-store, must-revalidate"; + default "public, max-age=31536000, immutable"; } server { @@ -56,6 +56,17 @@ server { try_files $uri $uri/ /hoy/index.html; } + # Documento entregable publicado como página web privada. + # NO se enlaza desde ninguna app: sólo se llega con el enlace exacto, y lleva + # noindex para que no lo indexe ningún buscador. Se regenera con + # scripts/generar-web-documento.py + location /doc/alcance-2f9k7d/ { + alias /root/frasa-ecosystem/docs/web/alcance-2f9k7d/; + index index.html; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; + add_header X-Robots-Tag "noindex, nofollow, noarchive" always; + } + # Landing (raíz) location / { root /root/frasa-ecosystem/apps/landing/dist; diff --git a/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf b/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf index 418b80f..dcb6e19 100644 Binary files a/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf and b/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf differ diff --git a/docs/web/alcance-2f9k7d/ALCANCE-VS-ENTREGADO.pdf b/docs/web/alcance-2f9k7d/ALCANCE-VS-ENTREGADO.pdf new file mode 100644 index 0000000..9e50b15 Binary files /dev/null and b/docs/web/alcance-2f9k7d/ALCANCE-VS-ENTREGADO.pdf differ diff --git a/docs/web/alcance-2f9k7d/index.html b/docs/web/alcance-2f9k7d/index.html new file mode 100644 index 0000000..3a34e70 --- /dev/null +++ b/docs/web/alcance-2f9k7d/index.html @@ -0,0 +1,1342 @@ + + + + + + + + CRM360 FRASA — Alcance contratado vs. desarrollo entregado + + + + +
+ CRM360 FRASA +
+ CRM360 FRASA + Alcance contratado vs. desarrollo entregado +
+ Descargar PDF +
+ +
+
+

Documento confidencial · Preparado para el cliente

+

CRM360 FRASA

+

Alcance contratado vs. desarrollo entregado

+
+
Cliente
Frasa Desarrollos S.A.P.I. de C.V.
+
Proveedor
Consult (AS) Technology
+
Proyecto
Ecosistema CRM360 FRASA
+
Periodo de ejecución
20 de agosto al 21 de septiembre de 2026
+
Referencia contractual
“Guía Técnica de Implementación — Ecosistema CRM + Apps Web + Automatizaciones”, versión 1.0, 19 de agosto de 2026
+
Fecha de emisión
21 de septiembre de 2026
+
Versión
1.0
+
+

1. Objeto del documento

+

Este documento pone por escrito dos cosas:

+
    +
  1. Lo que se contrató originalmente, tal como quedó en la Guía Técnica de Implementación del 19 de agosto de 2026.
  2. +
  3. Lo que efectivamente se diseñó, construyó, puso en operación y documentó entre el 20 de agosto y el 21 de septiembre de 2026.
  4. +
+

La razón es sencilla: el sistema entregado hace bastante más de lo contratado. Durante el proyecto se agregaron módulos que no existían en el alcance, se cambió la arquitectura de datos y de despliegue, se reemplazó la dependencia de Monday.com por una base de datos propia y se rediseñó la interfaz de las siete aplicaciones. Todo eso se atendió por solicitud expresa del cliente, con trabajo entregado y en uso.

+

El objetivo es contar con una base clara y verificable para facturar el desarrollo adicional. Cada partida listada en las secciones 6 y 7 se puede comprobar en el repositorio del proyecto (FRASA-CAS, 155 commits) y en el sistema en operación.

+
+

2. Resumen ejecutivo

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConceptoContratadoEntregado
Aplicaciones web4 (Cotizador, Alta de Clientes, Cobranza, Agente IA)7 (se agregaron Portal, Mi Día y Tableros)
Módulos funcionales412 (11 módulos + Configuraciones)
Módulos nuevos no previstos—8
Puntos de acceso (endpoints) del API~30 estimados137
Tablas en base de datos7 tableros replicados en Monday33 tablas propias
Pruebas automatizadasno contempladas1,203 pruebas / 131 archivos
Líneas de código (fuente + pruebas)—69,659
Integración con Monday.comeje central del contratoeliminada por solicitud del cliente
Roles de usuariono especificados7 roles con permisos diferenciados
Documentaciónmanual de usuarioguía de usuario en PDF, guía de flujo completo, guion de demo, manual de automatizaciones
+
+

3. Alcance contratado originalmente

+

3.1 Componentes previstos en el contrato

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Componente contratadoAlcance previsto
1App 1 — Cotizador financieroReemplazar el Excel “Formato Cotizador_Diagonal.xlsx”. Selección de unidad, cálculo de 4 propuestas comerciales, generación de PDF, guardado en Monday.
2App 2 — Alta de ClientesDigitalizar 4 formatos (Alta de Clientes, Identificación, Aviso de Privacidad, Anti-lavado). Formulario por secciones, carga de documentos, firma digital, generación de PDFs.
3App 3 — Módulo de CobranzaSeguimiento de pagos diferidos del enganche: planes de pago, registro de pagos, estados de cuenta, recordatorios automáticos.
4App 4 — Agente de IAChatbot interno con base de conocimiento (RAG) sobre documentos de Frasa, usando OpenAI + Supabase (pgvector).
5API GatewayPuente hacia Monday.com: autenticación JWT, proxy GraphQL, webhooks, servicios de PDF, WhatsApp y correo, persistencia en PostgreSQL.
6Integración con Monday.comTableros de Contactos, Desarrollos, Unidades, Leads, Oportunidades, Apartado y Ventas; automatizaciones nativas; round-robin de asignación; recordatorios de inactividad a 10 días.
7Infraestructura y despliegueDocker + Docker Compose, NGINX como proxy inverso, PostgreSQL 16, Redis 7, SSL con Let’s Encrypt (Certbot), PM2, dominio frasadesarrollos.ddns.net, rutas /quote, /client, /pay, /ai, /api.
8WA CRM / WhatsAppMarcado explícitamente en el contrato como “FASE FUTURA — instalar después”.
+

3.2 Checklist de entrega previsto (10 días, 8 fases)

+

El contrato definía la entrega en 8 fases dentro de 10 días hábiles: tableros y permisos; automatizaciones y webhooks; WhatsApp (fase futura); Cotizador; Alta de Clientes; Cobranza; Reportes; y Go-Live con capacitación y manual de usuario.

+
+

4. Lo que está entregado y en operación hoy

+

4.1 Aplicaciones publicadas

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AplicaciónRutaEstado
Portal CRM360 FRASA (lanzador de módulos, accesos según rol)/En operación
Cotizador financiero/quote/En operación
Alta de Clientes y Alta de Afluencia/client/En operación
Clientes / Cobranza/pay/En operación
Mi Día (CRM del vendedor)/hoy/En operación
Tableros y Reportes/dash/En operación
Chat FRASA (asistente IA)/ai/En operación
Micrositio público de desarrollos/d/{clave}En operación
Api Gateway/api/En operación
+

Acceso remoto publicado en https://frasa.consultoria-as.com mediante Cloudflare Zero Trust. El sistema está instalado y funcionando en la máquina del cliente, con servicio systemd, NGINX y respaldos automáticos.

+

4.2 Módulos visibles por el usuario

+

Mi Día · Alta Cliente · Alta Afluencia · Cotizador · Mi Tablero · Tablero Comercial · Cobranza · Contactos · Clientes Maestro · Captura · Soporte · Desarrollos · Chat FRASA · Configuraciones · Reportes · Auditoría.

+

4.3 Roles implementados

+

Vendedor · Administración de Venta · Director de Ventas · Escrituración · Dirección General · Manager · Administrador. Cada rol ve únicamente los módulos que le corresponden, y las restricciones se aplican en la aplicación y en el API (un módulo no autorizado responde 403 aunque se llame directamente al servidor).

+

4.4 Datos reales ya operando en el sistema

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DatoVolumen
Inventario de unidades288 unidades
Clientes maestro (base histórica del cliente)1,535 registros
Registros de captura por departamento5,628 registros
Leads de captura rápida332
Oportunidades187
Planes de cobranza con seguimiento103 planes
Pagos y abonos registrados617 pagos / 382 aplicaciones de abono
Cotizaciones emitidas18
Usuarios con acceso al sistema27
Registros de auditoría1,375
+
+

5. Comparativa: contratado vs. entregado

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tema del contratoLo contratadoLo que se entregó
Aplicaciones4 apps7 apps + micrositio público
Rol de Monday.comNúcleo del sistema: todos los datos vivían en MondayMonday se usó en la primera etapa y después se eliminó por completo; los datos viven en base de datos propia
Base de datosPostgreSQL 16 + Redis 7 en DockerBase de datos propia en el servidor del cliente, con 33 tablas y 16 migraciones versionadas
DespliegueDocker Compose + NGINX + SSL Certbot + PM2Instalación nativa con systemd, NGINX y Cloudflare Zero Trust (túnel cifrado, sin exponer puertos)
Dominiofrasadesarrollos.ddns.netfrasa.consultoria-as.com
Cotizador1 desarrollo (Diagonal)9 desarrollos (Atelier, Diagonal, Estanza, Mutuo, Senda, Solana, Sole y los que se den de alta), comparador de unidades, vigencia, historial, folio con QR, reglas de cálculo configurables por desarrollo
Alta de Clientes1 formulario2 flujos (Afluencia y Cliente) con etapas que guardan y avanzan, documentos obligatorios, firma digital, generación de PDFs
CobranzaMódulo por construirMódulo en operación con la cartera real del cliente importada (103 planes, 617 pagos), recibos en PDF, mora configurable y recordatorios
Agente de IAOpenAI + Supabase (pgvector)Chat FRASA con proveedor propio (NAN) y 13 herramientas conectadas a la base de datos, con alcance según el rol del usuario
Reportes“Dashboards en Monday”Módulo de Reportes con 10 reportes, filtros por fecha y descarga a Excel
Capacitación y manualManual de usuarioGuía de usuario en PDF (para personal sin conocimientos técnicos), guía de flujo completo por área, guion de demostración y manual de automatizaciones
+
+

6. Desarrollo adicional solicitado por el cliente (fuera del alcance original)

+

Las siguientes partidas no estaban en el contrato. Cada una se solicitó durante el proyecto y está entregada.

+

6.1 Módulos nuevos completos

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Módulo solicitadoQué incluyeEvidencia
1Mi Día (CRM del vendedor)Bandeja personal con pestañas: Mis Leads, Mis Oportunidades, Mis Apartados, Escrituración y Mi Tablero. Botones de avance de fase, vista Kanban, exportación, búsqueda, filtros, agenda y registro de último cambio con fecha y responsableapps/mi-dia, commits bc0f9da, 1963c06, febd636, 075d47c
2Captura por departamentosMódulo para Obra, Ventas, Escrituración, Finanzas y Postventa, a partir del archivo maestro de estatus de venta. Semáforo de fechas de obra (verde/rojo), bloqueo de fechas una vez establecidas (sólo el administrador las cambia), 4 campos de escrituración con cálculo de desviación en días, permisos por departamentoapps/landing/src/pages/CapturaPage.tsx, commits c2a8f4e, 1791dd6, d2e97f3, b2d3287
3Reportes10 reportes: leads por vendedor y total, cancelaciones por cliente/motivo, afluencias por desarrollo, listado de cancelaciones por etapa, captura por departamento, clientes en cobranza, disponibilidad por desarrollo y modelo, contactos, clientes y escrituración en entrega. Todos con filtro de fechas y descargaapps/dashboards, commits 8bcb093, 9630893
4ContactosDirectorio con ficha completa del cliente en ventana modal (estatus, propiedad comprada, historial de quién lo atiende) y privacidad por vendedor: cada quien ve sólo los contactos que tiene asignadoscommits a4575bb, 64b0124
5Clientes MaestroMódulo con la base histórica de 1,535 clientes: búsqueda, filtros, importación y exportación, y ventana de detallecommits 9021733, dc43cbe
6SoporteFormulario “Registrar Solicitud Interna” con solicitante, correo, título, descripción, departamento responsable, prioridad, responsable, fechas, archivos adjuntos y notas; bandeja con filtros y estadoscommits c8357fe, 2fc80e7
7Configuraciones (sólo administrador)Edición de permisos por rol, alta de usuarios, modo de operación, mantenimiento de la base de conocimiento del chat y bitácora de auditoríaapps/landing/src/components/config/, commits b2634f9, b997076
8Desarrollos + micrositio públicoAlta de desarrollos nuevos (ficha, modelos, inventario manual y por generador, reglas de cotizador por desarrollo) y sitio público por desarrollo en /d/{clave} con diseño comercial, catálogo de modelos y disponibilidadcommits d654899, 34229f1
+

6.2 Etapas y flujos de proceso agregados

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
9Separar el alta en dos flujosSe dividió el alta única en Alta Afluencia (primer contacto, perfilamiento del prospecto) y Alta de Cliente (expediente completo). Se agregó conversión directa de afluencia a cliente y, después, captura con etapas que guardan avance en cada pasocommits 2b87bf6, cd93aec, ccd4ca7
10Etapa de EscrituraciónDepartamento nuevo con 9 fases (Venta, Seguimiento, Integración, X Autorizar, Autorizado, X Escriturar, Escriturado, Titulado, Canceló), traspaso automático desde Apartados al firmar el cliente, cancelación con motivo y retiro de la listacommits a9c6b1e, d14749f, 356bf46, 1a70199
11Oportunidades y Apartados dentro de Mi DíaDos bandejas nuevas con botones de avance de fase vinculados a acciones reales: el botón Cotizar abre el cotizador con los datos del cliente precargados y guarda la cotización ligada a esa persona; el botón Afluencia abre el formulario con la información ya capturadacommits 1963c06, 5d7c6f5, cbd50e2
12Calendario y agenda del vendedorAgenda con citas por fecha y hora, seguimiento de cada cita y confirmación desde la bandeja de oportunidadescommits d546460, fabf507
13Bitácora del clienteVentana de detalle con notas e historial por etapa, que se conserva al avanzar de fase; muestra el vendedor asignado y el último cambio con fecha y responsablecommits 913bdff, b1e408f, 31f83b0
14Vista KanbanVista de tablero con arrastre visual en Leads, Oportunidades, Apartados y Escrituración, con botón y exportación integrados a la barra de pestañas y diseño adaptable a la resolución de cada equipocommits febd636, 8212fd9, 350455d
15Descartes con motivo y subcategoríaAl descartar un lead, oportunidad, apartado o escrituración se pide categoría (Timing, Cliente, Competencia, Financiamiento, Ubicación, Producto, Precio) y subcategoría, con movimiento automático del registro al grupo correspondientecommits 27ba9ea, 852182f
+

6.3 Cobranza, inventario y datos

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
16Cobranza con la cartera realImportación y depuración del archivo “Cobranza Preventa”: 103 planes de pago, 617 pagos y 382 abonos con sus fechas reales; corrección de saldos y del indicador de porcentaje pagadocommits 9b5ba96, 47b00bb, 3137980
17Recibos, mora y recordatoriosRecibo de pago en PDF, recargos por mora configurables, vista de cobranza del día, notas y recordatorios automáticos de pagocommits 2fc80e7, ce95cb9
18Inventario y etapas de la propiedadPipeline de 10 etapas por unidad (Apartado, Reserva, Vendido, Integración, En Análisis, Autorizado, Notaría, Escriturado, Entrega, Postventa), sincronización desde el reporte maestro y bloqueo automático de la unidad al apartarse para que no se venda dos vecescommits 9e04b76, b39fe63, 5063ffe
19Importación de datos maestros del clienteInventario y estatus desde el reporte de estatus de venta; 5 cotizadores de los distintos desarrollos; historial de afluencia de agosto; clientes del archivo maestro; y la cartera de cobranza. Incluye dos re-importaciones completas solicitadas por errores detectados en el origencommits b39fe63, 9ef86ef, 6473fb5, 47b00bb
20Limpiar datos de pruebaDepuración de registros de prueba solicitada en dos ocasiones (pendientes falsos en apartados, leads duplicados, cotizaciones de verificación) y eliminación total de los leads para reiniciar la operacióncommits 50e6eca, 16cacd5
+

6.4 Usuarios, permisos e integración

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
21Sistema de usuarios y rolesLogin con contraseña propia, cambio de contraseña, 27 usuarios y 7 roles con matriz de módulos por rol (Vendedor, Administración de Venta, Director de Ventas, Escrituración, Dirección General, Manager, Administrador)commits de11af9, f72354f, 4965a44
22Alta de usuarios y permisos desde el sistemaAlta de usuarios desde Configuraciones y lápiz de permisos por rol sin tocar códigocommit b997076
23Administración de leads para supervisiónEl administrador puede reasignar, mover y borrar leads y carteras entre vendedores, entre equipos y programascommits 31f83b0, 16cacd5
24AuditoríaBitácora de auditoría con el nombre del usuario (no el identificador), 1,375 registros del sistema en operacióncommits 84582ce, 9630893
25Integración delegada a un solo usuario de MondaySe habilitó que varios vendedores usen la aplicación sin cuenta de Monday, compartiendo un usuario de integración, para después desvincularse por completocommit af2fb10
+

6.5 Chat FRASA (antes “Chat Hermes”)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
26Asistente con acceso a toda la información13 herramientas conectadas a la base de datos con alcance según el rol: consulta de leads, oportunidades, apartados, cobranza, inventario, disponibilidad, contactos y reportes. Respuesta en streaming, formato enriquecido, caché de búsqueda semántica y mantenimiento desde Configuracionescommits 9643f86, 4436059
27Proveedor propio de IASe cambió el esquema contratado (OpenAI + Supabase) por el proveedor propio del cliente, NANcommits 05c4447, 4436059
28Indexación de las guíasLas guías y documentación del sistema se integraron a la base de conocimiento del asistentecommit 9643f86
+

6.6 Presentación, accesibilidad y equipos de trabajo

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
29Portal de inicio (landing) y accesibilidadPágina principal que funciona como lanzador de todos los módulos, con accesos según el rol del usuario y trabajo de accesibilidad (navegación por teclado, etiquetas, contraste, escalado de texto)apps/landing
30Rediseño visual integral de las 7 aplicacionesDos iteraciones de diseño (sistema de diseño indicado por el cliente y rediseño de estructura, no sólo de color), modo oscuro completo, gráficas de tableros rediseñadas y diseño adaptable a la resolución de cada equipocommits 599dce2, d59c8c0, 8b65f90, 7d19e0d, dc6ec78
31Identidad de marcaRenombrado del sistema a CRM360 FRASA con su logotipo, aplicado en las 7 aplicacionescommits 16cacd5
32Aplicación instalable y sin conexión (PWA)Las aplicaciones se pueden instalar en la computadora o el teléfono, funcionan sin conexión y envían notificacionescommit cb86a51
33Botón de regreso al menúBotón de retorno al portal en todos los móduloscommit f72354f
+

6.7 Infraestructura y puesta en producción

+
+ + + + + + + + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
34Acceso remoto sin abrir puertosPublicación del ecosistema por Cloudflare Zero Trust (túnel cifrado como servicio del sistema) en https://frasa.consultoria-as.com, con el sistema operando en la máquina del clientecommit 30c682e
35Repositorio y control de versionesRepositorio institucional en Gitea (FRASA-CAS) con 155 entregas versionadas y respaldo automático diariohistorial del repositorio
+

6.8 Cambio de arquitectura solicitado

+
+ + + + + + + + + + + + + + + + +
#SolicitudQué se entregóEvidencia
36Trabajar sin Monday.comSe solicitó primero operar en modo local cuando Monday fallara, después convivir en ambos modos y finalmente eliminar Monday por completo. Se implementó: operación 100% local con la base de datos propia, sincronización bidireccional mientras convivieron, retiro de los avisos de vinculación y purga total de la dependenciacommits 6064989, 388e1da, 89d95eb, 64b0124
+
+

7. Cambios de sentido solicitados durante el proyecto (retrabajo)

+

Estos pedidos modificaron o revirtieron trabajo ya entregado. Se listan porque implican reelaboración y verificación, no trabajo nuevo.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Cambio solicitadoTrabajo que se rehízo
1Primero integrar Monday como núcleo; después operar en local; después convivir; finalmente eliminarloRediseño del origen de datos, sincronización en ambos sentidos, retiro de avisos y purga completa de la integración
2Afluencias: primero se guardaban como Leads; después se pidió que vivieran en OportunidadesMigración de registros, cambio de reglas de asignación y de los tableros
3Asignación automática (round-robin): se implementó, se limitó al equipo de vendedores, y finalmente se desactivó y se borraron los leads asignadosTres iteraciones y limpieza de datos
4Fases de Oportunidades reordenadas cuatro vecesReordenamiento de botones, reglas de avance y vistas en cada ocasión
5Fases de Escrituración definidas dos veces con distinto ordenSe rehízo el catálogo de fases y su traspaso
6Renombre de módulos: Cobranza → Clientes y de vuelta a Cobranza; Chat Hermes → Chat FRASARotulado, rutas y permisos
7Cotizador: primero se pidió ocultar el descuento; el monto de apartado pasó de mínimo $20,000 a sin mínimo; el PDF pasó de imprimir las 4 propuestas a imprimir sólo la elegidaRediseño de la captura y del documento
8Contactos: primero “que se vean todos los clientes”, después “sólo los asignados a cada vendedor”Se agregó el filtro de privacidad por vendedor
9Campos renombrados: “usuario Enkontrol” → “cliente Enkontrol”; “medio de contacto” → “número de dependientes”; un solo campo de nombre → nombre y dos apellidosCambios de captura, validaciones e importaciones
10Fechas de Obra: primero captura libre por los encargados; después bloqueadas salvo para el administradorSe agregó control de permisos por campo
11Botón “Firma Cliente” en Apartados: primero debía enviar al Alta de Cliente, después a “Alta Enkontrol”Se rehízo el enlace y el nombre
+
+

8. Métricas del entregable

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MétricaValor
Periodo de trabajo20 de agosto al 21 de septiembre de 2026 (33 días naturales, actividad en 23 días)
Entregas versionadas (commits)155
Líneas de código agregadas142,920
Líneas de código vigentes (fuente + pruebas)69,659
Aplicaciones en producción7 + micrositio público
Módulos funcionales12
Endpoints del API137
Tablas en base de datos33
Migraciones de base de datos16
Pruebas automatizadas1,203 pruebas en 131 archivos (todas en verde)
Usuarios en operación27
Roles con permisos diferenciados7
+

Detalle de las pruebas por aplicación: API 708 · Alta de Clientes 124 · Cotizador 115 · Cobranza 92 · Portal 89 · Mi Día 32 · Chat FRASA 22 · Tableros 21.

+
+

9. Documentación y material de capacitación entregado

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DocumentoContenido
Guía de usuario del sistema (PDF)Recorrido de todos los módulos en lenguaje no técnico, dirigida a personal con poca familiaridad con la computadora
Guía del flujo completo por áreaProcedimiento de principio a fin para Ventas, Administración de Venta, Escrituración, Cobranza, postventa y dirección
Guion de demostraciónRecorrido guiado para presentar el sistema a cada departamento
Manual de automatizacionesAutomatizaciones configuradas y su comportamiento por etapa
Documento de entrega del 24 de agostoAvance de la primera fase del proyecto
Este documentoAlcance contratado vs. entregado, para la facturación del desarrollo adicional
+
+

10. Partidas sugeridas para la facturación del desarrollo adicional

+

Las cantidades se dejan en blanco para acordarlas con el cliente. La columna “unidad de cobro sugerida” existe para poder valorar cada partida de forma objetiva.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#PartidaUnidad de cobro sugeridaHorasImporte
6.1-1Módulo Mi Día (CRM del vendedor)Módulo completo
6.1-2Módulo Captura por departamentosMódulo completo
6.1-3Módulo Reportes (10 reportes)Módulo completo
6.1-4Módulo Contactos con privacidad por vendedorMódulo
6.1-5Módulo Clientes Maestro (1,535 registros)Módulo
6.1-6Módulo SoporteMódulo
6.1-7Módulo Configuraciones (permisos y usuarios)Módulo
6.1-8Módulo Desarrollos + micrositio públicoMódulo + sitio público
6.2-9Alta de Afluencia (flujo nuevo separado)Flujo completo
6.2-10Etapa Escrituración (9 fases)Módulo de etapa
6.2-11Oportunidades y Apartados con acciones vinculadasMódulo de etapa
6.2-12Calendario y agenda de citasFuncionalidad
6.2-13Bitácora del clienteFuncionalidad
6.2-14Vista Kanban en 4 bandejasFuncionalidad
6.2-15Descartes con motivo y subcategoríaFuncionalidad
6.3-16Importación y depuración de la cartera de cobranzaProyecto de datos
6.3-17Recibos PDF, mora y recordatorios de cobranzaFuncionalidad
6.3-18Pipeline de 10 etapas de la propiedad y bloqueo de inventarioFuncionalidad
6.3-19Importación de datos maestros (4 archivos, 2 re-importaciones)Proyecto de datos
6.3-20Depuración de datos de pruebaServicio
6.4-21Sistema de usuarios y roles (7 roles)Proyecto
6.4-22Alta de usuarios y permisos desde el sistemaFuncionalidad
6.4-23Reasignación y administración de carteras para supervisiónFuncionalidad
6.4-24Auditoría con nombre de usuarioFuncionalidad
6.4-25Soporte multiusuario sobre un solo usuario de MondayFuncionalidad
6.5-26Chat FRASA con 13 herramientas y alcance por rolProyecto
6.5-27Integración del proveedor de IA propio (NAN)Integración
6.5-28Indexación de la documentación en la base de conocimientoServicio
6.6-29Portal de inicio y accesibilidadProyecto
6.6-30Rediseño visual integral de 7 aplicaciones (2 iteraciones, modo oscuro)Proyecto
6.6-31Identidad de marca CRM360 FRASAServicio
6.6-32Aplicación instalable, sin conexión y con notificaciones (PWA)Funcionalidad
6.6-33Botón de regreso al portal en todos los módulosFuncionalidad menor
6.7-34Publicación por Cloudflare Zero Trust y dominio propioProyecto de infraestructura
6.7-35Repositorio institucional y respaldos automáticosServicio
6.8-36Rediseño de arquitectura para operar sin Monday.comProyecto de arquitectura
7Retrabajo por cambios de sentido solicitados (11 cambios listados)Bolsa de retrabajo
+
+

11. Pendientes y decisiones abiertas con el cliente

+

Para que este documento refleje el estado real, se dejan por escrito los puntos abiertos:

+
    +
  1. WA CRM / WhatsApp — era “fase futura” en el contrato y nunca se solicitó durante el proyecto. Sigue sin ejecutar. Nota: el sistema ya envía recordatorios y avisos internos por otros medios, pero la integración con WhatsApp Business no está construida.
  2. +
  3. Reglas del cotizador en dos desarrollos — los desarrollos Sole y Mutuo existen en el catálogo, pero todavía no tienen reglas de cálculo guardadas, por lo que no aparecen como opción en el cotizador. Se activan en cuanto el administrador las capture desde el módulo de Desarrollos (2 minutos por desarrollo, sin intervención del proveedor).
  4. +
  5. Datos de contacto del micrositio público — el sitio público de desarrollos usa un número de WhatsApp y un correo de ejemplo. Deben entregarse los definitivos para reemplazarlos.
  6. +
  7. PostgreSQL y Redis del contrato original — el contrato preveía PostgreSQL y Redis en contenedores. La operación actual corre sobre la base de datos local del servidor del cliente, que es lo que se solicitó al pedir la eliminación de Monday. Si el cliente quiere migrar a PostgreSQL y Redis, es un proyecto aparte.
  8. +
+
+

Anexo A — Checklist de entrega original vs. estado actual

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Fase del contratoEstado
Fase 0 — Tableros, columnas, permisos e inventario importadoEntregado (y después migrado del tablero de Monday a base de datos propia, por solicitud)
Fase 1 — Automatizaciones, webhooks, round-robin, recordatorios de inactividadEntregado. El round-robin se desactivó después por decisión del cliente
Fase 2 — WhatsApp / WA CRMNo ejecutado (era fase futura y no se solicitó)
Fase 3 — CotizadorEntregado, ampliado a 9 desarrollos, con comparador, vigencia, historial y reglas configurables
Fase 4 — Alta de ClientesEntregado, ampliado a dos flujos (Afluencia y Cliente), con etapas, documentos y firma
Fase 5 — CobranzaEntregado, con la cartera real del cliente en operación, recibos, mora y recordatorios
Fase 6 — ReportesEntregado en el módulo de Tableros y ampliado a 10 reportes descargables
Fase 7 — Agente de IAEntregado como Chat FRASA, con proveedor propio y acceso a la base de datos
Fase 8 — Go-Live, capacitación y manualEntregado: sistema en operación, guía de usuario en PDF, guía por área y guion de demostración
+
+

Anexo B — Cronología de actividad

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SemanaDíasActividad principal
120 – 23 de agostoArranque: 3 aplicaciones web, API Gateway, infraestructura, primeras revisiones de diseño
224 – 30 de agostoCotizador con inventario real, alta de clientes, login, tableros, acceso remoto
331 de agosto – 6 de septiembreFlujo comercial completo, roles, modo local, separación de afluencia y cliente
47 – 13 de septiembreSemana de mayor intensidad: captura por departamentos, Mi Día con etapas, reportes, bitácora, escrituración, estilo oscuro
514 – 20 de septiembreReportes definitivos, rol Manager, correcciones de flujo y auditoría completa por módulo
621 de septiembreMódulo de Desarrollos, micrositio público y entrega de este documento
+
+

Documento preparado por Consult (AS) Technology · 21 de septiembre de 2026

+
+
+ + + + diff --git a/docs/web/alcance-2f9k7d/logo.png b/docs/web/alcance-2f9k7d/logo.png new file mode 100644 index 0000000..51aa3d5 Binary files /dev/null and b/docs/web/alcance-2f9k7d/logo.png differ diff --git a/scripts/generar-pdf-documento.py b/scripts/generar-pdf-documento.py index 4bbace3..f604e51 100644 --- a/scripts/generar-pdf-documento.py +++ b/scripts/generar-pdf-documento.py @@ -38,14 +38,22 @@ COLOR_FONDO_LOGO = (247, 247, 247) TOLERANCIA_FONDO = 8 +DIMENSION_LOGO = (480, 360) + + def logo_con_transparencia() -> pathlib.Path: """El logo del repo trae fondo gris; se aplana a alfa 0 desde los bordes. Se rellena solo desde el exterior para no borrar los grises interiores del arte (contrafuertes de letras). Sin transparencia, la portada blanca dibuja un rectángulo gris alrededor del logotipo. + + La salida se reduce a `DIMENSION_LOGO`: el original de 959 px pesa 440 KB + (viene de un JPEG, con ruido que el PNG no comprime) y se dibuja a 24 mm, así + que sobra resolución. El nombre del archivo incluye el tamaño para no reusar + una versión vieja con otras medidas. """ - destino = pathlib.Path(tempfile.gettempdir()) / 'frasa-logo-transparente.png' + destino = pathlib.Path(tempfile.gettempdir()) / f'frasa-logo-transparente-{DIMENSION_LOGO[0]}x{DIMENSION_LOGO[1]}.png' if destino.exists(): return destino @@ -83,7 +91,8 @@ def logo_con_transparencia() -> pathlib.Path: if y < alto - 1: cola.append((x, y + 1)) - imagen.save(destino) + imagen.thumbnail(DIMENSION_LOGO) + imagen.save(destino, optimize=True) return destino diff --git a/scripts/generar-web-documento.py b/scripts/generar-web-documento.py new file mode 100644 index 0000000..9e2365f --- /dev/null +++ b/scripts/generar-web-documento.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +"""Publica un documento de `docs/` como página web no listada. + +Lee el mismo markdown que el generador de PDF, arma una página con el diseño +institucional (barra superior con logotipo, hoja blanca, tablas adaptables y modo +oscuro) y deja junto a ella el PDF para descargar. El resultado se sirve por +NGINX desde una ruta privada: no se enlaza desde ninguna app, así que sólo se +llega con el enlace. + +Uso: + /root/.venv-tools/bin/python scripts/generar-web-documento.py \\ + docs/ALCANCE-VS-ENTREGADO.md --slug alcance-2f9k7d + +Salida: docs/web//index.html + docs/web//.pdf + logo.png + +Requiere weasyprint, markdown y Pillow, que en esta máquina viven en el entorno +aislado /root/.venv-tools (no en el python del sistema). +""" +from __future__ import annotations + +import argparse +import html as html_mod +import importlib.util +import pathlib +import re +import shutil +import subprocess +import sys + +import markdown + +RAIZ = pathlib.Path(__file__).resolve().parent.parent +AQUI = pathlib.Path(__file__).resolve().parent +SCRIPT_PDF = AQUI / 'generar-pdf-documento.py' +PROVEEDOR = 'Consult (AS) Technology' + + +def cargar_generador_pdf(): + """Importa el generador de PDF (el nombre del archivo lleva guiones).""" + especificacion = importlib.util.spec_from_file_location('generador_pdf', SCRIPT_PDF) + if especificacion is None or especificacion.loader is None: + raise SystemExit(f'No se pudo cargar {SCRIPT_PDF}') + modulo = importlib.util.module_from_spec(especificacion) + especificacion.loader.exec_module(modulo) + return modulo + + +CSS_WEB = """ +:root { + --marca: #023681; + --acento: #1B4332; + --acento-claro: #2D6A4F; + --texto: #1F2937; + --texto-suave: #6B7280; + --fondo: #EEF1F0; + --hoja: #FFFFFF; + --borde: #DCE1E8; + --zebra: #F6F8F7; +} +@media (prefers-color-scheme: dark) { + :root { + --marca: #7FA8E8; + --acento: #4ADE9B; + --acento-claro: #6EE7B7; + --texto: #E2E8F0; + --texto-suave: #94A3B8; + --fondo: #0B1220; + --hoja: #131C2B; + --borde: #2A3648; + --zebra: #182233; + } +} +* { box-sizing: border-box; } +html { -webkit-text-size-adjust: 100%; } +body { + margin: 0; + background: var(--fondo); + color: var(--texto); + font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 16px; + line-height: 1.65; +} +.barra { + position: sticky; top: 0; z-index: 10; + display: flex; align-items: center; gap: 16px; + padding: 12px max(20px, calc((100vw - 1040px) / 2)); + background: var(--hoja); + border-bottom: 3px solid var(--acento); +} +.barra img { height: 38px; display: block; } +.barra .rotulo { display: flex; flex-direction: column; line-height: 1.25; margin-right: auto; } +.barra .rotulo strong { font-size: 15px; letter-spacing: .2px; } +.barra .rotulo span { font-size: 12.5px; color: var(--texto-suave); } +@media (max-width: 640px) { .barra .rotulo span { display: none; } } +.barra a.boton { + display: inline-flex; align-items: center; gap: 8px; + padding: 9px 16px; border-radius: 999px; + background: var(--marca); color: #fff; text-decoration: none; + font-size: 14px; font-weight: 600; white-space: nowrap; +} +.barra a.boton:hover { filter: brightness(1.12); } +@media (prefers-color-scheme: dark) { .barra a.boton { color: #0B1220; } } + +main { max-width: 1040px; margin: 28px auto 56px; padding: 0 20px; } +.hoja { + background: var(--hoja); + border: 1px solid var(--borde); + border-radius: 14px; + padding: 44px 52px 36px; + box-shadow: 0 1px 3px rgba(15, 23, 42, .06), 0 12px 32px -18px rgba(15, 23, 42, .25); +} +@media (max-width: 720px) { .hoja { padding: 26px 20px; border-radius: 10px; } } + +.sello { + display: inline-block; margin: 0 0 18px; + padding: 5px 12px; border-radius: 999px; + background: var(--zebra); border: 1px solid var(--borde); + font-size: 12px; letter-spacing: .4px; text-transform: uppercase; color: var(--texto-suave); +} +h1 { font-size: 30px; line-height: 1.22; margin: 0 0 6px; color: var(--acento); letter-spacing: -.4px; } +p.bajada { font-size: 17px; color: var(--texto-suave); margin: 0 0 26px; } + +.ficha { border: 1px solid var(--borde); border-radius: 10px; overflow: hidden; margin: 0 0 34px; } +.ficha div { display: flex; gap: 16px; padding: 10px 16px; border-bottom: 1px solid var(--borde); } +.ficha div:last-child { border-bottom: none; } +.ficha div:nth-child(odd) { background: var(--zebra); } +.ficha dt { + flex: 0 0 210px; margin: 0; font-size: 12px; letter-spacing: .5px; + text-transform: uppercase; color: var(--texto-suave); padding-top: 3px; +} +.ficha dd { margin: 0; font-weight: 600; font-size: 14.5px; } +@media (max-width: 620px) { .ficha div { display: block; } .ficha dt { margin-bottom: 2px; } } + +h2 { + font-size: 21px; color: var(--acento); margin: 44px 0 14px; + padding-bottom: 8px; border-bottom: 2px solid var(--acento-claro); + scroll-margin-top: 80px; +} +h3 { font-size: 17px; color: var(--acento-claro); margin: 30px 0 10px; } +p { margin: 0 0 14px; } +ul, ol { margin: 0 0 16px; padding-left: 24px; } +li { margin-bottom: 6px; } +strong { color: var(--acento); } +@media (prefers-color-scheme: dark) { strong { color: var(--texto); } } +hr { border: none; border-top: 1px solid var(--borde); margin: 34px 0; } + +.tabla-scroll { overflow-x: auto; margin: 16px 0 26px; border: 1px solid var(--borde); border-radius: 10px; } +table { width: 100%; border-collapse: collapse; font-size: 14px; } +th { + background: var(--acento); color: #fff; text-align: left; + padding: 10px 12px; font-size: 13px; font-weight: 600; white-space: nowrap; +} +td { padding: 9px 12px; border-top: 1px solid var(--borde); vertical-align: top; } +tbody tr:nth-child(even) td { background: var(--zebra); } +td:first-child { white-space: nowrap; } +@media (max-width: 620px) { td:first-child { white-space: normal; } } + +footer.pie { + max-width: 1040px; margin: 0 auto 40px; padding: 0 20px; + font-size: 13px; color: var(--texto-suave); text-align: center; +} +@media print { + body { background: #fff; } + .barra { position: static; border-bottom: 1px solid #ccc; } + .barra a.boton { display: none; } + main { max-width: none; margin: 0; padding: 0; } + .hoja { border: none; box-shadow: none; padding: 0; } + h2 { break-after: avoid; } + .tabla-scroll { overflow: visible; border: none; } + table { font-size: 11pt; } + tr { break-inside: avoid; } +} +""" + + +def envolver_tablas(cuerpo: str) -> str: + """Las tablas se envuelven para poder desplazarse en pantallas chicas.""" + cuerpo = cuerpo.replace('', '
') + return cuerpo.replace('
', '') + + +def ficha_html(datos: list[tuple[str, str]]) -> str: + if not datos: + return '' + filas = '\n'.join( + f'
{html_mod.escape(etiqueta)}
{html_mod.escape(valor)}
' + for etiqueta, valor in datos + ) + return f'
\n{filas}\n
\n' + + +def main() -> None: + parser = argparse.ArgumentParser(description='Publica un documento de docs/ como página web') + parser.add_argument('markdown', type=pathlib.Path, help='ruta del .md de entrada') + parser.add_argument('--slug', required=True, help='carpeta de la ruta privada (p. ej. alcance-2f9k7d)') + parser.add_argument('--salida', type=pathlib.Path, help='carpeta destino (por defecto docs/web/)') + args = parser.parse_args() + + generador = cargar_generador_pdf() + origen = args.markdown.resolve() + destino = args.salida or RAIZ / 'docs' / 'web' / args.slug + destino.mkdir(parents=True, exist_ok=True) + + titulo, subtitulo, datos, cuerpo_md = generador.partir_encabezado(origen.read_text(encoding='utf-8')) + principal, _, secundario = titulo.partition(' — ') + + # El PDF es la versión para imprimir y compartir: se genera aquí mismo. + # Conserva el nombre del markdown de origen (sin prefijo duplicado del título). + nombre_pdf = f'{origen.stem}.pdf' + ruta_pdf = destino / nombre_pdf + subprocess.run( + [sys.executable, str(SCRIPT_PDF), str(origen), '--salida', str(ruta_pdf)], + check=True, + capture_output=True, + ) + + logo_destino = destino / 'logo.png' + shutil.copy2(generador.logo_con_transparencia(), logo_destino) + + cuerpo = envolver_tablas(markdown.markdown(cuerpo_md, extensions=['tables', 'sane_lists', 'attr_list'])) + anio = next((v for k, v in datos if 'fecha' in k.lower()), '') + pie = f'{PROVEEDOR} · {anio}' if anio else PROVEEDOR + + pagina = f""" + + + + + + + {html_mod.escape(principal)} — {html_mod.escape(secundario or subtitulo)} + + + + +
+ CRM360 FRASA +
+ {html_mod.escape(principal)} + {html_mod.escape(secundario or subtitulo)} +
+ Descargar PDF +
+ +
+
+

Documento confidencial · Preparado para el cliente

+

{html_mod.escape(principal)}

+

{html_mod.escape(secundario or subtitulo)}

+{ficha_html(datos)}{cuerpo} +
+
+ + + + +""" + (destino / 'index.html').write_text(pagina, encoding='utf-8') + + print(f'Página generada: {destino / "index.html"} ({(destino / "index.html").stat().st_size} bytes)') + print(f'PDF para descarga: {ruta_pdf} ({ruta_pdf.stat().st_size} bytes)') + + +if __name__ == '__main__': + main()