Saltar para o conteúdo
PMPaulo Mota
Voltar à demo

Como foi feito

Compras, por dentro

O preço nasce no servidor, a guia confere-se linha a linha, e o que chega em condições vira lote sem ninguém copiar números.

01

O problema

As encomendas fazem-se por telefone e por WhatsApp, e o preço é o que o fornecedor disser na factura. Quando a carne chega, ninguém confere a guia contra o que foi pedido: assina-se e arruma-se. Faltam dois quilos, vem uma caixa danificada, e descobre-se na semana seguinte, se se descobrir. E quando o frango sobe seis por cento, o prato continua ao mesmo preço, porque ninguém deu por ela.

02

Modelo de dados

Fornecedores e preços são dados de referência, partilhados com o stock e o food cost. Uma encomenda pertence a uma sala e tem linhas: ingrediente, quantidade pedida, preço fixado no momento, e depois quantidade recebida, danificada e o lote que gerou. O preço fica gravado na linha para uma subida futura não reescrever encomendas antigas.

TabelaPara que serve
demo_suppliersQuem fornece, com prazo de entrega e encomenda mínima. Partilhado com o stock.
demo_supplier_pricesUm preço por fornecedor, ingrediente e data. O actual é o mais recente. Partilhado com o food cost.
demo_purchase_ordersUma encomenda por sala: fornecedor, estado, quando foi enviada e recebida e por quem.
demo_purchase_linesAs linhas: pedido, preço fixado no momento, recebido, danificado e o lote que gerou no stock.
03

O preço nasce no servidor

O browser diz o que quer e quanto. A função vai buscar o preço mais recente daquele fornecedor para aquele ingrediente e grava-o na linha. Um fornecedor que não vende o ingrediente é recusado. A encomenda mínima do fornecedor é verificada na função, não num aviso no ecrã, e uma encomenda abaixo dela não é criada.

sql
-- O browser diz o que quer e quanto. O preço vem daqui.
v_price := demo_current_price(p_supplier, (v_line->>'ingredient_id')::integer);
if v_price is null then
  raise exception 'fornecedor nao vende este ingrediente';
end if;

insert into demo_purchase_lines (order_id, ingredient_id, qty_ordered, unit_price_cents)
values (v_row.id, ..., (v_line->>'qty')::numeric, v_price);

-- Encomenda mínima do fornecedor: na função, não num aviso no ecrã.
if v_total < v_sup.min_order_cents then
  raise exception 'abaixo da encomenda minima';
end if;
04

A guia confere-se linha a linha

Receber é comparar encomendado com recebido, linha a linha, e dizer quanto veio danificado. Se tudo bater, a encomenda fica recebida; se não, fica parcial, e a diferença fica escrita. E é aqui que as compras tocam no stock: o que chegou em condições entra como lote, ao preço da encomenda, com a validade típica do ingrediente, e o movimento de entrada fica referenciado à encomenda. Ninguém copia números de um sistema para o outro.

sql
-- Encomendado contra recebido, linha a linha.
v_good := v_recv - v_dmg;
if v_recv <> v_line.qty_ordered or v_dmg > 0 then v_all_ok := false; end if;

-- É aqui que as compras tocam no stock: o que chegou em condições
-- entra como lote, ao preço da encomenda, com a validade do ingrediente.
if v_good > 0 then
  v_batch := demo_stock_receive(
    p_room, v_line.ingredient_id, v_good, v_line.unit_price_cents,
    v_today + v_ing.shelf_days, <local pela categoria>, v_po.supplier_id, p_by);
  update demo_purchase_lines set batch_id = v_batch.id where id = v_line.id;
end if;

update demo_purchase_orders
   set status = case when v_all_ok then 'recebida' else 'parcial' end;
05

Preços com história

Cada preço tem uma data. O preço actual é o mais recente, os anteriores ficam. É isso que permite ver que o frango subiu de 5,80 para 6,20, e comparar fornecedores lado a lado, com o prazo de entrega e a encomenda mínima ao lado do preço, porque o mais barato ao quilo com trinta quilos de mínimo e dois dias de espera nem sempre é a melhor compra.

sql
-- Cada preço tem uma data. O actual é o mais recente; os outros ficam.
create table demo_supplier_prices (
  supplier_id    integer references demo_suppliers(id),
  ingredient_id  integer references demo_ingredients(id),
  price_cents    integer not null check (price_cents > 0),
  since          date not null default current_date,
  unique (supplier_id, ingredient_id, since)
);

create function demo_current_price(p_supplier int, p_ingredient int)
returns integer language sql stable as $$
  select price_cents from demo_supplier_prices
   where supplier_id = p_supplier and ingredient_id = p_ingredient
   order by since desc limit 1;
$$;
06

O que faria diferente num cliente real

Envio da encomenda por email ao fornecedor a partir do sistema, com PDF. Importação da factura do fornecedor para conferir preço além de quantidade. Sugestão de encomenda a partir do stock baixo e do consumo previsto. E aprovação: encomendas acima de um valor passam pelo dono antes de sair.