La mercancía ya se puede mover
Racoonity ya puede consultar existencias, registrar entradas y salidas, ajustar inventario, transferir mercancía y explicar qué pasó sin convertir la interfaz en una calculadora optimista.
La mercancía ya se puede mover
Hasta ahora, el Segundo MVP de Racoonity había ido construyendo algo que empezaba a parecerse a un producto.
Primero apareció una puerta de entrada.
Después, la ciudad obtuvo un rostro.
Luego llegaron las superficies administrativas necesarias para preparar usuarios, clientes y ubicaciones.
Pero todavía faltaba algo bastante básico para cualquier sistema que pretenda operar un negocio físico:
la mercancía tenía que poder moverse.
Y no solamente en el backend.
Eso ya existía desde antes.
El problema real era otro: una persona debía poder abrir Racoonity, consultar cuánto stock existe, entender dónde está, registrar una entrada o una salida, corregir una cantidad, mover mercancía entre ubicaciones y revisar después qué ocurrió.
Sin abrir Postman.
Sin conocer una Action.
Sin tener que adivinar si la cifra que aparece en pantalla sigue siendo cierta.
Con I3, esa parte del producto ya existe.
Inventario no es una tabla con un número
Una primera versión ingenua de inventario podría parecer sencilla:
Producto A — 15 unidades
Producto B — 8 unidades
Producto C — 0 unidades
Pero una cifra aislada cuenta muy poco.
En una operación real aparecen preguntas inmediatamente:
- ¿en qué ubicación están esas quince unidades?
- ¿por qué ayer había veinte?
- ¿quién hizo el movimiento?
- ¿fue una entrada, una salida o una corrección?
- ¿las cantidades cambiaron mientras yo tenía el formulario abierto?
- ¿una transferencia modificó las dos ubicaciones o solo una parte?
- ¿qué ocurre si dos personas intentan consumir el mismo stock al mismo tiempo?
Por eso I3 no se planteó como “hacer una pantalla de existencias”.
La meta fue convertir Inventory en una experiencia completa:
consultar existencias
→ registrar inventario
→ entrada / salida
→ ajustar
→ transferir
→ consultar movimientos
La interfaz debía explicar la operación.
El backend debía seguir decidiendo la verdad.
La pantalla ayuda; el backend manda
Una de las reglas que quisimos conservar durante toda la iteración fue bastante simple:
Lo que la interfaz vio hace un segundo puede haber dejado de ser cierto.
Racoonity puede mostrar que existen diez unidades.
Puede avisar que intentar retirar veinte probablemente terminará mal.
Puede presentar un resumen antes de confirmar.
Pero la interfaz no tiene autoridad para decretar que el stock cambió.
Una operación solo termina cuando el backend la acepta y devuelve el nuevo estado.
Esto evita una clase de experiencia muy tentadora: actualizar números localmente para que todo “se sienta rápido” y corregir después si algo falla.
Para inventario preferimos otra regla:
usuario decide
→ frontend prepara
→ backend valida
→ backend ejecuta
→ frontend presenta el resultado real
Puede parecer menos espectacular que ver un contador cambiar instantáneamente, pero resulta mucho más importante cuando ese contador representa mercancía real.
El caso incómodo: alguien cambió el inventario mientras tú lo mirabas
La parte más interesante de esta iteración apareció con los ajustes.
Imaginemos este escenario:
Existencia actual: 10
Una persona abre “Ajustar existencias” y decide que la cantidad correcta debería ser 6.
Mientras revisa el resumen, otra operación registra una entrada de 5 unidades.
La realidad ahora es:
15 unidades
pero la primera pantalla todavía fue preparada cuando existían 10.
Aceptar ciegamente aquel ajuste significaría sobrescribir una operación legítima que ocurrió después.
Racoonity no lo hace.
El backend rechaza el ajuste como obsoleto y la interfaz explica qué cambió:
Habías visto: 10
Ahora hay: 15
Objetivo: 6
El formulario permanece ahí.
El objetivo no desaparece.
Pero el usuario debe revisar nuevamente la operación y confirmarla de forma explícita.
No hay reintento automático.
No hay magia silenciosa.
La decisión se vuelve consciente otra vez.
Durante las pruebas de cierre incluso reprodujimos este caso con dos páginas reales abiertas al mismo tiempo: una preparó el ajuste y la otra registró una entrada antes de que la primera confirmara.
Eso nos interesaba mucho más que simular un error artificialmente, porque la concurrencia deja de ser una idea teórica cuando dos ventanas pueden observar estados distintos del mismo negocio.
Transferir significa transferir todo o no transferir nada
Mover mercancía entre ubicaciones parece otra operación sencilla:
Bodega A: -4
Bodega B: +4
Pero contiene una condición bastante importante:
las dos cosas forman una sola operación.
No existe un estado aceptable donde el origen pierda mercancía pero el destino nunca la reciba.
Tampoco donde aparezca la entrada en destino sin haberse descontado el origen.
Por eso la transferencia se mantiene atómica.
En las pruebas de integración provocamos fallos deliberadamente en distintos puntos de la persistencia y comprobamos que el resultado seguía siendo el mismo:
si la transferencia no termina
→ nada de la transferencia queda aplicado
Después, el mismo flujo se recorrió desde la interfaz:
Origen: 6 → 2
Destino: 0 → 4
Una operación.
Dos balances finales.
Dos movimientos relacionados.
Una sola verdad.
Los movimientos cuentan la historia
Las existencias responden una pregunta:
¿Cuánto hay ahora?
Los movimientos responden otra:
¿Cómo llegamos hasta aquí?
I3 añadió una superficie donde puede observarse el historial de inventario con contexto comprensible: producto, ubicación, actor, tipo de movimiento, cantidad y relación con una transferencia cuando existe.
En el journey final pudimos ver una secuencia como esta:
Entrada +10
Entrada +5
Ajuste -9
Transferencia de salida -4
Transferencia de entrada +4
Y desde los movimientos de transferencia puede abrirse el detalle de la operación completa.
No estamos intentando convertir este historial en una herramienta de auditoría universal todavía.
Su trabajo, por ahora, es mucho más concreto: permitir que una persona entienda qué cambió en su inventario sin tener que mirar registros técnicos o consultar la base de datos.
Los permisos también forman parte del producto
No todas las personas necesitan poder mover mercancía.
Racoonity ya venía abandonando la idea de decidir la interfaz mediante preguntas como:
¿es administrador?
¿es cashier?
En su lugar, la navegación y las acciones se derivan de capacidades concretas.
Eso permitió comprobar comportamientos diferentes sin escribir una interfaz especial para cada rol.
Por ejemplo, una persona con acceso únicamente a existencias puede ver:
Inventario
└── Existencias
Una persona con permisos de lectura puede consultar existencias y movimientos sin encontrar botones de mutación.
Y alguien con todas las capacidades obtiene el conjunto completo de operaciones.
Durante esta iteración apareció además un bug interesante: Atlas ya enviaba correctamente la relación entre grupos y elementos de navegación, pero el frontend estaba ignorando esa jerarquía y pintando la lista como si todo estuviera al mismo nivel.
El resultado era una navegación intercalada bastante fea.
La solución no fue agregar una excepción para Inventory.
La solución fue hacer que el frontend respetara la relación padre-hijo que ya existía en la metadata.
Eso también significa que futuros grupos pueden integrarse sin obligar al Shell a conocerlos por nombre.
También queríamos saber qué ocurrió cuando algo sale mal
La observabilidad completa de Racoonity llegará más adelante, pero no queríamos esperar hasta entonces para comenzar a producir evidencia útil.
Las cuatro operaciones críticas de inventario ahora dejan evidencia estructurada de ejecución:
inventory.create_entry
inventory.create_exit
inventory.adjust
inventory.transfer
Esa evidencia puede indicar, entre otras cosas:
- qué operación ocurrió;
- si terminó con éxito o rechazo;
- qué código de error intervino;
- cuánto tardó;
- qué actor participó;
- cuál fue su correlation ID;
- qué ocurrió con la transacción.
Y algo igual de importante: no necesita guardar el payload completo para hacerlo.
Durante las pruebas verificamos que motivos, referencias, claves de idempotencia, tokens y contraseñas no terminaran filtrándose en esos registros operativos.
Observabilidad no debería convertirse en otra forma de copiar datos sensibles por todas partes.
Una iteración no termina cuando “ya se ve”
Cerrar I3 requirió algo más que navegar las nuevas pantallas.
El cierre terminó demostrando, entre otras cosas:
- 41 requirements cubiertos;
- 145 escenarios cubiertos;
- 131 tareas ejecutadas;
- pruebas completas de frontend;
- regresión completa de backend;
- PostgreSQL real;
- migraciones desde una base vacía;
- rollback de la migración de navegación;
- journeys E2E acumulados de I0, I1, I2 e I3;
- concurrencia real;
- rollback real de transferencias;
- fronteras arquitectónicas;
- accesibilidad y comportamiento a 1024 px;
- cero cambios en Painter;
- cero P0 y cero P1 al cierre.
Después de consolidar la evidencia, el change productize-inventory-operations fue archivado y el grafo de arquitectura se actualizó sobre el estado final.
Eso importa porque una de las reglas del Segundo MVP es precisamente no cerrar una iteración con:
“la pantalla se ve”.
Ni con:
“el endpoint responde”.
La capacidad tiene que funcionar como producto y seguir respetando la máquina debajo.
Un pequeño hito de producto
I0 permitió entrar a Racoonity.
I1 permitió trabajar con el catálogo.
I2 permitió preparar usuarios, clientes y ubicaciones.
Con I3 ya existe una base administrativa bastante más reconocible:
crear productos
→ preparar ubicaciones
→ registrar mercancía
→ conocer existencias
→ mover inventario
→ entender sus movimientos
Todavía no estamos vendiendo.
Eso viene después.
Pero ya no estamos mirando un conjunto de piezas aisladas esperando convertirse algún día en una aplicación.
La operación cotidiana empieza a tomar forma.
Y eso desbloquea el siguiente paso natural del Segundo MVP:
I4 — La caja ya puede abrir.
Antes de vender, Racoonity necesita saber desde qué caja se está operando, quién la abrió y cuál es su contexto.
Pero esa es la siguiente historia.
Por ahora, hay algo nuevo que ya podemos decir sobre la ciudad:
la mercancía ya se puede mover.