alter table tabelaprecoexamesusuario alter teu_alteracao type varchar(300); --[lode]

insert into atualizacoes(atu_nome) values ('v24020001_290524.sql'); --[lode]

alter table examerequisicao alter exr_motivorecoleta type varchar(500); --[lode]

insert into atualizacoes(atu_nome) values ('v24020002_100624.sql'); --[lode]

alter table geladeiras alter usr_codigo type varchar(20); --[lode]

create index xestornoloteexternoexamerequisicaofilial on loteexternoexamerequisicao(fil_kodigo, req_codigo, exa_codigo, mco_codigo, ler_datahoraestorno); --[lode]

insert into atualizacoes(atu_nome) values ('v24020003_170624.sql'); --[lode]

alter table examerequisicaosituacoes alter ers_motivorecoleta type varchar(500); --[lode]

insert into atualizacoes(atu_nome) values ('v24020004_180624.sql'); --[lode]

alter table loteexternorequisicao add leq_enviows text; --[lode]

insert into atualizacoes(atu_nome) values ('v24020005_240624.sql'); --[lode]

CREATE OR REPLACE FUNCTION calculaueps(integer, integer, integer, timestamp without time zone, numeric, numeric, character varying, integer, boolean)
 RETURNS void
 LANGUAGE plpgsql
AS $function$
declare
  filial         alias for $1;
  pcodigolancto  alias for $2;
  codigomaterial alias FOR $3;
  datalancto     alias FOR $4;
  pquantidade    alias for $5;
  pvalorunitario alias for $6;
  tipolancto     alias FOR $7;
  arredondamento alias FOR $8;
  cancelado      alias FOR $9;

  TIPO_LANCTO_ENTRADA CHARACTER VARYING := upper('E');
  TIPO_LANCTO_SAIDA CHARACTER VARYING := upper('S');
  CASAS_ARREDONDAMENTO INTEGER := 4;

  valoraux numeric(15,4);
  quantidadeaux numeric(15,4);
  valorunitarioaux numeric(15,4);
  qtdeloopaux numeric(15,4);
  saldorestanteaux numeric(15,4);
  quantidade numeric(15,4);

  auxcursor refcursor;
  auxrecord RECORD;
  auxcursorcanc refcursor;
  auxrecordcanc RECORD;

begin
  quantidade := pquantidade;

if (cancelado and upper(tipolancto) = TIPO_LANCTO_SAIDA) then --se o lancamento de saida foi cancelado
  --busca os lancamentos de entrada com saldo restante menor que a quantidade (em ordem ascendente)
    OPEN auxcursor
     for
  select *
    from (select uep_codigo as codigo
                ,uep_quantidade as qtde
                ,uep_saldorestante as saldorestante
                ,SUM(uep.uep_quantidade - uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora), lce.lce_codigo) as somatotal
                ,SUM(uep.uep_quantidade - uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora), lce.lce_codigo) - (uep.uep_quantidade - uep.uep_saldorestante) + 1 as somaanterior
            from lanctoestoqueueps uep
            join lanctoestoque lce on uep.fil_codigo = lce.fil_codigo and uep.lce_codigo = lce.lce_codigo
           where uep.fil_codigo = filial
             and uep.lce_kodigo is null
             and lce.mat_codigo = codigomaterial
             and lce.lce_datahoracancelamento is null
             and uep.uep_saldorestante < uep.uep_quantidade
             and lce.lce_tipo = TIPO_LANCTO_ENTRADA
           order by date_trunc('day', lce_datahora)
                   ,lce.lce_codigo) aux
   where quantidade >= somaanterior
      or quantidade between somaanterior and somatotal;

  loop
    fetch auxcursor into auxrecord;
    if (auxrecord is null OR quantidade <= 0) then
close auxcursor;
      exit;
    end if;

    qtdeloopaux := CASE WHEN quantidade > (auxrecord.qtde - auxrecord.saldorestante) then (auxrecord.qtde - auxrecord.saldorestante) ELSE quantidade end;
    quantidade := quantidade - qtdeloopaux;

    --atualiza os saldo restante dos lancamentos
    update lanctoestoqueueps
 set uep_saldorestante = uep_saldorestante + qtdeloopaux
     WHERE fil_codigo = filial
 and uep_codigo = auxrecord.codigo;

  end loop;

ELSIF (cancelado AND upper(tipolancto) = TIPO_LANCTO_ENTRADA) then --se o lancamento de entrada foi cancelado
  --busca os lancamentos de saida que utilizam a entrada que esta sendo cancelada (em ordem decrescente)
    OPEN auxcursorcanc 
     for
  select uep.lce_codigo as codigolancto
        ,uep.uep_quantidade as qtde
    from lanctoestoqueueps uep
    join lanctoestoque lce on uep.fil_codigo = lce.fil_codigo and uep.lce_codigo = lce.lce_codigo
   where uep.fil_codigo = filial
     and uep.lce_kodigo = pcodigolancto
     and lce.lce_datahoracancelamento is null
   order by date_trunc('day', lce_datahora) desc
           ,lce.lce_codigo desc;

  --para cada lancamento de saida, deve-se gerar uma nova entrada utilizando os proximos lancamentos de entrada (em ordem decrescente)
  loop
    fetch auxcursorcanc into auxrecordcanc;
    if (auxrecordcanc is null) then
      close auxcursorcanc;
      exit;
    end if;

      --busca os lancamentos de entrada que serao usados para dar a saida (em ordem decrescente)
      OPEN auxcursor
       for
    select *
      from (select uep.uep_codigo as codigo
                  ,uep.lce_codigo as codigolancto
                  ,uep.uep_quantidade as qtde
                  ,uep.uep_saldorestante as saldorestante
                  ,uep.uep_valorunitario as valorunitario
                  ,SUM(uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora), lce.lce_codigo) as somatotal
                  ,SUM(uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora), lce.lce_codigo) - uep.uep_saldorestante + 1 as somaanterior
              from lanctoestoqueueps uep
              join lanctoestoque lce on uep.fil_codigo = lce.fil_codigo and uep.lce_codigo = lce.lce_codigo
             where uep.fil_codigo = filial
               and uep.lce_kodigo is null
               and lce.mat_codigo = codigomaterial
               and lce.lce_datahoracancelamento is null
               and uep.uep_saldorestante > 0
               and lce.lce_tipo = TIPO_LANCTO_ENTRADA
             order by date_trunc('day', lce_datahora) desc
                     ,lce.lce_codigo desc
           ) aux
     where saldorestante < quantidade
        or quantidade between somaanterior and somatotal;

    qtdeloopaux := auxrecordcanc.qtde;
    loop
      fetch auxcursor into auxrecord;
      if (auxrecord is null OR qtdeloopaux <= 0) then
        close auxcursor;
        exit;
      end if;

      if (auxrecord.saldorestante <= qtdeloopaux) then
        valoraux := round(auxrecord.valorunitario * auxrecord.saldorestante, CASAS_ARREDONDAMENTO, arredondamento);
        quantidadeaux := auxrecord.saldorestante;
        saldorestanteaux := 0;
      else
        valoraux := round(auxrecord.valorunitario * qtdeloopaux, CASAS_ARREDONDAMENTO, arredondamento);
        quantidadeaux := qtdeloopaux;
        saldorestanteaux := auxrecord.saldorestante - qtdeloopaux;
      end if;

      valorunitarioaux := auxrecord.valorunitario;
      qtdeloopaux := qtdeloopaux - quantidadeaux;
    
      --atualiza o lancamento de entrada de origem
      update lanctoestoqueueps
         set uep_saldorestante = saldorestanteaux
       where fil_codigo = filial
         and uep_codigo = auxrecord.codigo;

      --insere o novo lancamento ueps
      insert into lanctoestoqueueps
                 (fil_codigo
                 ,uep_codigo
                 ,lce_codigo
                 ,lce_kodigo
                 ,uep_quantidade
                 ,uep_saldorestante
                 ,uep_valor
                 ,uep_valorunitario)
           values(filial
                 ,nextval('lanctoestoqueueps_' || filial || '_sequence')
                 ,auxrecordcanc.codigolancto
                 ,auxrecord.codigolancto
                 ,quantidadeaux
                 ,null
                 ,valoraux
                 ,valorunitarioaux);

    end loop;

  END LOOP;

elsif (not cancelado AND upper(tipolancto) = TIPO_LANCTO_SAIDA) then --se o lancamento for de saida
  --busca os lancamentos de entrada que serao usados para dar a saida
    OPEN auxcursor
     for
  select *
    from (select uep.uep_codigo as codigo
                ,uep.lce_codigo as codigolancto
                ,uep.uep_quantidade as qtde
                ,uep.uep_saldorestante as saldorestante
                ,uep.uep_valorunitario as valorunitario
                ,SUM(uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora) desc, lce.lce_codigo desc) as somatotal
                ,SUM(uep.uep_saldorestante) over (order by date_trunc('day', lce.lce_datahora) desc, lce.lce_codigo desc) - uep.uep_saldorestante + 1 as somaanterior
            from lanctoestoqueueps uep
            join lanctoestoque lce on uep.fil_codigo = lce.fil_codigo and uep.lce_codigo = lce.lce_codigo
           where uep.fil_codigo = filial
             and uep.lce_kodigo is null
             and lce.mat_codigo = codigomaterial
             and lce.lce_datahoracancelamento is null
             and uep.uep_saldorestante > 0
             and lce.lce_tipo = TIPO_LANCTO_ENTRADA
           order by date_trunc('day', lce_datahora) desc
                   ,lce.lce_codigo desc
         ) aux
   where saldorestante < quantidade
      or quantidade between somaanterior and somatotal;

  qtdeloopaux := quantidade;
  loop
    fetch auxcursor into auxrecord;
    if (auxrecord is null OR qtdeloopaux <= 0) then
      close auxcursor;
      exit;
    end if;

    if (auxrecord.saldorestante <= qtdeloopaux) then
      valoraux := round(auxrecord.valorunitario * auxrecord.saldorestante, CASAS_ARREDONDAMENTO, arredondamento);
      quantidadeaux := auxrecord.saldorestante;
      saldorestanteaux := 0;
    else
      valoraux := round(auxrecord.valorunitario * qtdeloopaux, CASAS_ARREDONDAMENTO, arredondamento);
      quantidadeaux := qtdeloopaux;
      saldorestanteaux := auxrecord.saldorestante - qtdeloopaux;
    end if;

    valorunitarioaux := auxrecord.valorunitario;
    qtdeloopaux := qtdeloopaux - quantidadeaux;

    --atualiza o lancamento de entrada de origem
    update lanctoestoqueueps
       set uep_saldorestante = saldorestanteaux
     where fil_codigo = filial
       and uep_codigo = auxrecord.codigo;

    --insere o novo lancamento ueps
    insert into lanctoestoqueueps
               (fil_codigo
               ,uep_codigo
               ,lce_codigo
               ,lce_kodigo
               ,uep_quantidade
               ,uep_saldorestante
               ,uep_valor
               ,uep_valorunitario)
         values(filial
               ,nextval('lanctoestoqueueps_' || filial || '_sequence')
               ,pcodigolancto
               ,auxrecord.codigolancto
               ,quantidadeaux
               ,null
               ,valoraux
               ,valorunitarioaux);

  end loop;

elsif (upper(tipolancto) = TIPO_LANCTO_ENTRADA) then --se o lancamento for de entrada
  --adiciona a entrada na tabela
  INSERT INTO lanctoestoqueueps
             (fil_codigo
             ,uep_codigo
             ,lce_codigo
             ,lce_kodigo
             ,uep_quantidade
             ,uep_saldorestante
             ,uep_valor
             ,uep_valorunitario)
       values (filial
              ,nextval('lanctoestoqueueps_' || filial || '_sequence')
              ,pcodigolancto
              ,null
              ,quantidade
              ,quantidade
              ,round(pvalorunitario * quantidade, CASAS_ARREDONDAMENTO, arredondamento)
              ,pvalorunitario);

end if;

end;
$function$
; --[lode]


insert into atualizacoes(atu_nome) values ('v24020006_250624.sql'); --[lode]