VE Facilites es un piloto de demostración de una plataforma para mantenimiento de edificios y condominios. La idea es transformar la visita técnica en un registro que el síndico consiga leer y verificar: la orden de servicio, el checklist de la disciplina, las fotos, la firma de quien recibió el servicio y, al final, un dictamen en PDF. El sistema tiene una API en Java, una aplicación Flutter para el técnico de campo y páginas web para el sitio, el portal del síndico y el panel de operación.

Es un piloto, y nada más que eso: no hay cliente contratado. Este caso técnico muestra lo que hace el código, las reglas que impiden que el dictamen invente datos, los errores que se volvieron regla y lo que todavía es maqueta. Los números se verificaron en el repositorio en la versión del 30/10/2026, la más reciente.

Transparencia: VE Facilites es un proyecto del autor de este blog, Jeremias Cardoso. El texto no cita empresa, personas ni ciudad, y deja fuera servidores, credenciales y detalles de seguridad.

Nota de actualización (30/10/2026): este post se reescribió como caso técnico, y el nombre del producto se corrigió a VE Facilites. La versión del 23/09/2026 citaba una región, que salió del texto. También presentaba cinco disciplinas, una aplicación que funcionaba sin red y se sincronizaba sola, fotos con anotaciones, firma biométrica y dictámenes en conformidad con el CREA. Verificado en el código, la base tiene nueve disciplinas, e incendio no es una disciplina propia: queda dentro de seguridad electrónica. Sin red, la aplicación solo retoma un borrador ya guardado, y el envío es manual. La foto no tiene anotación, la firma es un dibujo en la pantalla y el sistema no consulta al CREA: se niega a emitir un dictamen sin responsable técnico y ART informados. Este texto describe lo que hace el código. El 04/11/2026, la identificación del proyecto pasó a ser solo la autoría de este blog.

El problema: la visita técnica que no se vuelve prueba

En el mantenimiento de edificios, el servicio suele terminar en una conversación y en una foto de celular. El síndico no sabe qué se verificó en el tablero eléctrico, la administradora no tiene historial por equipo, y cuando alguien impugna un servicio meses después nadie sabe decir quién lo hizo, cuándo, qué vio y quién respondía técnicamente por aquello. Para una rutina pequeña, las hojas de cálculo de control resuelven; el problema empieza cuando el registro tiene que servir de prueba.

La planificación del proyecto describe la oportunidad así: vender continuidad operacional de ingeniería a condominios y “probar cada entrega con agenda, orden de servicio, checklist, fotos, historial, responsables y cierre visible para el cliente”. El mismo documento manda evitar “cualquier alegación de cumplimiento no auditada”. Las dos frases resumen la intención: registrar evidencia, y no afirmar conformidad por cuenta propia. El dictamen de hoy todavía se desliza en eso, como muestra la lista de lo que falta, al final de este texto.

El piloto en números

Números del repositorio el 30/10/2026, fecha del último commit.

El piloto en números
MedidaValor
API, sin líneas en blanco y sin comentarioscerca de 7,7 mil líneas: 6.948 en Java y 799 en 15 migraciones de base
Pruebas de la API7.780 líneas en 27 clases, con 263 métodos de prueba; 91 de ellos exigen una base PostgreSQL descartable
Aplicación de campo (Flutter)3.855 líneas de Dart, más 2.265 de pruebas
Páginas web22,4 mil líneas en 17 archivos HTML y JavaScript, contando prototipos y el paquete de demostración
Pruebas en esta revisión (30/10/2026)API: 183 ejecuciones sin base, ninguna falla; aplicación: 76 pruebas aprobadas; las 91 que exigen base no corrieron
Catálogo en la base9 disciplinas y 55 criterios de checklist
Historial en Git296 commits, del 20/08 al 30/10/2026
Versión1.0.0-SNAPSHOT, sin tag

La API usa Java 21, Spring Boot 3.3.4, PostgreSQL con migraciones versionadas y la biblioteca OpenPDF para el dictamen. La aplicación usa Flutter, con SQLite en el dispositivo, captura de firma y cámara.

De la orden de servicio al dictamen

El sistema tiene cuatro perfiles: administración, gestión de la operación, técnico de campo y síndico. El centro de todo es la orden de servicio, que puede ser preventiva mensual, correctiva urgente, inspección diagnóstica, termografía periódica u ocasional. Pasa por cuatro estados: agendada, en curso, concluida o cancelada.

Cada orden de servicio pertenece a una disciplina y recibe los ítems de checklist del catálogo de esa disciplina. Las seis primeras disciplinas entraron en una migración de septiembre: eléctrica, hidrosanitaria, civil y fachadas, climatización (con los ítems del plan de mantenimiento del aire acondicionado), seguridad electrónica e incendio, y servicio ocasional, con 45 criterios. Una migración posterior añadió otras tres, ligadas a fachada, mantenimiento de edificios e inspecciones, con 10 criterios más.

La conclusión es el momento más vigilado. La API solo concluye una orden que esté en curso y verifica, entre otras cosas:

  • si el presupuesto fue aprobado por el cliente, cuando la orden exige presupuesto previo;
  • si se informó el nombre de quien firma y si la firma enviada es una imagen válida;
  • si las respuestas cubren exactamente los ítems registrados en la orden, ni uno más, ni uno menos;
  • si cada ítem que exige foto tiene al menos una foto adjunta;
  • si cada ítem marcado como crítico tiene una observación técnica que describe el defecto.

Si todo pasa, las respuestas, la firma, el resumen y el nuevo estado se graban de una vez, con un recibo del envío. Una prueba de integración, de las que exigen base y no corrieron en esta revisión, se escribió para probar que, si falla la grabación de la firma, no queda nada grabado.

Las fotos tienen un tipo declarado por quien fotografió (antes, durante, después, defecto crítico, placa del equipo, entre otros) y leyenda. Al llegar al servidor, el archivo gana un resumen SHA-256, una especie de huella digital del contenido: si un solo byte cambia después, el resumen ya no coincide.

El dictamen que se niega a inventar

El dictamen en PDF es la parte más madura del sistema. Como el motor del simulador de maniobras, falla cerrado: ante la duda, rechaza. Las reglas están escritas en el código, con el motivo de cada una:

  • solo sale de una orden de servicio concluida;
  • sin ingeniero responsable técnico y número de la ART vinculados a la orden, la emisión se rechaza, con un mensaje que dice qué falta, a quién pedirlo y por qué: el dictamen declara responsabilidad ante el CREA. El comentario del código completa: ni el responsable ni la ART pueden ser elegidos o generados por el sistema;
  • todo campo vacío sale como “no informado”. Nada de “N/A”, que se leería como “no se aplica”, y nada de un nombre plausible en lugar de un nombre ausente;
  • un ítem sin resultado sale como “NO EVALUADO” y no entra en el conteo de ítems conformes;
  • si una foto no puede leerse del almacenamiento, o si la firma está dañada, el dictamen no sale;
  • cada foto impresa trae el resumen SHA-256, o la frase “hash no disponible — integridad no verificable”.

La emisión ocurre una vez. Los bytes del PDF, la fecha y el resumen SHA-256 del archivo entero quedan grabados en la orden de servicio. En las descargas siguientes, el sistema no genera el documento de nuevo: devuelve el original preservado, después de verificar que el resumen todavía coincide. Si el original desapareció o no coincide, la respuesta es un error, y el sistema avisa que no va a sustituir el documento.

Se diseñó una página de verificación para cerrar el ciclo. La persona elige el archivo del dictamen, el navegador calcula el resumen SHA-256 en el propio dispositivo y pregunta a la API cuál dictamen tiene ese resumen. El PDF también trae un código 2D con la dirección de esa consulta. Por ahora, la página apunta a la dirección local de desarrollo de la API, fija en el código.

La aplicación de campo y lo que quiere decir “sin red”

La aplicación del técnico muestra la agenda de órdenes de servicio, abre el checklist, toma fotos y captura la firma del receptor dibujada en la pantalla, grabada como imagen PNG. La propia documentación de la API dice que eso es evidencia de captura, “sin alegación de identidad criptográfica o firma calificada”.

El borrador de cada inspección queda en una base SQLite del dispositivo, separado por usuario y por orden de servicio. El README de la aplicación es directo sobre lo que eso permite: sin red, un borrador ya guardado puede retomarse, pero la orden no se concluye sin red y no hay sincronización automática.

El envío de la conclusión es manual. Antes de él, la aplicación verifica localmente si todos los ítems fueron respondidos y si hay resumen, receptor y firma. El cuerpo del envío se graba en el dispositivo antes de salir, y el reintento repite exactamente el mismo cuerpo. Un envío pendiente queda trabado para edición: el código responde que “el contenido de un envío pendiente no puede alterarse”. Las fotos suben en el momento en que se adjuntan; si la red falla, la aplicación avisa y la foto no entra en una cola.

Esto es menos de lo que la versión anterior de este texto prometía, y es lo que existe. En un garaje sin señal, el técnico consigue continuar y guardar un checklist que ya abrió con red, pero necesita red para mandar fotos y concluir.

Las páginas web: lo que habla con la API y lo que es maqueta

Son tres públicos, en páginas separadas. El sitio institucional tiene un formulario que envía el contacto de verdad y la página de verificación del dictamen. El panel de operación, usado por la administración, conversa con la API. El portal del síndico mezcla las dos cosas: un modo real, ligado a la API, y once pantallas ilustrativas que avisan lo que son, con frases como “En esta maqueta el campo ya viene lleno — no hay envío real de archivo” y “Los valores de esta maqueta son ilustrativos y no representan una tabla de precios”.

Esas frases nacieron de un error. En septiembre, un informe de revisión visual describía “conexión real” con la base, y las tres páginas web no tenían ninguna llamada de red en el código. La regla que quedó: la apariencia no prueba integración. La prueba es buscar la llamada a la API en el código, no mirar la pantalla.

La demostración en un clic

Para mostrar el sistema sin instalar nada, el repositorio tiene un paquete de demostración para Windows. Un archivo de dos clics levanta un pequeño servidor local, escrito en PowerShell, y abre el navegador en una página con el sitio, el portal, un simulador de la aplicación en un marco de celular y un guion de pruebas. El simulador avisa en la pantalla que es ilustrativo y que no autentica ni sincroniza datos.

El servidor es en PowerShell por un tropiezo. La primera versión buscaba Python, y en Windows el comando python puede existir sin que Python esté instalado: es un acceso directo de la tienda de Microsoft, que abre una ventana y no sirve ninguna página. Empaquetar el intérprete dejaría el archivo demasiado grande para mandarlo por WhatsApp.

Incidentes que se volvieron regla

  1. El dictamen que inventaba un responsable técnico (septiembre de 2026). La primera batería de pruebas contra el sistema en marcha, el 14/09, reprobó el piloto y aprobó solo el fundamento técnico. Una de las causas: el dictamen completaba solo lo que faltaba. Cuando la orden no tenía responsable técnico, el código ponía un nombre, números de registro y un número de ART derivado del código de la orden. El 15/09, once de esos puntos salieron del código, y una migración anuló las ART fabricadas que ya estaban en la base. El comentario de la migración dice: “La ART es un hecho jurídico emitido por el CREA”, y “el sistema no puede deducir ninguno de los dos”. Desde entonces, la falta de un dato se vuelve rechazo o “no informado”.
  2. Ninguna orden salía de agendada (14/09/2026). En la misma batería, la API no tenía ningún camino que llevara la orden de agendada a en curso, y la conclusión exige en curso. Toda orden nacía y moría agendada. El camino de inicio se creó en las rondas siguientes, junto con la regla de la foto obligatoria y de la observación en un ítem crítico.
  3. El hash que cambiaba en cada descarga (17/09/2026). El PDF se generaba de nuevo en cada pedido, con la hora de ese momento en los metadatos, y cada copia tenía un resumen SHA-256 distinto, lo que invalidaba la vía ya entregada. La primera corrección fijó la fecha de emisión en los metadatos. La definitiva, de octubre, fue guardar los bytes del primer PDF y devolver siempre el mismo archivo.
  4. El acceso directo que fingía ser Python (21/09/2026). Descrito arriba: la demostración pasó a usar un servidor en PowerShell.
  5. La tarea cerrada por una prueba (02/10/2026). Una revisión del control del proyecto encontró tareas marcadas como concluidas sin el acto que las concluía. La de contratar el condominio piloto se había cerrado con un registro de prueba en la base. La regla registrada: una tarea cuyo “listo” es un acto humano no se cierra con un archivo; exige la prueba externa, como un contrato o un mensaje.
  6. La capa comercial era maqueta (23/10/2026). Una evaluación del sitio y del portal concluyó que el núcleo de orden de servicio y dictamen era real, pero la captación, el catálogo, los planes, la contratación, el presupuesto y la comunicación eran maqueta. De ahí vino el diseño actual de piloto cerrado: cliente invitado, cuenta creada por la administración y servicio ocasional desactivado, con el mensaje “Los servicios ocasionales no están disponibles en el piloto cerrado”. El 27/10, dos migraciones crearon el núcleo comercial de verdad: clientes, servicios, planes, solicitudes de visita, presupuesto ligado a la orden de servicio y registro de contactos.
  7. El formulario que fallaba callado (29/10/2026). Una edición en un script incrustado en el HTML del panel de operación dejó un error de sintaxis. El navegador descartó el script entero, y el formulario de entrada dejó de funcionar sin mostrar ningún error, la misma falla silenciosa que derriba automatizaciones. La regla que quedó: verificar la sintaxis de todos los scripts de las páginas antes de publicar.

Pruebas: la API y la aplicación corrieron, la base no

En esta revisión, las pruebas que no necesitan base corrieron con Maven: 183 ejecuciones en 17 clases, ninguna falla. Cubren el dictamen en PDF (30 ejecuciones), el flujo de punta a punta de la orden de servicio, las fotos, el catálogo, la verificación pública y la validación de la firma, entre otros. Las pruebas de la aplicación Flutter también corrieron: 76 aprobadas. Cubren el borrador, el envío, la sesión que expira en medio del checklist y las fallas de SQLite.

Las 91 pruebas de las 8 clases marcadas para PostgreSQL quedan fuera de la ejecución estándar y solo corren contra una base descartable preparada para eso. No se ejecutaron aquí.

Lo que todavía no está resuelto

  • Ningún cliente. El piloto no tiene cliente contratado. La decisión de nombrar el primer condominio y la fecha de inicio sigue pendiente en el control del proyecto.
  • Responsable técnico y ART reales. Sin ellos, el sistema, correctamente, no emite ningún dictamen. Es una decisión del dueño, no del código.
  • Checklists sin el bloque normativo. El documento de los checklists se declara borrador propuesto y avisa que no se lo aplique en una orden real antes de la validación del responsable técnico. El bloque que citaría la norma está vacío a propósito.
  • El dictamen afirma lo que nadie verificó. Incluso con el bloque normativo vacío, el encabezado del PDF trae la línea “Conformidad normativa: ABNT NBR 5674 (Mantenimiento de edificios) · ABNT NBR 14037 (Operación y uso) · Legislación vigente”, y el pie repite las dos normas. Ninguna parte del sistema verifica esa conformidad. Es la misma familia de error que el proyecto ya corrigió en el responsable técnico, ahora en el encabezado.
  • Verificación todavía local. La página de verificación del dictamen apunta a la dirección de desarrollo de la API; publicada como está, no consulta nada.
  • Disciplinas contadas de tres maneras. La planificación habla de cinco, el guion de demostración de cuatro y la base tiene nueve.
  • Aplicación sin red de verdad. No hay sincronización automática, conclusión sin red ni cola de fotos.
  • Portal del síndico en parte maqueta. Once pantallas son ilustrativas.
  • Pago y factura fuera del sistema. La plataforma no cobra ni factura. El núcleo comercial arma atajos a WhatsApp y registra contactos, pero la API oficial de WhatsApp, el pago y la factura quedaron para después del piloto.
  • Documentación desactualizada en puntos. El README de la API todavía cita las migraciones hasta la V13, y la base ya está en la V15.

Lecciones que valen fuera del mantenimiento de edificios

  1. Un dato plausible es un dato inventado. Un nombre o un número de ART “de ejemplo” en un dictamen es peor que el campo vacío.
  2. La ausencia se declara. “No informado” y “no evaluado” cuentan la verdad; “N/A” y “conforme” por omisión, no.
  3. Un documento emitido no se regenera. Guardar los bytes del original es más simple y más fuerte que intentar reproducir el mismo archivo.
  4. La apariencia no prueba integración. Busque la llamada a la API en el código antes de creer en la pantalla.
  5. “Sin red” necesita una definición escrita. Decir qué funciona sin señal, y qué no funciona, evita la promesa equivocada.
  6. Una tarea de acto humano se cierra con prueba externa. Contrato, mensaje o documento, no una prueba con un dato ficticio.
  7. Un piloto sin cliente es una demostración. El código listo no sustituye al primer condominio real, la misma lección de ZapOrca, un MVP todavía sin usuarios.

Fuentes