-- EL INVENTARIO CUADRA: se descuenta al vender y se repone al cancelar.
--
-- El trigger viejo (tg_descontar_stock) solo restaba al insertar un renglon,
-- con un "exception when others then null" que se tragaba cualquier error, y
-- nada reponia el stock al cancelar un pedido ni al quitar un renglon. Con el
-- control de stock activado, los numeros se iban degradando sin ruido, y esos
-- numeros alimentan el cerebro de costos.
--
-- La invariante nueva: el stock consumido es exactamente la suma de los
-- renglones de pedidos EXISTENTES cuyo estado no es 'cancelado'.
--
--   - Insertar renglon (pedido vivo)  -> resta.
--   - Borrar renglon (pedido vivo)    -> repone (asi corrige quitar_item_pedido).
--   - Cambiar cantidad o producto     -> ajusta la diferencia exacta.
--   - Pedido pasa a 'cancelado'       -> repone todos sus renglones. El caso
--     "quitar el ultimo renglon cancela el pedido" no repone doble: el renglon
--     se repuso al borrarse y al cancelar ya no queda ninguno que sumar.
--   - Pedido sale de 'cancelado'      -> vuelve a restar (hoy es estado
--     terminal y no pasa, pero la simetria no se negocia: si algun dia alguien
--     revive un pedido a mano, el stock lo sigue).
--   - Borrado fisico del pedido       -> NO repone (cuando el cascade borra los
--     renglones, el padre ya no existe: purgar un pedido entregado no devuelve
--     comida al estante).
--   - Renglon de un pedido cancelado  -> no toca nada (ya se cuadro al cancelar).
--   - Mesas perdidas u olvidadas      -> sus pedidos quedan 'entregado', no
--     'cancelado': comida hecha y perdida sigue siendo stock consumido.
--
-- Sin greatest(0, ...): si se vende mas de lo contado, el stock queda negativo
-- a proposito. Un numero negativo es el aviso honesto de que el conteo estaba
-- malo; taparlo con cero era perder la diferencia y sobre-reponer al cancelar.
--
-- Sin "exception when others then null": no hay CHECK sobre productos.stock ni
-- nada mas que pueda fallar aqui; si algun dia falla, que se oiga, no que se
-- pierda callado.

begin;

drop trigger if exists trg_descontar_stock on public.pedido_items;
drop function if exists public.tg_descontar_stock();

-- Cada renglon que nace, muere o cambia, ajusta el stock de su producto,
-- siempre que el pedido padre exista y no este cancelado.
create or replace function public.tg_cuadrar_stock_item()
returns trigger
language plpgsql
security definer
set search_path to 'public'
as $$
declare
  v_estado text;
begin
  select estado into v_estado
    from public.pedidos
   where id = coalesce(new.pedido_id, old.pedido_id);

  -- Padre borrado (cascade de una purga) o cancelado: el stock ya quedo
  -- cuadrado por otra via, este renglon no mueve nada.
  if v_estado is null or v_estado = 'cancelado' then
    return null;
  end if;

  if tg_op in ('DELETE', 'UPDATE') and old.producto_id is not null then
    update public.productos
       set stock = stock + old.cantidad
     where id = old.producto_id and controla_stock;
  end if;

  if tg_op in ('INSERT', 'UPDATE') and new.producto_id is not null then
    update public.productos
       set stock = stock - new.cantidad
     where id = new.producto_id and controla_stock;
  end if;

  return null;
end $$;

create trigger trg_cuadrar_stock_item
  after insert or delete or update of cantidad, producto_id
  on public.pedido_items
  for each row execute function public.tg_cuadrar_stock_item();

-- Cancelar un pedido repone sus renglones; revivirlo (si algun dia pasa) los
-- vuelve a restar. Cualquier otro cambio de estado no toca el stock.
create or replace function public.tg_cuadrar_stock_pedido()
returns trigger
language plpgsql
security definer
set search_path to 'public'
as $$
declare
  v_signo int;
begin
  if old.estado <> 'cancelado' and new.estado = 'cancelado' then
    v_signo := 1;
  elsif old.estado = 'cancelado' and new.estado <> 'cancelado' then
    v_signo := -1;
  else
    return null;
  end if;

  update public.productos pr
     set stock = pr.stock + v_signo * agg.total
    from (
      select producto_id, sum(cantidad) as total
        from public.pedido_items
       where pedido_id = new.id and producto_id is not null
       group by producto_id
    ) agg
   where pr.id = agg.producto_id and pr.controla_stock;

  return null;
end $$;

create trigger trg_cuadrar_stock_pedido
  after update of estado
  on public.pedidos
  for each row execute function public.tg_cuadrar_stock_pedido();

commit;
