Para qué sirve y para quién
Menús y combos guarda los artículos compuestos que se venden a precio cerrado: el menú del día («primero + segundo + postre + bebida por 13,50 €»), el menú infantil o el «bocadillo + bebida» para llevar. Cada menú tiene platos (Primero, Segundo, Bebida…), cada uno con cuántas elecciones pide como mínimo y como máximo, y cada plato ofrece elecciones: productos reales del catálogo de Inventario, con un suplemento si sustituir cuesta más (o menos).
Quién la usa:
- El propietario, el administrador y el responsable montan los menús: precio, cómo se vende, platos, elecciones, suplementos, orden y qué está agotado hoy.
- El empleado los consulta, y en el TPV los vende (la hoja del TPV es de Ventas).
- El asistente, por voz o por texto y con la sesión de quien habla: «crea el menú del día a 13,50 para tomar en el local», «añade al menú del día un plato Primeros», «pon las lentejas en Primeros», «la merluza lleva 2 euros de suplemento», «hoy no hay flan».
Este módulo guarda el menú y dice lo que vale y cómo se factura; Ventas lo ofrece en el TPV y lo cobra, Inventario baja el stock de cada plato y Cocina recibe cada plato en su estación. No es un descuento ni una variante, ni los extras de un plato (eso es Suplementos).
Referencia adoptada
El barrido de mercado del producto actual (architecture/modules/combos.md, 24/08/2026: 10
referencias y sus foros) sigue vigente y se adopta, no más:
- Square — combos: el combo es un artículo del catálogo, con «elecciones requeridas» por grupo y un cargo por opción; un artículo agotado no se ofrece.
- Lightspeed K-Series — combos: grupos con cuántos artículos hay que elegir, a precio fijo; se añaden varios artículos de golpe.
- Odoo — combos: precio extra por opción y el precio cerrado repartido en una línea por componente, en proporción a su precio de catálogo (foro: cómo calcula el reparto).
- Shopify Bundles, WooCommerce Product Bundles, NetSuite (kit), Holded (pack): el stock lo mueven los componentes, nunca el menú.
- Toast y Clover lo hacen como un descuento, y por eso no se siguen: un descuento no se manda a cocina, no se desglosa ni mueve stock.
- Ley del IVA: art. 91.Uno.2.2º (el menú servido en el local es una prestación única de restauración al 10 %, la bebida incluida, también la alcohólica) y art. 79.Dos (el pack de bienes a precio único reparte la base en proporción al valor de mercado de cada bien, que HMRC concreta como el precio de venta individual: VATVAL03800).
- Confirmar antes de borrar (Square, Odoo, Shopify): un menú, un plato o una elección se quitan con una pregunta antes.
Antes de empezar
- Menús necesita Inventario (sus elecciones son productos del catálogo) e Impuestos (el tipo de IVA de un menú «en el local»). Al activar Menús se activan los dos.
- Solo un producto que se vende y se cobra por unidades puede ser una elección (un menú lleva «una ración», no «un kilo» ni «el precio de 100»); los buscadores solo ofrecen esos. Un producto desactivado o eliminado en Inventario no se puede añadir.
- El precio cerrado y los suplementos son importes en euros, escritos con coma o punto.
Configuración inicial, paso a paso:
- Abre Menús y pulsa + (COMBO-F02): nombre, precio cerrado, cómo se vende y, si es «en el local», el tipo de IVA.
- Toca el menú para abrir su constructor y añade sus platos con el mínimo y el máximo de cada uno (COMBO-F05).
- En cada plato, añade las elecciones, con su suplemento si lo hay (COMBO-F06).
- Mira el aviso de arriba del constructor: dice si el menú ya sale en el TPV o qué le falta.
Pantallas
Menús
Menú Menús. Tabla de menús con el buscador «Buscar un menú…» (busca por nombre y por nombre de cocina), vista de tabla o de tarjetas y columnas: Nombre (se ordena y se filtra), «Precio cerrado» (se ordena), «Cómo se vende» («En el local» o «Para llevar»; se ordena y se filtra), «A la venta» (Sí o No; se ordena y se filtra) y Orden (se ordena). Páginas de 50, por orden y nombre. Con permiso de gestión hay botón + y, por fila, «Editar datos» y «Retirar»; sin él, solo se ve la lista. Tocar una fila abre el constructor.
El formulario de alta y de edición (título «Menú nuevo» o «Menú · {nombre}») lleva: Nombre, «Nombre de cocina» («Déjalo vacío y la comanda usa el nombre comercial.»), «Precio cerrado» («Lo que paga el cliente por el menú entero, elija lo que elija.»), «¿Cómo se vende?» («Se consume en el local (servicio de restauración)» o «Se vende como producto / para llevar», con la explicación de las dos siempre a la vista y la elegida resaltada), «Tipo de IVA» (solo si es «en el local»), «Orden en la lista de menús» y la casilla «A la venta» (marcada de partida), con «Guardar» y «Cancelar».
Constructor del menú
Se abre al tocar un menú (/combo/menus/<id>). Arriba, la flecha atrás («Todos los menús»), el nombre
y el precio del menú, y un aviso con lo que hará ese menú en el tique: «Este menú se factura como un
único servicio: una sola línea a su tipo de IVA, tributen como tributen sus componentes.» o «Este menú
se factura como entrega de bienes: su precio se repartirá entre los componentes que añadas, una línea
por cada uno.». Debajo, si el menú todavía no sale en el TPV, otro aviso dice por qué («Todavía no
sale en el TPV: …»).
Después, la sección «Platos» («Un plato es un paso de ESTE menú: Primero, Segundo, Postre, Bebida. Su orden es el orden en que se pregunta.») y una tarjeta por plato, en orden: nombre, su regla («Obligatorio · elige 1 · hasta 1 · sin repetir») y, con permiso de gestión, el asa de arrastre, «Subir», «Bajar», «Editar datos» y «Retirar». Dentro, «Elecciones»: una fila por producto con su suplemento, una marca «Agotado» si no está disponible hoy y, si el producto ya no se puede vender, «Desactivado en Inventario», «Ya no está en el catálogo», «No se vende por unidades en Inventario» o, en un menú «para llevar», «Sin tipo de IVA para llevar»; por fila, el asa, subir, bajar, «Agotado / Disponible», editar y quitar. Debajo, el buscador «Busca un producto…», «Suplemento», «Añadir elección» y «Añadir varios» (ventana con buscador y casillas: «Añadir N seleccionados»; lo que ya está en el plato sale marcado «Ya está en este plato»). Al final, el formulario «Añadir un plato» (Nombre del plato, «Elecciones mínimas», «Elecciones máximas», la casilla «Se puede elegir la misma opción más de una vez» y «Guardar»), que pasa a «Plato · {nombre}» al editar uno.
Estados de cada pantalla
| Estado | Qué ve la persona |
|---|---|
| Cargando | «Cargando…» en el hueco de la tabla; «Cargando los platos…» en el constructor. |
| Vacío | «Aún no hay menús. Crea el primero con el botón +.»; con búsqueda o filtro, «Sin resultados». En el constructor, «Aún no hay platos. Añade el primero: un menú sin platos no tiene nada que elegir.» y, en un plato, «Aún no hay elecciones: nadie puede resolver este plato.». |
| Error | El aviso de la tabla con «Reintentar»; «No se han podido cargar los platos de este menú.» en el constructor. El rechazo de un formulario sale dentro del formulario, sin perder lo tecleado; el de una elección, dentro de su plato. |
| Reconectando | Lo pintado se queda; una franja avisa «Reconectando…». |
| Ya no existe | Si el menú se retira mientras su constructor está abierto (aquí o en otro dispositivo), el constructor se vacía y dice «Este menú ya no existe» («Se ha retirado, quizá desde otro dispositivo.»), con «Todos los menús» para volver. |
| App inactiva | «Esta app no está activa en este negocio». |
| Sin permiso | No aparece lo que no puede hacer; si llega por otra puerta, «No tienes permiso». |
Flujos
| ID | Flujo | Estado |
|---|---|---|
| COMBO-F01 | Consultar y buscar los menús | hecho |
| COMBO-F02 | Crear un menú a precio cerrado | hecho |
| COMBO-F03 | Cambiar los datos de un menú, ponerlo o quitarlo de la venta | hecho |
| COMBO-F04 | Retirar un menú | hecho |
| COMBO-F05 | Montar los platos de un menú | hecho |
| COMBO-F06 | Elegir los productos de cada plato y su suplemento | hecho |
| COMBO-F07 | Elegir un menú al vender | servidor hecho; la hoja del TPV es de Ventas (pedido a Ventas) |
| COMBO-F08 | Cobrar el menú: precio cerrado y suplementos | servidor hecho; lo llama Ventas al cobrar (pedido a Ventas) |
| COMBO-F09 | Repartir el IVA de un menú | servidor hecho; lo aplica Ventas en sus líneas (pedido a Ventas) |
| COMBO-F10 | Bajar el stock de cada plato al cobrar | contrato probado con Inventario real; el aviso lo manda Ventas (pedido a Ventas) |
| COMBO-F11 | Mandar el menú a cocina | contrato hecho; la comanda la arma Ventas y la pinta Cocina (pedido a Ventas y a Cocina) |
| COMBO-F12 | Marcar una elección como agotada | hecho |
| COMBO-F13 | Cuando un producto deja el catálogo | hecho — la reacción está declarada; la entrega entre apps la hace el relay del outbox (fase 2); mientras, la elección se ve y se trata como huérfana al leerla |
COMBO-F01 Consultar y buscar los menús
Estado: hecho Actor: propietario, administrador, responsable, empleado, asistente Pantalla: Menús Pasos: 1. Abre Menús. 2. Lee la tabla: nombre, precio cerrado, cómo se vende, si está a la venta y orden. 3. Para encontrar uno, escribe en «Buscar un menú…», ordena por una columna o filtra por nombre, por cómo se vende o por «A la venta». 4. Toca una fila para abrir su constructor. Entra: los menús del negocio (permiso de consulta). Sale: nada; solo lee. Si falla: el error de carga sale en la tabla con «Reintentar»; sin menús, «Aún no hay menús. Crea el primero con el botón +.». Implicados: ninguno
COMBO-F02 Crear un menú a precio cerrado
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Menús Pasos: 1. En Menús, pulsa +. 2. Escribe el Nombre; si la comanda debe decir otra cosa, el «Nombre de cocina». 3. Escribe el «Precio cerrado»: lo que paga el cliente por el menú entero. Vacío es 0. 4. Elige «¿Cómo se vende?»: «Se consume en el local» factura UNA línea al tipo del menú, la bebida incluida (el menú del día); «Se vende como producto / para llevar»: si todas las elecciones tributan igual, UNA línea a ese tipo; si no, UNA línea por plato, cada una a su tipo, con el precio repartido (COMBO-F09). 5. Si es «en el local», elige el «Tipo de IVA» del menú entero (en un restaurante, «Restaurante — comida», al 10 %). 6. Deja marcada «A la venta» si ya se puede vender y pulsa «Guardar». 7. El formulario se cierra y el menú aparece en la lista. Todavía no sale en el TPV: le faltan los platos y las elecciones (COMBO-F05, F06). Entra: nombre, nombre de cocina, precio, forma de venta, tipo de IVA, orden y «A la venta»; las categorías de IVA las lee de Impuestos. Sale: el menú (avisa: menú nuevo). Si falla: junto al campo: «El menú necesita un nombre.», «Un menú que se consume en el local necesita su propio tipo de IVA: sin él no se puede facturar.», «El precio de un menú no puede ser negativo. Para abaratar una opción, ponle un suplemento negativo.», «Esto no es un importe. Escribe una cifra, por ejemplo 12,50.» y el de las dos lecturas posibles de un importe («1.250»), que cita lo escrito. Un tipo de IVA que no existe en Impuestos: «Ese tipo de IVA no existe en Impuestos.». Sin permiso de gestión no hay botón +. Implicados: TAX-F01
COMBO-F03 Cambiar los datos de un menú, ponerlo o quitarlo de la venta
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Menús Pasos: 1. En la fila del menú, pulsa «Editar datos». 2. Cambia lo que haga falta: nombre, precio, forma de venta, tipo de IVA, orden o «A la venta». 3. Pulsa «Guardar». La lista muestra el cambio al momento. 4. Un menú con «A la venta» desmarcada sigue en la lista, pero el TPV deja de ofrecerlo. Entra: los mismos campos que el alta; solo cambia lo que se envía (por el asistente o la API, lo que no se nombra se queda como estaba). Sale: el menú cambiado (avisa: menú cambiado). Lo ya cobrado no cambia: cada línea de venta lleva su copia del menú. En una cuenta abierta, Ventas conserva el importe pedido; la forma de venta, el tipo de IVA y si el menú sigue a la venta se leen al cobrar (COMBO-F08). Si falla: los mismos avisos que el alta. Pasar a «en el local» sin tipo de IVA se rechaza. Implicados: SALE-F12
COMBO-F04 Retirar un menú
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Menús Pasos: 1. En la fila del menú, pulsa «Retirar». 2. Confirma en «¿Retirar este menú?» («{nombre}» deja de estar a la venta, junto con sus platos y sus elecciones. Los tiques ya emitidos se siguen leyendo.). 3. El menú desaparece de la lista, con sus platos y sus elecciones, y el TPV deja de ofrecerlo. Quien lo tenga abierto en su constructor ve «Este menú ya no existe». Entra: el menú elegido (por id o por nombre). Sale: el menú, sus platos y sus elecciones borrados en una sola operación (avisa: menú retirado, con su nombre). Los tiques emitidos llevan su copia y se siguen leyendo. Si falla: «No se ha podido retirar el menú.» en el diálogo; un menú que ya no existe, «Ese menú no existe en este negocio.». Una cuenta abierta que lo llevaba se rechaza al cobrar (COMBO-F08). Implicados: SALE-F12
COMBO-F05 Montar los platos de un menú
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Constructor del menú Pasos: 1. Abre el menú (toca su fila) y baja a «Añadir un plato». 2. Escribe el «Nombre del plato» (Primeros, Segundos, Postre, Bebida), las «Elecciones mínimas» (1 o más hace el plato obligatorio; 0, opcional) y las «Elecciones máximas» (0 es sin límite). De partida salen 1 y 1. 3. Marca «Se puede elegir la misma opción más de una vez» solo si hace falta (dos cafés en el mismo menú). 4. Pulsa «Guardar»: el plato entra el último, que es el último que se pregunta. 5. Para cambiar el orden, arrastra el asa o usa «Subir» y «Bajar»: el orden nuevo se guarda entero de una vez. «Editar datos» abre el plato en el formulario y conserva su sitio. «Retirar» lo quita con sus elecciones, tras confirmarlo. Entra: nombre, mínimo, máximo y repetición. Sale: el plato (avisa: plato nuevo, cambiado, reordenado o retirado). Si falla: junto al campo: «El plato necesita un nombre.», «El máximo ({max}) queda por debajo del mínimo ({min}): nadie podría satisfacer nunca este plato.» y «Ese menú ya tiene un plato «{nombre}».». Un mínimo o un máximo por encima de 50 se rechaza, igual que más de 20 platos en un menú («Un menú admite como mucho 20 platos.»). Si el servidor rechaza, el formulario lo dice y no pierde lo escrito. Implicados: ninguno
COMBO-F06 Elegir los productos de cada plato y su suplemento
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Constructor del menú Pasos: 1. En la tarjeta del plato, escribe en «Busca un producto…» y elige el producto del catálogo de Inventario (busca por nombre y por SKU). 2. Escribe el «Suplemento» si sustituir cuesta más (o menos, con signo negativo) que el precio cerrado; vacío es sin suplemento. 3. Pulsa «Añadir elección»: entra la última. Para varios de golpe, «Añadir varios»: marca las casillas y pulsa «Añadir N seleccionados» (todos entran sin suplemento y en el orden en que se marcaron). 4. Para cambiar el producto o el suplemento de una elección, pulsa su lápiz: el formulario dice «Editando «{producto}». Conserva su posición en el plato.». Con el asa, subir o bajar se reordena; la papelera la quita, tras confirmarlo. Entra: los productos de Inventario (los activos, que se venden por unidades). Sale: la elección (avisa: elección nueva, cambiada, reordenada o quitada). El suplemento se suma al precio cerrado, nunca al precio del producto. Si falla: «Elige antes un producto del catálogo.», «Este plato ya ofrece ese producto. Para que se pueda elegir dos veces, activa «se puede elegir la misma opción más de una vez».», ««{producto}» está desactivado en Inventario: actívalo antes de ponerlo en un menú.», «Solo un producto que se vende por unidades puede ser una elección de menú.», «Un plato admite como mucho 40 elecciones.» y los avisos de importe. «Añadir varios» sin nada marcado: «Marca al menos un producto.». Si el catálogo no carga, el buscador lo dice con «Reintentar». Implicados: INVENTORY-F27
COMBO-F07 Elegir un menú al vender
Estado: servidor hecho; la hoja del TPV es de Ventas (pedido a Ventas, SALE-F12)
Actor: empleado, responsable
Pantalla: Ventas: Vender
Pasos:
1. En el TPV, en «Todos», los menús salen primero con la etiqueta «Menú». Toca uno: se abre una hoja
con todos sus platos a la vez.
2. Cada plato muestra su contador (0/1) y sus elecciones disponibles, con el suplemento con su signo
si no es cero. En un plato de una sola elección, tocar otra la cambia; en uno de varias, tocar de
más no añade.
3. Mientras falte algo, el botón dice el motivo («Elige {n} en {plato}»); cuando cada plato
obligatorio está completo, el botón suma el total en vivo. Pulsa añadir.
Entra: los menús a la venta que se pueden completar, con sus platos y sus elecciones disponibles
(combo.combo.for_sale).
Sale: la línea del menú con lo elegido. El precio que ve el TPV es una vista previa: el que vale lo
calcula este módulo al cobrar (COMBO-F08).
Si falla: un menú no sale en el TPV si no está a la venta, si no tiene ningún plato con elecciones o
si un plato obligatorio no tiene bastantes elecciones disponibles (agotadas, desactivadas o
eliminadas en Inventario). Un plato opcional sin elecciones disponibles no sale en la hoja.
Implicados: SALE-F12, MODIFIER-F06
COMBO-F08 Cobrar el menú: precio cerrado y suplementos
Estado: servidor hecho; lo llama Ventas al pedir y al cobrar (pedido a Ventas, SALE-F12)
Actor: sistema
Pantalla: ninguna
Pasos:
1. Ventas manda a Menús el menú, las elecciones de la línea y cómo se vende esa línea (el canal de su
pedido: en el local, para llevar o a domicilio; sin canal vale el ajuste del menú)
(combo.combo.resolve).
2. Menús comprueba cada plato (mínimo, máximo, repetición), que cada elección siga en el menú y siga
siendo una ración (se vende por unidades) y, en un pedido nuevo, que esté disponible, con su producto
activo, y que el menú siga a la venta.
3. El importe es el precio cerrado más un suplemento por cada elección (también la repetida). En una
cuenta abierta, Ventas manda el importe que tenía al pedirlo: Menús reparte ese y no vuelve a
pedir disponibilidad (el último flan servido y marcado «Agotado» no impide cobrar la mesa), solo
los platos, las raciones y los datos fiscales.
4. Devuelve la línea del menú: nombre, nombre de cocina, importe, forma de venta y sus componentes, en
el orden de los platos y de la elección.
Entra: el menú (por id o por nombre) y las elecciones (por id, o por el nombre o el SKU del producto,
con el plato si hace falta), con cuántas de cada una.
Sale: la línea del menú con sus componentes y su reparto fiscal (COMBO-F09). No guarda nada.
Si falla: la venta no se guarda y Menús dice por qué: «Ese menú no existe en este negocio.» (combo.combo_missing: un
404 es también «la app no está activa»), «Ese menú ya no está a la venta.», «Elige algo del menú antes de añadirlo.», «Al menú le falta elegir en «{plato}»: elige
{min}.», «El plato «{plato}» admite como mucho {max}.», «El plato «{plato}» no admite elegir dos veces
lo mismo.», ««{producto}» no es una elección de este menú.», ««{producto}» está agotado.», ««{producto}»
ya no se puede vender: está desactivado o eliminado en Inventario.», ««{producto}» ya no se vende por
unidades en Inventario, así que no se puede cobrar como elección de menú.» y «El importe del menú no puede
quedar en negativo.».
Implicados: SALE-F12
COMBO-F09 Repartir el IVA de un menú
Estado: servidor hecho; lo aplica Ventas en sus líneas (pedido a Ventas, SALE-F12)
Actor: sistema
Pantalla: ninguna
Pasos:
1. «En el local»: el menú sale en UNA línea, con el importe y el tipo de IVA del menú, aunque una
bebida de dentro tenga otro tipo en el catálogo (art. 91.Uno.2.2º LIVA: la bebida, también la
alcohólica, es accesoria del servicio de restauración).
2. «Para llevar» (el ajuste del menú, o un pedido para llevar o a domicilio aunque el menú sea «en el
local»): cada plato va a la categoría de su producto, salvo lo que en el local es servicio y para
llevar es venta de bienes: restaurant.alcohol (la cerveza o el vino del restaurante, 10 % en la
mesa) va a retail.alcohol (21 %, art. 90 LIVA). Es un mapa explícito con solo ese par de fábrica de
Impuestos; una categoría propia del negocio va como está. Si todos van a la misma categoría: UNA línea
con el importe y esa categoría.
3. «Para llevar» con categorías distintas: UNA línea por plato elegido, cada una con su categoría y el
importe repartido en proporción al precio de catálogo de cada producto (art. 79.Dos
LIVA), con el céntimo sobrante al resto mayor (a igualdad, al plato elegido antes); la suma es
exactamente el importe. Ejemplo: 6,00 € sobre un bocadillo de 4,50 € (10 %) y una cerveza de 2,00 €
(21 %): 4,15 € + 1,85 €.
4. No hay línea «padre» con dinero.
Entra: forma de venta, tipo de IVA del menú y elecciones; precio de catálogo y categoría fiscal de cada
producto, de Inventario.
Sale: el reparto, con la categoría fiscal de cada línea; Ventas calcula base y cuota con Impuestos.
Un menú «para llevar» sin tipo de IVA propio que se toma en el local va por plato a las categorías de
sus productos (las del local).
Si falla: «En el local» sin tipo de IVA no se puede guardar (COMBO-F02). Para llevar, un plato cuya
categoría no tiene equivalente activo en Impuestos: ««{producto}» se factura en {categoría}, que no
tiene equivalente para llevar en Impuestos, así que este menú no se puede vender para llevar.»
(combo.tax_category_takeaway); en un menú «para llevar» ese plato no se ofrece y el constructor lo
marca «Sin tipo de IVA para llevar». «Para llevar» con un producto sin categoría fiscal: ««{producto}» no tiene categoría fiscal en Inventario, así que este menú no se
puede cobrar.» (y no se ofrece). Partido con todos los productos a 0 € en el catálogo: «Los productos de
este menú no tienen precio de catálogo, así que no se puede repartir su IVA.»; si todas las elecciones
ofrecidas de un menú «para llevar» valen 0 € y van a categorías distintas, el menú no sale en el TPV.
Implicados: SALE-F12, TAX-F05
COMBO-F10 Bajar el stock de cada plato al cobrar
Estado: contrato probado con Inventario real; el aviso de venta lo manda Ventas (pedido a Ventas)
Actor: sistema
Pantalla: ninguna
Pasos:
1. Al cobrar, Ventas pone en el aviso de venta cobrada, en la línea del menú, cada producto elegido con
su cantidad (la de la línea ya multiplicada: tres menús, tres raciones de cada plato).
2. Inventario baja el stock de cada producto elegido, nunca el del menú, y solo de los que controlan
existencias; un producto de tipo servicio no mueve nada.
3. Si se anula la venta, Inventario devuelve lo que salió de su libro de movimientos.
4. Si se devuelve, Inventario repone lo que le lleguen como líneas devueltas (INVENTORY-F24): Ventas
manda los componentes del menú × los menús devueltos, una línea por producto.
Entra: los componentes que da COMBO-F08 (producto, cantidad, si es servicio).
Sale: movimientos de stock por producto (Inventario); nada de este módulo.
Si falla: como INVENTORY-F21. Probado con Inventario real (restore_refund repone los platos que le
llegan como líneas).
Implicados: INVENTORY-F22, INVENTORY-F23, SALE-F12
COMBO-F11 Mandar el menú a cocina
Estado: contrato hecho; la comanda la arma Ventas y la pinta Cocina (pedido a Ventas y a Cocina) Actor: sistema Pantalla: ninguna Pasos: 1. El camarero añade un menú a la cuenta de la mesa y manda la cuenta a cocina. 2. Ventas manda la línea del menú con su nombre, su nombre de cocina y los platos elegidos, cada uno con su producto, su categoría y su cantidad (lo que dio COMBO-F08 al pedir). 3. Cocina pone cada plato en la estación de su producto (o de su categoría), bajo el nombre de cocina del menú (KITCHEN-F06). Entra: la línea del menú de la cuenta abierta. Sale: nada de este módulo. Si falla: lo decide Ventas (pedido: no mandar a cocina un menú con un plato obligatorio sin elegir). Implicados: KITCHEN-F06, SALE-F20
COMBO-F12 Marcar una elección como agotada
Estado: hecho Actor: propietario, administrador, responsable, asistente Pantalla: Constructor del menú Pasos: 1. En la fila de la elección, pulsa «Agotado» (o di «hoy no hay flan en el menú del día»). 2. La fila se marca «Agotado» y el TPV deja de ofrecerla; el resto del menú se sigue vendiendo. 3. Cuando vuelva a haber, pulsa «Disponible». Entra: la elección. Sale: la elección cambiada (avisa: elección cambiada). Si falla: si al agotarla un plato obligatorio se queda sin bastantes elecciones, el menú deja de salir en el TPV y el aviso del constructor lo dice. Implicados: COMBO-F07
COMBO-F13 Cuando un producto deja el catálogo
Estado: hecho — la reacción está declarada; el aviso de Inventario se lo entrega el relay del outbox (fase 2). Mientras tanto, y siempre, la elección de un producto desactivado o eliminado no se ofrece ni se cobra. Actor: sistema Pantalla: Constructor del menú Pasos: 1. Alguien desactiva un producto en Inventario: sus elecciones siguen en los menús, marcadas «Desactivado en Inventario», y el TPV no las ofrece hasta que se reactive. 2. Alguien elimina un producto en Inventario: Menús quita sus elecciones de todos los menús (es definitivo: un producto eliminado no vuelve). Entra: el aviso de producto eliminado de Inventario. Sale: las elecciones quitadas (avisa: elección quitada, con el motivo). Si falla: la quita es idempotente: el mismo aviso dos veces no hace nada más. Implicados: INVENTORY-F04
Servicios: lo que entra y lo que sale
Todo se puede pedir por las pantallas, el asistente (por voz o por texto, con la sesión de quien
habla) y la API, con las mismas reglas y permisos. Un menú se nombra por su id o por su nombre; un plato
por su id o por su nombre dentro del menú; un producto por su id, su SKU o su nombre. Si un nombre vale
para varios, se dice cuáles y se pide que elija (combo.combo_ambiguous, combo.group_ambiguous,
combo.option_ambiguous, inventory.product_ambiguous).
Lo que ofrece: consultas (leen, no cambian nada)
| Qué da | Quién puede | Flujos | Servicio |
|---|---|---|---|
| Los menús, con búsqueda, filtros y orden, en páginas de hasta 100 (en vivo) | ver | F01 | combo.combo.list |
| Un menú con sus platos y sus elecciones, si sale en el TPV y por qué no (en vivo) | ver | F05, F06, F12 | combo.combo.get |
| El catálogo de menús del TPV: los que se pueden vender, con sus platos y sus elecciones disponibles (en vivo) | ver | F07 | combo.combo.for_sale |
| La línea de un menú con lo elegido: importe, componentes y reparto fiscal | ver | F08, F09, F10, F11 | combo.combo.resolve |
Lo que ofrece: acciones (cambian datos)
| Qué hace | Quién puede | Flujos | Servicio |
|---|---|---|---|
| Crea un menú | gestionar | F02 | combo.combo.create |
| Cambia un menú (solo lo enviado) | gestionar | F03 | combo.combo.update |
| Retira un menú con sus platos y elecciones | gestionar | F04 | combo.combo.delete |
| Añade un plato (al final) | gestionar | F05 | combo.group.create |
| Cambia un plato (solo lo enviado) | gestionar | F05 | combo.group.update |
| Retira un plato con sus elecciones | gestionar | F05 | combo.group.delete |
| Ordena los platos de un menú, todos a la vez | gestionar | F05 | combo.group.reorder |
| Añade una elección (al final) | gestionar | F06 | combo.option.create |
| Añade varias elecciones de golpe | gestionar | F06 | combo.option.bulk_create |
| Cambia una elección: producto, suplemento o disponible (solo lo enviado) | gestionar | F06, F12 | combo.option.update |
| Quita una elección | gestionar | F06 | combo.option.delete |
| Ordena las elecciones de un plato, todas a la vez | gestionar | F06 | combo.option.reorder |
| Quita las elecciones de un producto eliminado | solo la plataforma | F13 | combo.option.drop_product |
Lo que avisa
| Aviso | Cuándo | Qué lleva | Servicio |
|---|---|---|---|
| Menú nuevo | Crear un menú | id, name |
combo.combo.created |
| Menú cambiado | Guardar con algún cambio | id, name |
combo.combo.updated |
| Menú retirado | Retirar | id, name |
combo.combo.deleted |
| Plato nuevo, cambiado o retirado | Crear, guardar con cambios o retirar un plato | id, combo_id |
combo.group.created, .updated, .deleted |
| Platos reordenados | Cambiar el orden | combo_id |
combo.group.reordered |
| Elección nueva, cambiada o quitada | Añadir (una o varias), guardar con cambios, agotar o quitar; quitar por producto eliminado lleva reason: "product_deleted" |
id, group_id, combo_id, product_id |
combo.option.created, .updated, .deleted |
| Elecciones reordenadas | Cambiar el orden | group_id, combo_id |
combo.option.reordered |
A qué reacciona
| Aviso de | Qué hace Menús | Flujos | Servicio que escucha |
|---|---|---|---|
| Inventario: producto eliminado | Quita sus elecciones de todos los menús | F13 | inventory.product.deleted → combo.option.drop_product |
Además, los cambios de producto de Inventario (inventory.product.updated, .deleted) vuelven a
leer en vivo el constructor y el catálogo del TPV: el nombre, el precio y si está activo son de
Inventario.
Lo que necesita de otras apps
| De | Para qué | Flujos | Servicio |
|---|---|---|---|
Inventario (requires) |
Los productos que pueden ser elección, con su nombre, SKU, precio, categoría fiscal y si están activos (lectura directa: es una app requerida) | F06, F07, F08, F09 | modelo inventory.Product; el buscador de la pantalla usa inventory.product.list |
Impuestos (requires) |
Las categorías fiscales del selector y la del menú «en el local» | F02, F03, F09 | modelo tax.TaxCategory; el selector usa tax.category.list |
Contrato con Ventas
Ventas no requiere Menús: lo llama por el nombre de la operación (call_app), con la sesión de quien
vende, y si Menús no está activo el TPV no ofrece menús.
- Catálogo del TPV:
useLive('combo.combo.for_sale', {limit: 50}). Cada fila:id,name,kitchen_name,price(céntimos),supply_kind,sort_orderygroups[](id,name,min_choices,max_choices—0 sin límite—,allow_repeat,options[]conid,product_id,product_name,sku,price_delta). Solo menús a la venta que se pueden completar y elecciones disponibles. - Al pedir y al cobrar:
combo.combo.resolvecon{id | combo, choices: [{option_id | product (+ group), quantity}], amount?, channel?};channeles el de su pedido (dine_in,takeaway,delivery) y decide «en el local» o «para llevar» (sin él, el ajuste del menú). Devuelvecombo_id,name,kitchen_name,supply_kind,price,supplements,amount,splitytax_category_key(la de la línea única, onullsi se parte) ycomponents[]en orden:option_id,group_id,group_name,product_id,product_name,sku,category_id,is_service,quantity(por menú),price_delta,catalog_price,tax_category_keyyamount(su parte, solo sisplit). - Qué hace Ventas con eso (pedido a Ventas, SALE-F12): congelar la respuesta en la línea al pedir
(en una cuenta abierta, guardar
amount); al cobrar, volver a llamar con eseamounty materializar UNA línea (split = false, atax_category_key) o UNA línea por componente (split = true, cada una a sutax_category_keyy por suamount), hermanas y sin línea padre con dinero; mandar siempre elchanneldel pedido; multiplicar las cantidades por la de la línea; propagar los 409 de Menús tal cual; no mandar a cocina un menú con un plato obligatorio sin elegir; en una devolución, mandar los componentes × la cantidad devuelta como líneas desale.sale.refunded.
Contrato con Inventario
- En el aviso
sale.sale.completed, la línea del menú llevacomponents[]conproduct_id,quantity(la de la línea ya multiplicada) eis_servicede cada componente de COMBO-F08, y no mueve stock por sí misma (INVENTORY-F22; ya implementado eninventory.stock.apply_sale). Probado con Inventario real. - La devolución: INVENTORY-F24 ya repone las líneas que le llegan en
sale.sale.refunded(inventory.stock.restore_refund,linesde producto y cantidad). Ventas manda los componentes del menú × los menús devueltos como esas líneas (pedido a Ventas); probado con Inventario real.
Contrato con Cocina
- Cocina no lee Menús. Recibe de Ventas, en
sale.order.fired, la línea del menú conname,kitchen_namey sus componentes (product_id,product_name,category_id,quantity), y pone cada plato en su estación bajo el nombre de cocina del menú (KITCHEN-F06, pedido a Ventas por Cocina).
Quién puede qué
| Permiso | Propietario | Administrador | Responsable | Empleado | Servicio |
|---|---|---|---|---|---|
| Ver los menús, el catálogo del TPV y calcular una línea de menú | sí | sí | sí | sí | combo.view |
| Crear, cambiar, ordenar y retirar menús, platos y elecciones | sí | sí | sí | no | combo.manage |