-- LA REFERENCIA OBLIGATORIA TAMBIEN EN LAS OTRAS DOS PUERTAS DE COBRO.
--
-- La migracion 20260730050000 creo fn_exigir_referencia y la puso en
-- cobrar_y_cerrar_mesa, con la advertencia de que la regla vive en la BASE
-- porque "una validacion que solo esta en el navegador se la salta cualquiera
-- llamando la RPC directo, y esto es registro fiscal". Pero quedaron dos
-- puertas sin el candado:
--   - pagar_parte_cliente: la llama el CLIENTE (grant a anon), con la apikey
--     publica del bundle. Un POST directo con p_referencia:'' registraba un
--     pago electronico sin referencia, justo el hueco que se dijo cerrar.
--   - cobrar_parte_mesa: la llama el mesero (cobro por partes); el guard de
--     referencia solo estaba en la pantalla, no en la base.
--
-- Aca se redefinen ambas exigiendo fn_exigir_referencia, igual que la hermana.
-- La firma y los grants no cambian; efectivo y 'perdida' siguen sin referencia
-- (fn_pago_necesita_referencia ya los exceptua). Lo viejo no se toca.

begin;

-- ---- pagar_parte_cliente: el cliente paga su parte (queda por revisar) ----
create or replace function public.pagar_parte_cliente(
  p_cuenta_id uuid, p_comensal_id uuid, p_metodo text,
  p_referencia text default null, p_comprobante_url text default null
)
returns table(pago_id uuid, monto_usd numeric)
language plpgsql security definer set search_path to 'public'
as $function$
declare
  v_rest  uuid;
  v_monto numeric(10,2);
  v_n     int;
  v_pago  uuid;
  v_ref   text;
begin
  select restaurante_id into v_rest from public.cuentas_mesa where id = p_cuenta_id and estado <> 'cerrada';
  if v_rest is null then raise exception 'La cuenta no esta disponible'; end if;
  if p_metodo not in ('pago_movil','zelle','transferencia') then
    raise exception 'Metodo de pago en linea invalido';
  end if;
  -- La referencia se exige ANTES de bloquear nada: si falta, no se cobra a medias.
  v_ref := public.fn_exigir_referencia(p_metodo, p_referencia);
  perform 1 from public.comensales_mesa cm where cm.id = p_comensal_id and cm.cuenta_id = p_cuenta_id;
  if not found then raise exception 'La persona no pertenece a esta mesa'; end if;

  -- Pedidos de esa persona, sin cobrar y no cancelados: se bloquean (lock).
  select count(*), coalesce(sum(s.total_usd), 0) into v_n, v_monto
    from (
      select p.total_usd from public.pedidos p
       where p.cuenta_id = p_cuenta_id
         and p.comensal_id = p_comensal_id
         and p.pago_mesa_id is null
         and p.estado <> 'cancelado'
       for update
    ) s;
  if v_n = 0 or v_monto <= 0 then
    raise exception 'Esta persona no tiene nada pendiente por pagar';
  end if;

  insert into public.pagos_mesa (cuenta_id, restaurante_id, monto_usd, metodo, referencia, comprobante_url, comensal_id, verificado)
    values (p_cuenta_id, v_rest, v_monto, p_metodo,
            v_ref,
            nullif(trim(coalesce(p_comprobante_url, '')), ''),
            p_comensal_id, false)
    returning id into v_pago;

  update public.pedidos set pago_mesa_id = v_pago
    where cuenta_id = p_cuenta_id and comensal_id = p_comensal_id and pago_mesa_id is null and estado <> 'cancelado';

  return query select v_pago, v_monto;
end;
$function$;
grant execute on function public.pagar_parte_cliente(uuid, uuid, text, text, text) to anon, authenticated;

-- ---- cobrar_parte_mesa: el mesero cobra pedidos sueltos de la cuenta ----
create or replace function public.cobrar_parte_mesa(
  p_cuenta_id uuid,
  p_pedido_ids uuid[],
  p_metodo text,
  p_referencia text default null,
  p_nota text default null,
  p_comprobante_url text default null
)
returns table(pago_id uuid, monto_usd numeric, saldo_usd numeric)
language plpgsql
security definer
set search_path to 'public'
as $function$
declare
  v_cta            public.cuentas_mesa%rowtype;
  v_esperados      int;
  v_n              int;
  v_monto          numeric(10,2);
  v_pago           uuid;
  v_mesero_id      uuid := null;
  v_mesero_nombre  text := null;
  v_total          numeric(10,2);
  v_pagado         numeric(10,2);
  v_verif          boolean;
  v_ref            text;
begin
  select * into v_cta from public.cuentas_mesa where id = p_cuenta_id for update;
  if not found or v_cta.estado = 'cerrada' then
    raise exception 'La cuenta no esta disponible';
  end if;
  if not (fn_es_staff_de(v_cta.restaurante_id) or fn_es_admin()) then
    raise exception 'Sin permiso';
  end if;
  if p_metodo not in ('pago_movil','efectivo','transferencia','zelle','punto_venta') then
    raise exception 'Metodo de pago invalido';
  end if;
  -- La referencia se exige ANTES de tocar nada (efectivo no la necesita).
  v_ref := public.fn_exigir_referencia(p_metodo, p_referencia);
  -- Efectivo (o el dueño) = verificado; electronico del mesero = por revisar.
  v_verif := (p_metodo = 'efectivo') or fn_es_dueno_de(v_cta.restaurante_id) or fn_es_admin();

  select count(distinct u) into v_esperados from unnest(coalesce(p_pedido_ids, '{}'::uuid[])) u;
  if v_esperados = 0 then raise exception 'Elige al menos un pedido para cobrar'; end if;

  select pf.id, coalesce(nullif(trim(pf.nombre), ''), 'Mesero')
    into v_mesero_id, v_mesero_nombre from public.perfiles pf where pf.id = auth.uid();

  select count(*), coalesce(sum(s.total_usd), 0) into v_n, v_monto
    from (
      select p.total_usd from public.pedidos p
       where p.id = any(p_pedido_ids) and p.cuenta_id = p_cuenta_id
         and p.pago_mesa_id is null and p.estado <> 'cancelado'
       for update
    ) s;
  if v_n <> v_esperados then
    raise exception 'Algun pedido ya fue cobrado, esta cancelado o no es de esta cuenta';
  end if;
  if v_monto <= 0 then raise exception 'Nada que cobrar'; end if;

  insert into public.pagos_mesa (cuenta_id, restaurante_id, monto_usd, metodo, referencia, nota, comprobante_url, mesero_id, mesero_nombre, verificado)
    values (p_cuenta_id, v_cta.restaurante_id, v_monto, p_metodo, v_ref, nullif(trim(coalesce(p_nota,'')), ''), nullif(trim(coalesce(p_comprobante_url,'')), ''), v_mesero_id, v_mesero_nombre, v_verif)
    returning id into v_pago;
  update public.pedidos set pago_mesa_id = v_pago where id = any(p_pedido_ids);

  select coalesce(sum(p.total_usd), 0) into v_total
    from public.pedidos p where p.cuenta_id = p_cuenta_id and p.estado <> 'cancelado';
  select coalesce(sum(pm.monto_usd), 0) into v_pagado
    from public.pagos_mesa pm where pm.cuenta_id = p_cuenta_id and pm.verificado;

  return query select v_pago, v_monto, greatest(v_total - v_pagado, 0::numeric);
end;
$function$;

commit;
