-- TODA VENTA SE SELLA, Y LOS DATOS DEL COMPRADOR SE CAPTURAN (2026-07-29)
--
-- Dos cosas, y las dos nacen de la misma regla:
--
-- 1. Cuando un negocio esta habilitado, TODA venta cerrada entra a la cola de
--    sellado. No existe la venta que "no se factura porque el cliente no pidio
--    factura". Eso es lo que hace una maquina fiscal: imprime todo, pida o no
--    pida el cliente. Lo unico que cambia es si el documento sale a nombre del
--    comprador o como Consumidor Final.
--
-- 2. El "necesitas factura?" NO enciende ni apaga nada. Solo sirve para pedirle
--    el RIF y la razon social a quien la quiera a su nombre.
--
-- La cola se llena SOLA, por trigger, no por que alguien se acuerde de llamar
-- una funcion. Si el negocio no esta habilitado (ver fn_estado_fiscal), no se
-- encola nada y Gustito sigue siendo lo que siempre fue: el puente.
--
-- FRONTERA INTACTA: aca no se calcula ni un centimo de impuesto. Gustito entrega
-- el total que cobro; quien desglosa, numera y sella es el instalable.
begin;

-- 1. DATOS DEL COMPRADOR ---------------------------------------------------
alter table public.pedidos
  add column if not exists cliente_correo text;

alter table public.cuentas_mesa
  add column if not exists cliente_rif text,
  add column if not exists cliente_razon_social text,
  add column if not exists cliente_direccion_fiscal text,
  add column if not exists cliente_correo text;

alter table public.cuentas_mesa drop constraint if exists cuentas_mesa_cliente_rif_check;
alter table public.cuentas_mesa
  add constraint cuentas_mesa_cliente_rif_check
  check (cliente_rif is null or public.fn_rif_valido(cliente_rif));

-- 2. LA COLA ---------------------------------------------------------------
create table if not exists fiscal.cola_gustito (
  id uuid primary key default gen_random_uuid(),
  restaurante_id uuid not null references public.restaurantes(id) on delete cascade,
  emisor_id uuid not null references fiscal.emisores(id) on delete cascade,
  tipo text not null check (tipo in ('cuenta','pedido')),
  origen_id uuid not null,
  estado text not null default 'pendiente' check (estado in ('pendiente','sellado','error')),
  creado_en timestamptz not null default now(),
  tomado_en timestamptz,
  sellado_en timestamptz,
  numero_documento bigint,
  numero_control bigint,
  token text,
  intentos integer not null default 0,
  ultimo_error text,
  unique (tipo, origen_id)
);
create index if not exists fiscal_cola_pendientes_idx
  on fiscal.cola_gustito(emisor_id, creado_en) where estado = 'pendiente';

revoke all on table fiscal.cola_gustito from anon, authenticated;

-- 3. QUIEN ENCOLA ----------------------------------------------------------
-- Silencioso a proposito: si el negocio no esta habilitado, no pasa nada y la
-- venta sigue su curso normal. Nunca puede tumbar un cobro.
create or replace function fiscal.encolar(p_tipo text, p_origen_id uuid, p_restaurante_id uuid)
returns void
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $fn$
declare v_emisor uuid;
begin
  if p_restaurante_id is null or p_origen_id is null then return; end if;

  select n.emisor_id into v_emisor
    from fiscal.negocios n
   where n.restaurante_id = p_restaurante_id
     and (public.fn_estado_fiscal(p_restaurante_id)->>'activa')::boolean;
  if v_emisor is null then return; end if;

  insert into fiscal.cola_gustito (restaurante_id, emisor_id, tipo, origen_id)
    values (p_restaurante_id, v_emisor, p_tipo, p_origen_id)
    on conflict (tipo, origen_id) do nothing;
exception when others then
  -- Nunca tumbar la venta por un problema en la cola. Queda el rastro y la
  -- Estacion la recoge despues con fiscal.rescatar_sin_encolar().
  return;
end;
$fn$;

-- Un pedido suelto (delivery, retiro en tienda, venta de caja) se factura
-- cuando queda entregado. Los de mesa NO: esos van dentro de la cuenta.
create or replace function public.fn_encolar_pedido_fiscal()
returns trigger
language plpgsql
security definer
set search_path to 'public', 'fiscal'
as $fn$
begin
  if new.cuenta_id is null
     and new.estado = 'entregado'
     and (tg_op = 'INSERT' or old.estado is distinct from 'entregado') then
    perform fiscal.encolar('pedido', new.id, new.restaurante_id);
  end if;
  return new;
end;
$fn$;

drop trigger if exists tg_encolar_pedido_fiscal on public.pedidos;
create trigger tg_encolar_pedido_fiscal
  after insert or update of estado on public.pedidos
  for each row execute function public.fn_encolar_pedido_fiscal();

-- Una mesa se factura cuando se cierra: un solo documento con todo lo que se
-- consumio, que es como se cobra en la vida real.
create or replace function public.fn_encolar_cuenta_fiscal()
returns trigger
language plpgsql
security definer
set search_path to 'public', 'fiscal'
as $fn$
begin
  if new.estado = 'cerrada'
     and (tg_op = 'INSERT' or old.estado is distinct from 'cerrada') then
    perform fiscal.encolar('cuenta', new.id, new.restaurante_id);
  end if;
  return new;
end;
$fn$;

drop trigger if exists tg_encolar_cuenta_fiscal on public.cuentas_mesa;
create trigger tg_encolar_cuenta_fiscal
  after insert or update of estado on public.cuentas_mesa
  for each row execute function public.fn_encolar_cuenta_fiscal();

-- Red de seguridad: si alguna venta se escapo (la cola fallo, o el negocio se
-- habilito despues de haber vendido), esto la recoge. La Estacion la llama al
-- arrancar. Solo mira para atras un rato, no toda la historia del negocio.
create or replace function fiscal.rescatar_sin_encolar(p_horas integer default 72)
returns integer
language plpgsql
security definer
set search_path to 'fiscal', 'public'
as $fn$
declare
  v_n integer := 0;
  v_parcial integer := 0;
begin
  insert into fiscal.cola_gustito (restaurante_id, emisor_id, tipo, origen_id)
  select p.restaurante_id, n.emisor_id, 'pedido', p.id
    from public.pedidos p
    join fiscal.negocios n on n.restaurante_id = p.restaurante_id
   where p.cuenta_id is null
     and p.estado = 'entregado'
     and p.entregado_en > now() - make_interval(hours => p_horas)
     and (public.fn_estado_fiscal(p.restaurante_id)->>'activa')::boolean
  on conflict (tipo, origen_id) do nothing;
  get diagnostics v_n = row_count;

  insert into fiscal.cola_gustito (restaurante_id, emisor_id, tipo, origen_id)
  select c.restaurante_id, n.emisor_id, 'cuenta', c.id
    from public.cuentas_mesa c
    join fiscal.negocios n on n.restaurante_id = c.restaurante_id
   where c.estado = 'cerrada'
     and c.cerrada_en > now() - make_interval(hours => p_horas)
     and (public.fn_estado_fiscal(c.restaurante_id)->>'activa')::boolean
  on conflict (tipo, origen_id) do nothing;
  get diagnostics v_parcial = row_count;

  return v_n + v_parcial;
end;
$fn$;

-- 4. EL "NECESITAS FACTURA?" ----------------------------------------------
-- Solo captura datos. No enciende ni apaga nada. Si el negocio no esta
-- habilitado, ni siquiera deja guardarlos (para que no queden datos fiscales
-- dando vueltas en negocios que no facturan).
create or replace function public.guardar_datos_fiscales(
  p_tipo text,
  p_id uuid,
  p_rif text,
  p_razon_social text default null,
  p_direccion text default null,
  p_correo text default null
)
returns jsonb
language plpgsql
security definer
set search_path to 'public', 'fiscal'
as $fn$
declare
  v_rest uuid;
  v_rif  text;
  v_sellado boolean;
begin
  if p_tipo not in ('cuenta','pedido') then
    raise exception 'Tipo invalido';
  end if;

  if p_tipo = 'pedido' then
    select restaurante_id into v_rest from public.pedidos where id = p_id;
  else
    select restaurante_id into v_rest from public.cuentas_mesa where id = p_id;
  end if;
  if v_rest is null then
    raise exception 'No encontramos esa venta';
  end if;

  if not (public.fn_estado_fiscal(v_rest)->>'activa')::boolean then
    raise exception 'Este negocio todavia no esta habilitado para facturar';
  end if;

  if not public.fn_rif_valido(p_rif) then
    raise exception 'Ese RIF o cedula no es valido, revisa el ultimo digito';
  end if;
  v_rif := public.fn_rif_normalizado(p_rif);

  -- Una vez sellado el documento no se le cambian los datos al comprador: eso
  -- se corrige con nota de credito, no editando la venta.
  select exists (select 1 from fiscal.cola_gustito c
                  where c.tipo = p_tipo and c.origen_id = p_id and c.estado = 'sellado')
    into v_sellado;
  if v_sellado then
    raise exception 'Esa venta ya tiene su documento emitido. Para cambiar los datos hace falta una nota de credito.';
  end if;

  if p_tipo = 'pedido' then
    update public.pedidos
       set cliente_rif = v_rif,
           cliente_razon_social = nullif(trim(p_razon_social), ''),
           cliente_direccion_fiscal = nullif(trim(p_direccion), ''),
           cliente_correo = nullif(trim(p_correo), '')
     where id = p_id;
  else
    update public.cuentas_mesa
       set cliente_rif = v_rif,
           cliente_razon_social = nullif(trim(p_razon_social), ''),
           cliente_direccion_fiscal = nullif(trim(p_direccion), ''),
           cliente_correo = nullif(trim(p_correo), '')
     where id = p_id;
  end if;

  return jsonb_build_object('ok', true, 'rif', v_rif);
end;
$fn$;

grant execute on function public.guardar_datos_fiscales(text, uuid, text, text, text, text)
  to anon, authenticated, service_role;

commit;
