-- ART. 29 NUMERAL 5 Y ART. 34 DE LA 000102: la imprenta debe remitir al SENIAT
-- la relacion de cada mes dentro de los diez dias continuos siguientes, y dejar
-- de informar dos o mas periodos mensuales es causal de REVOCATORIA de la
-- autorizacion. Hasta hoy el panel tenia el reporte, pero nada avisaba si un
-- mes se estaba pasando de la fecha. Esta funcion responde la salud de los
-- reportes: que meses tienen documentos numerados, cuales ya se remitieron y
-- cuales estan vencidos, para que el panel lo grite a tiempo.

create or replace function fiscal.salud_reportes()
returns table(
  rif text,
  razon_social text,
  anio int,
  mes int,
  documentos bigint,
  reportado boolean,
  vencido boolean
)
language sql
security definer
set search_path to 'fiscal', 'public'
as $$
  with meses as (
    -- Meses calendario en hora de Venezuela: la relacion es por mes del
    -- contribuyente, no por mes UTC del servidor.
    select d.emisor_id,
           date_trunc('month', d.recibido_en at time zone 'America/Caracas')::date as periodo,
           count(*) as documentos
      from fiscal.documentos d
     where date_trunc('month', d.recibido_en at time zone 'America/Caracas')
         < date_trunc('month', now() at time zone 'America/Caracas')
     group by 1, 2
  )
  select e.rif,
         e.razon_social,
         extract(year from m.periodo)::int as anio,
         extract(month from m.periodo)::int as mes,
         m.documentos,
         (env.id is not null) as reportado,
         -- Vencido: paso el dia 10 del mes siguiente (los "diez dias continuos"
         -- del art. 29) y el periodo sigue sin marcarse como remitido.
         (env.id is null
          and (now() at time zone 'America/Caracas')::date
              > (m.periodo + interval '1 month' + interval '9 days')::date) as vencido
    from meses m
    join fiscal.emisores e on e.id = m.emisor_id
    left join fiscal.envios_mensuales env
      on env.emisor_id = m.emisor_id and env.periodo = m.periodo
   order by m.periodo desc, e.rif;
$$;

-- CORRECCION DE RAIZ que destapo la prueba viva: la relacion mensual agrupaba
-- por fecha_emision, que es una fecha DECLARADA POR EL EMISOR (viaja en el
-- cuerpo de la peticion). El art. 32 de la 000102 manda llevar el registro por
-- fecha de ASIGNACION del numero de control, que es la que controla la
-- imprenta (recibido_en). Con el criterio viejo, un emisor podia mover
-- documentos de periodo declarando otra fecha, y el aviso de salud (que ya
-- usaba la fecha de asignacion) no cuadraba con la relacion. Todo el circuito
-- queda ahora por fecha de asignacion, en mes calendario de Venezuela, y la
-- relacion ademas la muestra (el art. 32 la exige en el registro).
drop function if exists fiscal.relacion_mensual(text, integer, integer);

create function fiscal.relacion_mensual(p_rif text, p_anio integer, p_mes integer)
returns table(
  rif_emisor text, razon_social text, serie text, tipo text,
  numero_documento bigint, numero_control bigint,
  fecha_emision timestamptz, fecha_asignacion timestamptz,
  base_bs numeric, iva_bs numeric, total_bs numeric
)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $$
  select e.rif, e.razon_social, d.serie, d.tipo, d.numero_documento, d.numero_control,
         d.fecha_emision, d.recibido_en as fecha_asignacion,
         d.base_bs, d.iva_bs, d.total_bs
    from fiscal.documentos d
    join fiscal.emisores e on e.id = d.emisor_id
   where e.rif = public.fn_rif_normalizado(p_rif)
     and date_trunc('month', d.recibido_en at time zone 'America/Caracas')::date
         = make_date(p_anio, p_mes, 1)
   order by d.numero_control;
$$;

-- resumen_mensual y marcar_enviado se apoyan en relacion_mensual, asi que
-- heredan el criterio sin tocarles el cuerpo. Se recrea resumen_mensual
-- identico solo para que quede registrado que se valido contra la nueva firma.
create or replace function fiscal.resumen_mensual(p_rif text, p_anio integer, p_mes integer)
returns table(documentos integer, desde_control bigint, hasta_control bigint, base_bs numeric, iva_bs numeric, total_bs numeric, ya_enviado boolean)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $$
  with r as (
    select * from fiscal.relacion_mensual(p_rif, p_anio, p_mes)
  )
  select count(*)::int,
         min(numero_control),
         max(numero_control),
         coalesce(round(sum(base_bs), 2), 0),
         coalesce(round(sum(iva_bs), 2), 0),
         coalesce(round(sum(total_bs), 2), 0),
         exists (
           select 1 from fiscal.envios_mensuales em
             join fiscal.emisores e on e.id = em.emisor_id
            where e.rif = public.fn_rif_normalizado(p_rif)
              and em.periodo = make_date(p_anio, p_mes, 1)
         )
    from r;
$$;

-- El tablero deja de calcular los periodos por su cuenta (fecha declarada, sin
-- los diez dias de plazo) y cuenta los VENCIDOS de la misma salud_reportes():
-- una sola verdad para el contador, el aviso y el detalle.
create or replace function fiscal.tablero()
returns table(clientes_activos integer, documentos_mes integer, total_mes_bs numeric, documentos_hoy integer, intentos_fallidos_hoy integer, intentos_panel_hoy integer, periodos_sin_reportar integer)
language sql
stable
security definer
set search_path to 'fiscal', 'public'
as $$
  select
    (select count(*)::int from fiscal.emisores where activo),
    (select count(*)::int from fiscal.documentos where fecha_emision >= date_trunc('month', now())),
    coalesce((select round(sum(total_bs), 2) from fiscal.documentos where fecha_emision >= date_trunc('month', now())), 0),
    (select count(*)::int from fiscal.documentos where recibido_en >= current_date),
    (select count(*)::int from fiscal.intentos where tipo = 'estacion' and not entro and en >= current_date),
    (select count(*)::int from fiscal.intentos where tipo = 'panel' and not entro and en >= current_date),
    -- Periodos VENCIDOS: mes cerrado, con documentos, sin constancia de envio,
    -- y ya pasado el dia 10 del mes siguiente (art. 29). Dos de estos son
    -- causal de revocatoria (art. 34).
    (select count(*)::int from fiscal.salud_reportes() s where s.vencido);
$$;

-- El despachador del panel gana la accion 'salud_reportes'. Mismo cuerpo vivo,
-- un caso mas (el tipo de retorno no cambia, CREATE OR REPLACE basta).
CREATE OR REPLACE FUNCTION public.fn_panel_imprenta(p_accion text, p_datos jsonb DEFAULT '{}'::jsonb, p_quien text DEFAULT NULL::text, p_desde text DEFAULT NULL::text, p_yo text DEFAULT NULL::text)
 RETURNS jsonb
 LANGUAGE plpgsql
 SECURITY DEFINER
 SET search_path TO 'public', 'fiscal'
AS $function$
declare
  v jsonb;
  v_quien text := coalesce(nullif(btrim(p_quien), ''), 'sin identificar');
  v_viejo jsonb;
begin
  case p_accion
    -- ===== NUMERACION DE LA IMPRENTA (lo nuevo) =====
    when 'numeracion' then
      -- Todo lo que la imprenta necesita ver de un vistazo.
      select jsonb_build_object(
        'resumen', fiscal.resumen_numeracion(),
        'rangos', coalesce((select jsonb_agg(to_jsonb(r) order by r.desde)
                              from (select id, desde, hasta, oficio, asignado_en, anulado, nota,
                                           (hasta - desde + 1) as cuantos
                                      from fiscal.rangos_imprenta) r), '[]'::jsonb),
        'clientes', coalesce((select jsonb_agg(to_jsonb(t)) from fiscal.rangos_de_clientes() t), '[]'::jsonb)
      ) into v;
    when 'guardar_imprenta' then
      select fiscal.guardar_imprenta(
        p_datos->>'rif', p_datos->>'razon_social', p_datos->>'domicilio',
        p_datos->>'providencia', nullif(p_datos->>'providencia_fecha','')::date,
        nullif(p_datos->>'vigencia_hasta','')::date,
        coalesce((p_datos->>'autorizada')::boolean, false)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'imprenta_guardada',
        jsonb_build_object('providencia', p_datos->>'providencia',
                           'autorizada', coalesce((p_datos->>'autorizada')::boolean, false)));
    when 'cargar_rango' then
      select fiscal.cargar_rango_imprenta(
        (p_datos->>'desde')::bigint, (p_datos->>'hasta')::bigint,
        p_datos->>'oficio', p_datos->>'nota') into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'rango_cargado',
        jsonb_build_object('desde', p_datos->>'desde', 'hasta', p_datos->>'hasta',
                           'oficio', p_datos->>'oficio'));
    when 'entregar_rango' then
      select fiscal.entregar_rango(
        p_datos->>'rif', (p_datos->>'cuantos')::bigint,
        nullif(p_datos->>'rango_imprenta','')::uuid) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'rango_entregado',
        jsonb_build_object('rif', p_datos->>'rif', 'cuantos', p_datos->>'cuantos',
                           'desde', v->>'desde', 'hasta', v->>'hasta'));

    -- ===== LO QUE YA EXISTIA =====
    when 'entrar' then
      select to_jsonb(t) into v from fiscal.entrar_panel(
        p_datos->>'usuario', p_datos->>'clave', p_datos->>'desde') t;
    when 'tablero' then
      select to_jsonb(t) into v from fiscal.tablero() t;
    when 'salud_reportes' then
      -- Art. 29 num. 5 y art. 34: meses con documentos numerados y su estado
      -- de remision al SENIAT, para el aviso del panel.
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.salud_reportes() t;
    when 'clientes' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.clientes() t;
    when 'guardar_cliente' then
      select to_jsonb(fiscal.guardar_cliente(
        p_datos->>'rif', p_datos->>'razon_social', p_datos->>'domicilio',
        p_datos->>'representante', p_datos->>'cedula', p_datos->>'correo',
        p_datos->>'telefono', p_datos->>'sistema', coalesce(p_datos->>'serie', 'A'),
        coalesce((p_datos->>'rif_comprobado')::boolean, false), p_datos->>'nota')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'cliente_guardado',
        jsonb_build_object('rif', p_datos->>'rif'));
    when 'activar_cliente' then
      select to_jsonb(fiscal.activar_cliente(p_datos->>'rif', (p_datos->>'activo')::boolean)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde,
        case when (p_datos->>'activo')::boolean then 'cliente_activado' else 'cliente_suspendido' end,
        jsonb_build_object('rif', p_datos->>'rif'));
    when 'llaves' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.llaves_de(p_datos->>'rif') t;
    when 'crear_llave' then
      select to_jsonb(fiscal.crear_llave(p_datos->>'rif', p_datos->>'nombre')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'llave_creada',
        jsonb_build_object('rif', p_datos->>'rif', 'nombre', p_datos->>'nombre'));
    when 'revocar_llave' then
      select to_jsonb(fiscal.revocar_llave((p_datos->>'llave_id')::uuid)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'llave_revocada',
        jsonb_build_object('llave_id', p_datos->>'llave_id'));
    -- La pantalla llama 'relacion_mensual'/'resumen_mensual' y el despachador
    -- solo conocia 'relacion'/'resumen': la pestaña del reporte respondia
    -- "Accion desconocida" en produccion. Se aceptan los dos nombres.
    when 'relacion', 'relacion_mensual' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.relacion_mensual(p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int) t;
    when 'resumen', 'resumen_mensual' then
      select to_jsonb(t) into v from fiscal.resumen_mensual(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int) t;
    when 'marcar_enviado' then
      select to_jsonb(fiscal.marcar_enviado(
        p_datos->>'rif', (p_datos->>'anio')::int, (p_datos->>'mes')::int,
        v_quien, p_datos->>'nota')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'periodo_marcado_enviado',
        jsonb_build_object('rif', p_datos->>'rif', 'anio', p_datos->>'anio', 'mes', p_datos->>'mes'));
    when 'buscar_negocio' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.buscar_negocio(p_datos->>'texto') t;
    when 'vincular_negocio' then
      select to_jsonb(fiscal.vincular_negocio(
        (p_datos->>'restaurante_id')::uuid, p_datos->>'rif',
        coalesce(p_datos->>'serie', 'A'), v_quien)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'negocio_vinculado',
        jsonb_build_object('restaurante_id', p_datos->>'restaurante_id', 'rif', p_datos->>'rif'));
    when 'desvincular_negocio' then
      select to_jsonb(fiscal.desvincular_negocio((p_datos->>'restaurante_id')::uuid)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'negocio_desvinculado',
        jsonb_build_object('restaurante_id', p_datos->>'restaurante_id'));
    when 'negocios' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.negocios_gustito() t;
    when 'estaciones' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.estaciones_vivas() t;
    when 'cola' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.cola_por_revisar() t;
    when 'movimientos' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v
        from fiscal.movimientos_panel(coalesce((p_datos->>'limite')::int, 100)) t;
    when 'operadores' then
      select coalesce(jsonb_agg(to_jsonb(t)), '[]'::jsonb) into v from fiscal.operadores_lista() t;
    when 'guardar_operador' then
      select to_jsonb(fiscal.guardar_operador(
        p_datos->>'usuario', p_datos->>'nombre', p_datos->>'clave')) into v;
      perform fiscal.anotar_panel(v_quien, p_desde, 'operador_guardado',
        jsonb_build_object('usuario', lower(btrim(p_datos->>'usuario'))));
    when 'activar_operador' then
      select to_jsonb(fiscal.activar_operador(
        p_datos->>'usuario', (p_datos->>'activo')::boolean, p_yo)) into v;
      perform fiscal.anotar_panel(v_quien, p_desde,
        case when (p_datos->>'activo')::boolean then 'operador_activado' else 'operador_apagado' end,
        jsonb_build_object('usuario', lower(btrim(p_datos->>'usuario'))));
    else
      raise exception 'Accion desconocida';
  end case;
  return coalesce(v, 'null'::jsonb);
end;
$function$;

notify pgrst, 'reload schema';
