La caja ya puede abrir
Racoonity estrena Caja y POS: abrir sesión, buscar productos, preparar carrito, cliente y pago, dejando la confirmación de la venta para la siguiente iteración.
La caja ya puede abrir
Hasta ahora, el Segundo MVP de Racoonity había ido convirtiendo una máquina funcional en algo cada vez más parecido a un producto.
Primero apareció una puerta de entrada.
Después, una cara.
Luego llegaron las superficies para administrar el negocio y, más tarde, una forma de ver y mover la mercancía sin hablar directamente con una API.
La siguiente pieza era inevitable.
Había que llegar a la caja.
Pero abrir una caja no significa todavía vender.
Y esa diferencia terminó siendo una de las decisiones más importantes de esta iteración.
Una venta empieza mucho antes de confirmarse
Desde fuera, un punto de venta puede parecer sencillo:
buscar producto
→ agregarlo
→ cobrar
Por dentro, incluso una experiencia pequeña empieza a acumular decisiones muy rápido.
¿Hay una sesión de caja abierta?
¿Ese producto sigue disponible?
¿El precio que estamos mostrando es realmente el precio que debe utilizar la venta?
¿Qué pasa si el mismo producto se escanea dos veces?
¿La cantidad es válida?
¿Hay un cliente asociado?
¿El método de pago sigue disponible?
¿Y qué ocurre si algo falla después de que el usuario ya armó medio carrito?
En I4 — La caja ya puede abrir, decidimos no intentar resolver toda la venta de una sola vez.
La meta fue más precisa:
Racoonity debe poder construir una intención de venta completa sin materializarla todavía.
Eso nos permitió trabajar el POS como experiencia de producto sin adelantar el momento más delicado de la operación comercial.
Primero, la caja
Antes de entrar al punto de venta, Racoonity necesita saber que existe una sesión de caja válida.
La nueva superficie de Caja permite que una persona autorizada vea el estado de su sesión y pueda abrirla cuando corresponda.
Ese estado no se inventa en el frontend.
Racoonity pregunta al backend cuál es el registro de la sucursal, si existe una sesión propia y si la apertura puede realizarse.
Si otra persona ya ocupa la caja, la operación se rechaza sin revelar información que el usuario no necesita conocer.
La interfaz explica lo ocurrido.
El backend conserva la autoridad.
Parece una distinción pequeña, pero es la clase de frontera que queremos conservar en todo Racoonity:
la interfaz guía; el dominio decide.
Y deliberadamente nos detuvimos ahí.
Cerrar caja, forzar cierres, arqueos y movimientos de efectivo no pertenecen todavía a esta iteración.
Después, el POS
Con una sesión abierta, el punto de venta puede pasar de un estado bloqueado a un estado operativo.
Ahí aparece el recorrido principal:
buscar o escanear producto
→ añadirlo al carrito
→ ajustar cantidades
→ elegir o crear cliente
→ seleccionar método de pago
→ venta preparada
La palabra importante es preparada.
No confirmada.
No cobrada.
No persistida como venta.
Preparada.
El carrito vive como estado de experiencia y puede cambiar mientras el usuario trabaja. Racoonity todavía no crea una venta en el backend solamente porque alguien añadió un producto a la pantalla.
Ese límite nos deja una regla muy clara:
El POS construye la intención de vender; la confirmación decide si esa intención se convierte en un hecho comercial.
La segunda mitad llegará en I5.
Buscar no significa confiar
Una de las decisiones más interesantes apareció con los productos.
La búsqueda debe ser rápida y útil, pero un resultado de búsqueda no debería convertirse automáticamente en autoridad comercial.
Entre el momento en que un producto aparece en una lista y el momento en que el usuario lo añade al carrito pueden haber cambiado cosas.
Por eso Racoonity utiliza la búsqueda para descubrir y, al añadir, vuelve a resolver el producto mediante su contrato vendible.
También separamos explícitamente la representación de dinero de la autoridad del dinero.
La lista puede mostrar información útil para descubrir productos, pero los importes que forman parte del carrito se construyen desde valores exactos y se manejan como centavos enteros.
Nada de dejar que un float decida cuánto debe pagar alguien.
Puede parecer excesivo para una caja que todavía ni cobra.
Precisamente por eso queríamos resolverlo ahora.
El scanner más simple posible
Otra tentación era convertir el escaneo de códigos de barras en un pequeño proyecto de integración de hardware.
No hizo falta.
Para esta etapa, un scanner USB que se comporte como teclado ya resuelve el problema correctamente:
scanner
→ escribe código
→ Enter
→ Racoonity busca
El mismo campo permite búsqueda manual y lectura por código.
Sin integración HID.
Sin puerto serial.
Sin captura global del teclado.
Sin inventar una abstracción de hardware que todavía no necesitamos.
Simple por defecto también significa saber cuándo no construir algo.
El carrito no desaparece porque algo salga mal
Una experiencia operativa se siente frágil cuando cualquier error obliga a empezar desde cero.
Por eso uno de los objetivos de I4 fue que los errores locales no destruyeran el trabajo del usuario.
Un código desconocido no vacía el carrito.
Un error al buscar productos no elimina lo que ya estaba preparado.
Un email inválido al crear un cliente mantiene tanto el formulario como la venta en construcción.
Si desaparece temporalmente una condición necesaria para operar, el POS puede bloquearse sin destruir el borrador.
La diferencia parece visual.
En realidad es una decisión sobre dónde vive cada tipo de estado.
El servidor conserva la verdad del negocio.
El POS conserva la intención que la persona todavía está construyendo.
Cliente sin abandonar la venta
También queríamos evitar uno de esos recorridos clásicos donde el usuario está cobrando, descubre que necesita registrar un cliente y termina viajando por media aplicación.
Ahora puede buscarlo y seleccionarlo desde el POS.
Si no existe, puede crear uno mediante una experiencia compacta y volver exactamente al punto donde estaba.
El carrito sigue ahí.
La selección queda hecha.
Y el flujo continúa.
No es una gran pieza arquitectónica.
Es una pequeña señal de algo que queremos que Racoonity haga cada vez mejor:
adaptarse al trabajo de la persona, en vez de obligar a la persona a adaptarse a la estructura interna del sistema.
Una caja también se usa con teclado
Un POS no debería depender de perseguir botones con el mouse.
Durante esta iteración tratamos el teclado como parte del journey, no como un detalle de accesibilidad añadido al final.
Buscar.
Moverse entre resultados.
Añadir.
Cambiar cantidad.
Abrir cliente.
Preparar pago.
Elegir una opción.
Volver al flujo.
Todo eso puede recorrerse sin mouse.
También cuidamos foco, diálogos, estados anunciables y una superficie usable en un escritorio compacto.
No estamos afirmando paridad móvil.
Todavía no es el objetivo.
Estamos afirmando que la experiencia que sí prometimos puede operarse de forma coherente.
Lo importante es lo que no ocurrió
Una de las pruebas más valiosas de esta iteración no demuestra algo que Racoonity hizo.
Demuestra algo que no hizo.
Al terminar un recorrido completo hasta Venta preparada comprobamos que:
ventas creadas 0
pagos creados 0
eventos de venta 0
movimientos de stock 0
El inventario quedó exactamente igual.
Eso puede parecer extraño en una entrada sobre un punto de venta.
Para nosotros es una buena noticia.
Significa que la frontera entre preparar y confirmar sigue existiendo de verdad y no solamente en el diseño.
La siguiente iteración podrá concentrarse en el momento peligroso:
el instante en que una intención del usuario se convierte en dinero, inventario, historial y hechos que ya no podemos fingir que no ocurrieron.
Probar el recorrido, no solo las piezas
También seguimos endureciendo una idea que ha acompañado al Segundo MVP desde el principio:
una pantalla no está terminada porque renderiza.
Un endpoint no está terminado porque responde.
Para cerrar I4 ejecutamos el recorrido completo sobre una infraestructura real:
PostgreSQL
→ migraciones
→ API
→ aplicación
→ navegador
→ usuario
Abrimos la caja.
Entramos al POS.
Buscamos y escaneamos productos.
Modificamos cantidades.
Provocamos errores.
Creamos un cliente.
Preparamos el pago.
Probamos conflictos entre usuarios.
Probamos permisos.
Y comprobamos que, al final, no existía ninguna venta escondida detrás de todo aquello.
Ese recorrido se repitió varias veces sin retries.
Las iteraciones anteriores también volvieron a pasar.
La intención no es acumular números de tests como trofeos.
Es poder decir que la nueva pieza se integró sin invalidar lo que la ciudad ya sabía hacer.
La ciudad ya tiene una caja
I4 no termina con Racoonity vendiendo.
Termina justo antes.
Ahora existe una superficie de Caja.
Existe una sesión operativa.
Existe un POS capaz de buscar y escanear productos.
Existe un carrito con dinero exacto.
Existe selección y creación de clientes.
Existe preparación de pago.
Existe navegación, autorización y comportamiento por teclado.
Y existe, sobre todo, una frontera clara que impide confundir una intención con una venta real.
Eso era lo que necesitábamos antes de dar el siguiente paso.
Porque la siguiente iteración ya no podrá esconderse detrás de un borrador.
En I5 — La tienda ya puede vender de verdad, confirmar significará algo.
Habrá que convertir una intención en un hecho comercial sin duplicarla, sin perderla a medias y sin dejar al usuario preguntándose si cobró o no cobró.
Pero eso es problema del siguiente capítulo.
Por ahora:
La caja ya puede abrir.