Sistema de Cavas
CAVA-1−18,0 °C

Cadena de frío
que sí emite el PDF

Cadena de frío, procesos y nómina para una procesadora de alimentos venezolana, offline-first y sin servidor propio.

Offline-firstPlan gratuito, sin Cloud Functions
Software empresarial · ERP de cadena de frío (Android + web)2026

Rol

Análisis del negocio, especificación técnica, arquitectura, sistema de diseño y desarrollo Flutter + Firebase

Estado

En desarrollo activo — Fase 0 y 0.5 (cimientos, identidad de marca, acceso y roles) construidas sobre un plan de 11 fases ya especificado

Sistema interno · sin enlace público
CAVA-1−18,0 °C
01 / El sistema anterior

Lo que no salía

Una aplicación de AppSheet que se quedó a medias. Tres fallas concretas la hacían inservible.

AVERÍA 01

El PDF que nunca salió

La nota de recepción no se emitía en PDF con la dirección completa del cliente.

Es la razón por la que el dueño pagó la aplicación anterior.

AVERÍA 02

La nota de entrega sin IVA

No había forma de emitir el documento sin IVA que exige el negocio.

Sin ese papel no se despacha mercancía de la cava.

AVERÍA 03

El estado vivía en una cabeza

El avance real de cada trabajo no estaba en el sistema, sino en la memoria de alguien.

Nadie podía decir, al kilo, qué tiene cada cliente y desde cuándo.

El parte completo

La planta trabajaba con una aplicación de AppSheet que se quedó a medias. Tres fallas concretas la hacían inservible: la nota de recepción nunca salía en PDF con la dirección completa del cliente, no había forma de emitir la nota de entrega sin IVA que exige el negocio, y el estado real de cada trabajo vivía en la cabeza de alguien en vez de en el sistema. A eso se suma el contexto: cavas con señal de móvil pobre y cortes eléctricos, operarios manipulando el teléfono con guantes de nitrilo mojados a −20 °C o bajo sol directo, doble moneda con tasa BCV e IGTF, kilos que hay que cuadrar al gramo entre lo que entra, lo que se procesa, la merma y lo que sale, y una nómina venezolana con feriados, descuento del día de descanso, prestaciones y utilidades. El dueño necesitaba saber, al kilo, qué tiene cada cliente en cada cava y desde cuándo, y poder cobrar por ello.

−20 °CGuantes de nitrilo mojadosSol directoCortes eléctricosDoble moneda e IGTFSin señal en la cava
TUNEL-1−35,0 °C
02 / La solución

Backend sin backend

Cada hueco que deja el plan gratuito tiene su compensación explícita, escrita en la especificación.

Una aplicación Flutter única para Android (planta) y web (administración) sobre Firebase en plan gratuito, diseñada desde el primer día como offline-first: la caché de Firestore siempre encendida y sin límite de tamaño, identificadores generados en el cliente, correlativos por bloques reservados para poder emitir documentos sin red, y fechas de negocio escritas como literales porque el sello de servidor vale nulo mientras no sincroniza. La seguridad no depende de la interfaz: las reglas de Firestore deniegan todo por defecto y se abren colección por colección, el rol vive en el documento del usuario y se lee con un único get() por evaluación, la auditoría y el kardex son estrictamente append-only, y ningún usuario puede tocar los campos de control de su propio perfil. El dinero se guarda en centavos enteros y el peso en gramos enteros, con la tasa a ocho decimales y aritmética en BigInt, porque un double en Firestore descuadra cuentas por cobrar con hiperinflación. Y toda la identidad visual sale del logo real de la empresa: el azul petróleo de las ondas del mar significa frío y bajo control, el naranja del sol significa que algo se salió de rango.

LO QUE HARÍA UNA CLOUD FUNCTIONLO QUE LO SUSTITUYE EN SPARKTrigger onCreate / onUpdateReglas que validan lo que validaría el triggerNumeración correlativa en servidorTransacción y bloques de correlativo reservadosCustom claims con el rolRol en usuarios/{uid}, leído con un solo get()Cron nocturno de cómputosCómputo en el cliente al abrir la pantallaRender de PDF remotoPDF generado en el propio dispositivoAdmin SDK corriendo en servidorScripts Node ESM ejecutados a manoBlaze queda documentado, pero no se activa antes de la Fase 5.
TUNEL-1−35,0 °C
03 / Lo que sostiene el sistema

Seis piezas que aguantan el frío

01

Backend sin backend: todo el diseño cabe en el plan gratuito

Sin Cloud Functions no hay triggers, ni cron, ni custom claims, ni numeración en servidor, ni render de PDF remoto. Cada uno de esos huecos tiene su compensación explícita: reglas de seguridad que validan lo que un trigger validaría, transacciones para los correlativos, cómputo al abrir la pantalla, PDF generado en el propio dispositivo y scripts de Node con firebase-admin que el desarrollador ejecuta a mano. La documentación incluye una tabla de qué cambia el día que se active Blaze, y la recomendación de no activarlo hasta la Fase 5.

02

Reglas que deniegan por defecto, sin comodines

firestore.rules no tiene ningún match /{document=**} permisivo: una subcolección futura no puede quedar expuesta por olvido. Hoy solo están abiertas usuarios, config y auditoria. El perfil propio se lee siempre porque el guard de sesión lo necesita, pero listar la colección es solo del administrador; nadie se auto-eleva el rol porque la actualización propia bloquea los campos rol, activo, clienteId y estacionesPermitidas; y los usuarios no se borran, se desactivan, para no perder la trazabilidad de quién hizo qué.

03

Offline-first porque el camión llega de madrugada a una cava sin señal

La persistencia de Firestore arranca con caché ilimitada en main.dart. El identificador de cada documento es generado en el cliente y el número legible va en un campo aparte, porque los correlativos exigen transacción y una transacción no funciona sin red: un documento creado offline queda como «S/N (pendiente de numerar)» con chip ámbar y tiene prohibido pasar a estado cerrado hasta que se numere. Las fechas de negocio se derivan en el cliente a hora de Caracas y se escriben como literales, porque serverTimestamp vale nulo en la caché local hasta que sincroniza.

04

Enteros escalados: ni un céntimo ni un gramo en coma flotante

Peso en gramos enteros, dinero en centavos enteros, tasa BCV a ocho decimales, porcentajes en centésimas y temperatura en décimas de grado. Todo producto de tasa por monto se calcula en BigInt y se convierte al final, porque en Flutter web los enteros de Dart son doubles de 53 bits y el error aparece justo al conciliar contra el extracto bancario. El motivo está escrito en la especificación: FieldValue.increment sobre double acumula error de coma flotante en el saldo y en los kilos en cava; con enteros el incremento es exacto y conmutativo.

05

Paleta muestreada del logo, con cada contraste verificado

Los hexadecimales no son inventados: se muestrearon píxel a píxel del logo real de la empresa y cada par de colores está calculado con la fórmula WCAG 2.1. De ahí salen dos reglas duras que el código repite en comentarios: sobre el naranja del sol el texto es #2B1400 y nunca blanco, porque el blanco da 2,37:1; y el teal medio y el oliva de la colina son decorativos, jamás fondo de texto. Hay además un anti-objetivo escrito: ningún tono entre 260° y 330° en toda la aplicación, porque el lila es el color por defecto de AppSheet y es exactamente lo que el dueño rechaza.

06

Diseñado para guantes mojados, sol directo y penumbra de cava

El sistema de diseño define un modo campo con paleta de alto contraste, tipografía dos puntos mayor, botones de 64 dp, bordes de 2 dp y gradientes sustituidos por color plano porque los degradados pierden legibilidad con reflejo. Los objetivos táctiles nunca bajan de 48 dp y el teclado de pesos usa teclas de 72 a 88 dp separadas 12 mm, porque con guante de nitrilo el dedo tiene unos 14 mm de área efectiva. Toda la animación pasa por un único helper que la anula de golpe cuando el sistema pide reducir movimiento.

TUNEL-1−35,0 °C
04 / Arquitectura

Un solo paquete, tres capas

Paquete Flutter único llamado sistema_cavas, con Clean Architecture por feature en tres capas —domain, infrastructure, presentation— bajo lib/features, y un núcleo compartido en lib/core. El sistema de diseño vive entero en lib/core/design_system y no depende de ninguna feature: tokens de marca muestreados del logo, colores semánticos como ThemeExtension, escala tipográfica, escala de espaciado en múltiplos de cuatro, radios, duraciones y curvas, puntos de corte y el pintor animado del logo; ninguna pantalla declara un Color literal. El estado es Riverpod sin generación de código, con providers escritos a mano. La navegación es go_router con un ShellRoute que envuelve el cascarón adaptativo y un redirect que lee un estado de sesión síncrono y devuelve null cuando el usuario debe quedarse donde está, precisamente para no entrar en bucle. La autenticación combina el flujo de Firebase Auth con el perfil en usuarios/{uid} escuchado como stream, y el rol se persiste como cadena en mayúsculas, nunca como índice de enum, para que reordenar el enum no cambie los permisos de nadie. Firebase corre en el proyecto sistemas-de-cava en plan Spark: Auth con correo y contraseña, Firestore en modo nativo con persistencia y caché ilimitada, reglas que deniegan todo por defecto, hosting apuntando a build/web y emuladores configurados en 9099, 8080, 9199 y 4000. Lo que exige privilegios de servidor vive fuera de la aplicación, en scripts de Node ESM con firebase-admin que se ejecutan a mano desde el portátil del desarrollador: no son Cloud Functions y el propio package.json lo aclara.

Puertos de los emuladores

Auth9099
Firestore8080
Storage9199
UI4000

Proyecto sistemas-de-cava, plan Spark. Hosting apuntando a build/web.

TUNEL-1−35,0 °C
05 / Retos

Decisiones a −18 °C

Cada ficha tiene su parte fría —lo que se rompía— y su parte templada: lo que quedó escrito para que no se repita.

Lectura 01 · frío

Sin Cloud Functions no se pueden escribir custom claims, así que el rol no puede vivir en el token y las reglas se quedan sin la vía normal de autorización.

Templado · lo que se hizo

El rol se guarda en usuarios/{uid} y las reglas lo leen con get(). Como cada get() de una regla es una lectura facturable, las funciones se diseñaron para invocarse una sola vez por operación, con la cadena habilitado → perfil → rol encapsulada; y un rol desconocido se trata como «sin permisos», nunca como un valor por defecto permisivo.

Lectura 02 · frío

createUserWithEmailAndPassword desloguea al administrador que está creando la cuenta, y en Spark no hay Admin SDK en servidor para evitarlo.

Templado · lo que se hizo

Alta desde una instancia secundaria de FirebaseApp: se crea el usuario contra esa instancia, se escribe su documento de perfil y se cierra sesión solo en la secundaria, sin tocar nunca la app principal. Como respaldo y para los casos que la app no cubre quedan los scripts crear_admin.mjs y crear_usuario.mjs con firebase-admin, que corren en el portátil.

Lectura 03 · frío

El guard de navegación de un proyecto anterior entraba en bucle porque consultaba la base de datos dentro del redirect.

Templado · lo que se hizo

Se introdujo un enum EstadoSesion con cinco casos que se resuelve de forma síncrona a partir de dos streams ya escuchados, y un puente ChangeNotifier entre Riverpod y refreshListenable. El redirect devuelve null cuando el usuario debe quedarse donde está: devolver siempre una ruta es exactamente lo que produce el bucle, y así quedó anotado en el código.

Lectura 04 · frío

serverTimestamp vale nulo en la caché local mientras no hay red, de modo que ordenar o agrupar por fecha rompe la aplicación justo en la planta, que es donde no hay señal.

Templado · lo que se hizo

Las fechas de negocio se derivan en el cliente a hora de Caracas (UTC−4 fijo, sin horario de verano) y se escriben además como cadenas literales de día, mes, año y semana; se ordena por esa cadena más un identificador ULID, las reglas validan coherencia con el reloj del servidor con tolerancia de dos días, y si el desfase de reloj del dispositivo supera cinco minutos la app bloquea la captura y pide corregir la hora.

Lectura 05 · frío

El borrador del kardex marcaba el movimiento original como revertido y además lo filtraba al reconstruir saldos, con lo que el reverso se contaba dos veces y el saldo quedaba mal.

Templado · lo que se hizo

Se eliminó el campo revertido del modelo. La reconstrucción suma todos los movimientos sin filtro y el par original-reverso se anula solo; la interfaz pinta tachado el original consultando si existe un movimiento que lo apunte. El kardex y la auditoría quedaron append-only por reglas, sin excepción para ningún rol.

Lectura 06 · frío

El naranja del sol del logo, que es el color más reconocible de la marca, no alcanza contraste con texto blanco: da 2,37:1.

Templado · lo que se hizo

Se muestreó el logo píxel a píxel, se calculó cada par con la fórmula WCAG 2.1 y se construyeron dos esquemas de color completos donde el naranja solo aparece como color terciario con texto #2B1400 encima. Los colores decorativos —el teal medio de las ondas y el oliva de la colina— quedaron marcados en el código como prohibidos para fondo de texto, y el azul puro del wordmark se ajustó a #1740B3 para el rol de información.

Lectura 07 · frío

La versión más reciente de firebase_core rompía la compilación web con el SDK de Dart en uso, y la última de intl no resolvía contra flutter_localizations.

Templado · lo que se hizo

Se fijaron versiones exactas con el motivo escrito junto a cada una en pubspec.yaml: firebase_core en 4.13.0 y un límite directo sobre firebase_core_web porque la 3.11.0 llama a un método de interoperabilidad que no existe en ese Dart, e intl en 0.20.2 porque es la versión exacta que exige el SDK. Sin esa nota, el siguiente que actualice dependencias repite el fallo.

CAVA-20 a 4 °C
06 / La interfaz

Las pantallas que ya compilan

Cinco pantallas construidas en la Fase 0, recreadas aquí con los mismos colores, medidas y textos que pinta la app.

Inversiones MarSaLe

Splash — el sol sale detrás de las ondas

Inversiones MarSaLe

Sistema de cavas y procesos

Iniciar sesión

operaciones@marsale.com.ve
••••••••
Correo o contraseña incorrectos.
Entrar

Olvidé mi contraseña

RIF J-40776405-5 · Empresa Procesadora de Alimentos

Login — tarjeta blanca sobre el mar

Buenas tardes,

Andrés Marcano

Kilos en cava

Recepciones hoy

Órdenes en proceso

Por cobrar

Los indicadores se llenan a partir de la Fase 1, cuando entren las primeras recepciones.

Panel — saludo, cuatro indicadores y una advertencia honesta

Inversiones MarSaLe

AM

Andrés Marcano

Gerente de planta

Cerrar sesión

Inventario

Esta sección se construye en la Fase 2.

PanelRecepciónInventarioProcesosClientes

Cascarón adaptativo — la misma app en el teléfono de planta y en la PC de oficina

Crea tu contraseña

Entraste con una contraseña temporal. Elige una propia para continuar.

••••••••

Mínimo 8 caracteres

•••••

Las contraseñas no coinciden

Guardar y continuar

Cerrar sesión

Crea tu contraseña — el primer ingreso obligatorio

Arrastra para ver las cinco pantallas

sistemas-de-cava.web.app
Panel
Recepción
Inventario
Procesos
Clientes
Documentos
Personal
Usuarios

8 de los 10 destinos del administrador

Buenos días,

Andrés Marcano

AM

Kilos en cava

Recepciones hoy

Órdenes en proceso

Por cobrar

Los indicadores se llenan a partir de la Fase 1, cuando entren las primeras recepciones.

Cascarón adaptativo — la misma app en el teléfono de planta y en la PC de oficina

Las maquetas reproducen la interfaz real; los datos que muestran son de ejemplo.

CAVA-20 a 4 °C
07 / Identidad

De esta imagen salió cada hexadecimal

Ningún color del sistema de diseño está inventado: se muestrearon píxel a píxel del logo real de la empresa.

Sistema de Cavas
Logo original de Inversiones MarSaLe 0216, C.A. sobre fondo transparente: sol naranja saliendo tras seis ondas de mar en degradado teal, colina verde con la escena del campesino y el buey, y el wordmark «Inversiones MarSaLe 0216 C.A» con el RIF J-40776405-5. Es la imagen de la que se muestrearon píxel a píxel todos los hexadecimales del sistema de diseño.

#0E4C5A

Teal profundo

Primario · botones y acentos

#14707A

Teal medio

Secundario · decorativo

#5EC1BC

Cresta de onda

Trazo superior del logo

#1B3F4A

Onda profunda

Trazo inferior del logo

#F49021

Naranja del sol

Terciario · sólo con texto #2B1400

#FFC15E

Centro del sol

Degradado radial del disco

Regla escrita en el código

Sobre el naranja del sol el texto es #2B1400 y nunca blanco: el blanco da 2,37:1.

Anti-objetivo

Ningún tono entre 260° y 330° en toda la aplicación. El lila es el color por defecto de AppSheet, y es exactamente lo que el dueño rechaza.

Mood de marca · Frío industrial y mar profundo: azules petróleo sacados de las ondas del logo para decir «bajo control», con el naranja del sol reservado para cuando algo se sale de rango. Sobrio, legible bajo sol directo, sin un solo tono lila porque ese es el color de AppSheet y es justo lo que el cliente rechaza.

ANTESALA8 a 12 °C
08 / Funcionalidades

Kardex de lo construido y lo especificado

Append-only, como el kardex del proyecto: lo que entró en la Fase 0 y lo que ya tiene especificación esperando turno.

#MovimientoDetalle
001EntradaAcceso con correo y contraseña contra Firebase Auth, con mensajes de error traducidos a lenguaje que un operario entiende: «Correo o contraseña incorrectos», «Sin conexión, verifica el internet», «Esta cuenta está desactivada, habla con el administrador».
002EntradaEstado de sesión resuelto en un único valor síncrono con cinco casos —cargando, sin sesión, sin acceso, debe cambiar clave y activa— para que el guard del router decida sin esperar a Firestore.
003EntradaCambio de contraseña obligatorio en el primer ingreso: el administrador entrega una clave temporal y la bandera debeCambiarClave no baja hasta que el usuario elige la suya.
004EntradaPantalla explícita de «sin acceso» para la cuenta que existe en Auth pero no tiene perfil, no tiene rol reconocido o fue desactivada, con el correo en pantalla para que el administrador sepa a quién habilitar.
005EntradaOcho roles con permisos distintos: administrador, gerente de planta, administración, operador, control de calidad, recursos humanos, cliente y auditor.
006EntradaNavegación construida desde el catálogo de roles, de modo que nadie ve una pestaña que las reglas de seguridad le negarían; el operador entra directo a sus tareas y el cliente a su mercancía.
007EntradaCascarón adaptativo por ancho de pantalla: barra inferior en el teléfono de planta, rail lateral en tablet y rail extendido con etiquetas en la PC de oficina.
008EntradaPanel de indicadores con kilos en cava, recepciones del día, órdenes en proceso y cuentas por cobrar, hoy con guion en vez de cifra y con un aviso honesto de que se llenan a partir de la Fase 1.
009EntradaMarcador de sección en construcción que dice en qué fase concreta llega cada módulo, en vez de un «próximamente» sin fecha.
010EntradaLogo de la empresa dibujado con CustomPainter en vez de mostrar el JPG: escala sin pixelarse, el sol sale por detrás del horizonte y las seis ondas del mar ondulan con dos senos superpuestos para que el oleaje no se vea mecánico.
011EntradaTema claro y oscuro completos, con Material 3, colores semánticos propios expuestos como ThemeExtension y tres estados térmicos de negocio: congelado, refrigerado y fuera de rango.
012EntradaTipografía Sora para títulos e Inter para texto y cifras, con cifras tabulares obligatorias en toda columna de kilos, montos y temperaturas.
013EntradaLocalización es-VE en toda la aplicación, con formato de miles con punto y decimales con coma.
014EntradaReglas de Firestore y de Cloud Storage en modo denegar-por-defecto, con auditoría append-only que ni el administrador puede modificar.
015EntradaÍndices compuestos declarados para las consultas que ya existen: usuarios activos ordenados por nombre y auditoría por usuario y fecha.
016EntradaScripts de administración en Node ESM con firebase-admin, fuera de la aplicación, para crear el administrador y dar de alta usuarios cuando la app todavía no puede.
017ReservadoHoja de recepción fiel a la plantilla en papel, con servicios como selección múltiple, condición de la mercancía, temperatura y tabla de bruto, jaula, cesta, neto, unidades y lote; y PDF en dos copias, original para el cliente y copia para archivo.
018ReservadoInventario por lote con kardex append-only, cavas y ubicaciones, etiquetas QR de lote imprimibles y escaneables, alertas de estadía prolongada y tablero de ocupación.
019ReservadoÓrdenes de proceso con los tres estados reales de la planta, aplicación del operador con tarjetas grandes, modo guantes y háptica, captura de mermas por tipo y balance de masa con glaseo y tolerancia por producto.
020ReservadoTarifario con vigencias, devengo de almacenaje por kilo-día por tramos, nota de entrega sin IVA con doble moneda y tasa BCV congelada, IGTF configurable y apagado por defecto, abonos, estado de cuenta y antigüedad de saldos.
021ReservadoExportación de cualquier tabla a PDF con membrete y a Excel con celdas numéricas reales, más reportes de kilos por cliente, ingresos por servicio, mermas y rendimiento, ocupación de cavas y productividad por operario.
022ReservadoMódulo de recursos humanos con expediente del empleado, asistencia con origen controlado, feriados, parámetros legales versionados, motor de nómina en Dart puro y recibo en PDF.

16

funcionalidades ya construidas

6

módulos con especificación cerrada

ANTESALA8 a 12 °C
09 / Stack

Con qué está hecho

Aplicación

FlutterDart ^3.10.8Material 3cupertino_icons ^1.0.8

Estado y navegación

flutter_riverpod ^3.3.2go_router ^17.5.0

Firebase (plan Spark)

firebase_core 4.13.0 (fijada)firebase_core_web >=3.9.0 <3.11.0firebase_auth >=6.3.0 <6.7.0cloud_firestore >=6.6.0 <6.9.0 con persistencia offlineFirestore Security RulesCloud Storage RulesFirebase HostingEmuladores de Auth, Firestore, Storage y UI

Interfaz, tipografía y formato

google_fonts ^8.2.1 (Sora + Inter)flutter_animate ^4.5.2intl 0.20.2flutter_localizationsshared_preferences ^2.5.5

Herramientas y administración

firebase-admin ^13.6.0 en scripts Node ESMflutter_lints ^6.0.0flutter_test
ANTESALA8 a 12 °C
10 / Métricas

El proyecto en cifras

9

documentos de especificación técnica

14.126

líneas de especificación escritas

11

fases en el plan de ejecución

8

roles con permisos diferenciados

10

secciones de navegación por rol

14

rutas registradas en go_router

6

pantallas construidas en la Fase 0

35

colecciones y subcolecciones modeladas

3

colecciones abiertas hoy en las reglas

0

Cloud Functions: todo corre en plan gratuito

Fuera de lo esperado, y a propósito
ANTESALA8 a 12 °C
11 / El plan

Once fases, dos ya escarchadas

El tramo con escarcha es el que ya está en frío. El tubo desnudo es lo que queda por conectar.

Fase 0Construida

Paleta muestreada del logo, tema claro y oscuro verificados contra WCAG, logo animado con CustomPainter, splash, login, guard de sesión sin bucles y reglas en modo denegar-por-defecto.

Fase 0.5Construida

Acceso y roles: ocho roles con permisos distintos, cascarón adaptativo por rol, cambio de contraseña obligatorio, pantalla de «sin acceso» y scripts de alta en Node.

Fase 1Siguiente

Hoja de recepción fiel a la plantilla en papel, con servicios como selección múltiple, condición de la mercancía, temperatura y tabla de bruto, jaula, cesta, neto, unidades y lote; y PDF en dos copias, original para el cliente y copia para archivo.

En el planEspecificado

Inventario por lote con kardex append-only, cavas y ubicaciones, etiquetas QR de lote imprimibles y escaneables, alertas de estadía prolongada y tablero de ocupación.

En el planEspecificado

Órdenes de proceso con los tres estados reales de la planta, aplicación del operador con tarjetas grandes, modo guantes y háptica, captura de mermas por tipo y balance de masa con glaseo y tolerancia por producto.

En el planEspecificado

Tarifario con vigencias, devengo de almacenaje por kilo-día por tramos, nota de entrega sin IVA con doble moneda y tasa BCV congelada, IGTF configurable y apagado por defecto, abonos, estado de cuenta y antigüedad de saldos.

En el planEspecificado

Exportación de cualquier tabla a PDF con membrete y a Excel con celdas numéricas reales, más reportes de kilos por cliente, ingresos por servicio, mermas y rendimiento, ocupación de cavas y productividad por operario.

En el planEspecificado

Módulo de recursos humanos con expediente del empleado, asistencia con origen controlado, feriados, parámetros legales versionados, motor de nómina en Dart puro y recibo en PDF.

Once fases en el plan · dos construidas · nueve documentos de especificación por delante

MUELLE26 °C

El proyecto, contado entero

Sistema de gestión de cavas de congelación, procesos de planta y nómina para Inversiones MarSaLe 0216, C.A. (RIF J-40776405-5), una procesadora de alimentos que recibe pescado y mariscos, los almacena en cámaras frigoríficas, los procesa y los factura por kilo y por día de estadía.

Sustituye a una aplicación hecha en AppSheet que nunca llegó a emitir el PDF de la nota de recepción ni la nota de entrega con la dirección completa del cliente: las dos cosas por las que el dueño pagó y que el desarrollador anterior no logró resolver.

El proyecto arranca con nueve documentos de especificación ejecutable —más de catorce mil líneas— que cubren seguridad y roles, modelo de datos Firestore, núcleo operativo, comercial y cobranza, RRHH y nómina venezolana, documentos y exportación, sistema de diseño, arquitectura offline-first y módulos adicionales. Cada uno fue revisado de forma adversarial contra su propio borrador y las correcciones están anotadas con su motivo.

La restricción que define toda la arquitectura es el plan Spark de Firebase: sin Cloud Functions, sin Admin SDK en servidor, sin custom claims en tiempo de ejecución, sin cron y sin FCM enviado desde servidor. Todo lo que en otro proyecto sería un trigger aquí se resuelve con reglas de seguridad, transacciones, cómputo en el cliente o scripts de Node que corren a mano en el portátil.

Lo que hoy está construido y compila es la Fase 0: paleta muestreada del logo real, tema claro y oscuro verificado contra WCAG, logo dibujado y animado con CustomPainter, splash, login, guard de sesión sin bucles, cascarón de navegación adaptativo por rol y panel con indicadores todavía vacíos, más las reglas de Firestore y Storage en modo denegar-por-defecto.

CAVA-1 · −18,0 °CTUNEL-1 · −35,0 °CKardex append-onlyGramos enterosCentavos enterosTasa BCV a 8 decimalesModo campoULIDes-VESin Cloud FunctionsCAVA-1 · −18,0 °CTUNEL-1 · −35,0 °CKardex append-onlyGramos enterosCentavos enterosTasa BCV a 8 decimalesModo campoULIDes-VESin Cloud Functions

Puerta cerrándose

La cava queda a −18,0 °C y el sistema sigue funcionando sin señal.

Sistema de Cavas

¿Quieres algo así para tu producto?

Un ERP de cadena de frío que emite documentos sin red, cuadra kilos al gramo y cabe entero en el plan gratuito de Firebase. Si tienes una operación con reglas propias —almacenaje, procesos, nómina venezolana— este es exactamente el terreno que conozco.