O problema
Oito sistemas, cada um com a sua sala, cada um a contar a sua noite. O prato da ementa, o prato do take-away e a ficha técnica eram três linhas sem relação: uma venda não podia consumir stock porque não sabia que receita era. Um visitante via oito demonstrações bonitas e nenhuma casa.
Uma sala, uma ementa
Não há tabelas novas para a casa. As tabelas já pendiam todas de demo_rooms; a sala ganhou a lista de módulos ligados e passou a vir por contexto para todos os ecrãs, em vez de nascer oito vezes. E cada prato das duas ementas ganhou uma coluna, recipe_id, a apontar para a ficha técnica. Uma coluna, e a venda passou a saber o que consumir.
-- Uma coluna em cada ementa, e a venda sabe o que consumir.
alter table demo_menu_items add column recipe_id integer references demo_recipes(id);
alter table demo_takeaway_dishes add column recipe_id integer references demo_recipes(id);
-- A sala sabe que módulos tem ligados. Não há tabela nova para a casa.
alter table demo_rooms add column modules text[] not null default '{}';| Tabela | Para que serve |
|---|---|
| demo_rooms.modules | Que módulos esta casa tem ligados. A única coluna nova na sala. |
| demo_menu_items.recipe_id | A ficha técnica por trás de cada prato da sala. Nulo para o que não se cozinha (bebidas). |
| demo_takeaway_dishes.recipe_id | O mesmo, para a ementa do take-away. |
| demo_order_items.consumed_at | Quando a ficha consumiu, e stock_short se saiu sem stock que chegasse. |
| demo_events | O registo do que atravessou módulos, por sala. |
| demo_haccp_deliveries.po_code, batch_id | A entrada HACCP que veio de uma guia, ligada ao lote do stock. |
Uma noite coerente
Abrir a casa semeia cada módulo ligado, uma vez: duas mesas a meio do serviço, duas reservas, uma encomenda de take-away em preparação, a abertura HACCP feita com o frigorífico da carne fora dos limites, a despensa com validades espalhadas, uma encomenda enviada ao talho, a escala da semana com quem já devia ter entrado a trabalhar. Todos os módulos contam a mesma noite porque são os mesmos ingredientes, a mesma equipa e a mesma ementa.
A venda consome no passe
Quando a cozinha marca um prato pronto, a ficha técnica consome os ingredientes por FEFO, com movimentos, dentro da mesma função que muda o estado. É no pronto e não no pedido, porque pedidos anulam-se e um prato que saiu do passe não volta. Sem stock a cozinha não pára: o prato sai marcado como saído sem stock e passa a esgotado nas duas ementas até entrar mercadoria. Só acontece em casas com stock ligado: um KDS sozinho não tem despensa e não finge que tem.
-- No passe: marcar pronto consome, uma vez só, dentro da mesma função.
if p_done and not p_voided and v_row.consumed_at is null then
select m.recipe_id into v_recipe from demo_menu_items m where m.id = v_row.menu_item_id;
if v_recipe is not null then
v_res := demo_consume_dish(p_room, v_recipe, v_row.qty, 'Mesa ' || v_order.table_id || ' · ' || v_row.name_snapshot);
if v_res is not null then -- null = casa sem stock ligado
v_row.consumed_at := now();
v_row.stock_short := (v_res->>'short')::boolean;
end if;
end if;
end if;
-- Quantas doses cada ficha ainda dá: o ingrediente mais curto manda.
select recipe_id, min(floor(disponivel / (qty_por_lote / doses))) from ...O registo de acontecimentos
Cada coisa que atravessa um módulo para outro fica numa linha: a venda que consumiu, o lote que ficou abaixo do mínimo, a guia que chegou com dois quilos a menos, o incumprimento e a sua correcção, a mesa sentada, o ponto picado. O servidor guarda os dados; a frase faz-se no browser, na língua do visitante. A faixa no topo da casa é a ligação entre os sistemas a acontecer, em vez de afirmada num texto.
-- Cada travessia fica numa linha. O texto faz-se no browser.
create table demo_events (
id bigserial primary key,
room_code text not null references demo_rooms(code) on delete cascade,
at timestamptz not null default now(),
module text not null, kind text not null,
payload jsonb not null default '{}'
);
-- 'sale.consumed' {dish, ref, lines: [{ingredient, qty, allocation: [{batch, qty, left}]}]}
-- 'sale.short' {dish, ingredient, needed, available}
-- 'po.received' {code, status, batches, short}
-- 'haccp.fail' {point, value, min, max}Avançar o serviço não é fingir tempo
Uma casa aberta às cinco da manhã está parada. Avançar o serviço dá um passo na noite com as mesmas funções que os ecrãs usam: até três passos de módulos diferentes por carga, e devolve o que fez. Não há relógio falso, não há dados encenados: cada passo podia ter sido feito à mão e fica registado como tal. Um visitante vê uma noite inteira em dez cargas.
Uma só porta para a mercadoria
A recepção HACCP e a conferência da guia registavam o mesmo camião duas vezes, com dois lotes que nunca se encontravam. Agora conferir a guia cria os lotes no stock e, se a casa tem HACCP, escreve o registo de recepção com temperatura, lote e validade, ligado ao lote, na mesma transacção. Um prato servido leva ao lote, o lote leva à guia, a guia leva à temperatura a que a carne entrou. O formulário do HACCP fica para o que chega sem encomenda.
-- Conferir a guia faz as duas coisas na mesma transacção.
v_batch := demo_stock_receive(p_room, ingrediente, v_good, preco_da_encomenda, validade, local, fornecedor, p_by);
if v_haccp then
insert into demo_haccp_deliveries (room_code, supplier, product, kind, batch, expiry, qty, unit,
temp_c, temp_ok, accepted, signed_by, po_code, batch_id)
values (p_room, fornecedor, ingrediente, tipo, v_batch.code, validade, v_recv, unidade,
p_temp, p_temp <= 4.0, v_good > 0, p_by, v_po.code, v_batch.id);
end if;O que faria diferente num cliente real
Utilizadores com sessão e políticas por linha em vez de um código de sala: o empregado de sala não vê o custo-hora dos colegas. Perfis, em vez de um botão para trocar de lado. O painel com histórico, não só hoje. Sub-receitas e perdas de preparação, para o consumo ser exacto. E a caixa registadora ligada, para a venda ser a venda e não um pedido entregue.