

-- StoreProcedure {gravaestoque()} Versao {55} Esquema {padrao} Descricao {Grava/atualiza saldos}
-- -----------------------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION padrao.gravaestoque(integer, integer, date, boolean)
  RETURNS boolean AS
$BODY$

DECLARE
v_produto        ALIAS FOR $1;
v_filial         ALIAS FOR $2;
p_database       ALIAS FOR $3;
v_todos          ALIAS FOR $4;
v_resultado      RECORD;
r_movto          RECORD;
v_fadiv          RECORD;
r_filial         RECORD;
r_parameters     RECORD;
r_encargos       RECORD;
r_doctoPA        RECORD;
v_database       Date;
v_recno          BigInt;

v_filialConv     int4;
v_custo_Ant      NUMERIC(50,4);
v_custo_AntU     NUMERIC(50,4);
v_custo_Real     NUMERIC(50,4);
v_custo_Nf       NUMERIC(50,4);
v_custo_Dig      NUMERIC(50,4);

mcustonf         NUMERIC(50,4);
mcustoreal       NUMERIC(50,4);
mcustoDig        NUMERIC(50,4);
mValor           NUMERIC(50,4);
mValorTot        NUMERIC(50,4);
mcustoMed        NUMERIC(50,4);
mcustoMedT       NUMERIC(50,4);
msaldo           NUMERIC(50,4);
msaldo2          NUMERIC(50,4);
msaldoInv        NUMERIC(50,4);

filial integer;
filial2 integer;

controle INT8;

aCamposPed            character varying[];
aDadosPed             character varying[];

BEGIN



   IF v_todos = True THEN
      v_database = '1999-01-01'::DATE;
   ELSE
      v_database = p_database;
      v_database = v_database - 1;
   END IF;

   --Posiciona parameters
	 SELECT *
	   INTO r_parameters
	   FROM fat_parameters
	  LIMIT 1 ;

   --posiciona filial
    SELECT id, filialestoque
	  INTO r_filial
      FROM cad_filial
	 WHERE id = v_Filial;

  --posiciona encargos
    SELECT *
	  INTO r_encargos
      FROM fat_encargopadrao
	 WHERE cad_filial_id = v_Filial;


	 filial  = r_filial.id;
	 filial2 = r_filial.filialestoque;

   --Filial p/ centralizar estoque
   --IF coalesce(r_filial.filialestoque, r_parameters.fatfilialpadrao) = r_parameters.fatfilialpadrao THEN
   --   v_Filial = r_filial.filialestoque;
   --END IF;

 --acha saldo inicial
   SELECT saldo,  saldoinventario, customedio, fat_produto_id,
          customediototal, custodigitado, custoreal, custonf
     INTO v_resultado
     FROM fat_produto_movto
    WHERE (cad_filial_id  = filial OR fat_produto_movto.cad_filial_id  = filial2)
      AND fat_produto_id    = v_Produto
      AND datamovto        <= v_DataBase
    ORDER BY datamovto DESC, fat_docto_c_controle DESC
    LIMIT 1;

   -- ORDER BY DataMovto DESC, fat_docto_c_controle DESC
   IF FOUND  AND  v_resultado.fat_produto_id    = v_Produto THEN
      msaldo           = coalesce(v_resultado.saldo,0);
      v_custo_Real     = coalesce(v_resultado.custoreal,0);
      v_custo_Nf       = coalesce(v_resultado.custonf,0);
      v_custo_Dig      = coalesce(v_resultado.custodigitado,0);

      msaldoInv       = coalesce(v_resultado.saldo,0);
      v_custo_Ant     = Round(coalesce(v_resultado.saldo,0) * coalesce(v_resultado.customedio,0),4); -- Custo Medio Total
      v_custo_AntU    = Round(coalesce(v_Resultado.customedio,0),4); -- Custo Medio Unitario

   ELSE
      msaldo          = 0;
      msaldoInv       = 0;
      v_custo_Ant     = 0;
      v_custo_AntU    = 0;
      v_custo_Real    = 0;
      v_custo_Nf      = 0;
      v_custo_Dig     = 0;
   END IF;

  --acha saldo inicial saldo2
   SELECT saldo2, customedio, fat_produto_id,
          customediototal, custodigitado, custoreal, custonf
     INTO v_resultado
     FROM fat_produto_movto
    WHERE (cad_filial_id  = filial OR fat_produto_movto.cad_filial_id  = filial2)
      AND fat_produto_id  = v_Produto
      AND DataMovto      <= v_DataBase
    ORDER BY DataMovto DESC, fat_docto_c_controle DESC
    LIMIT 1;

 -- ORDER BY DataMovto DESC, fat_docto_c_controle DESC SALDO2
   IF FOUND  AND  v_resultado.fat_produto_id    = v_Produto THEN
      msaldo2         = coalesce(v_resultado.saldo2,0);
      v_custo_Real    = coalesce(v_resultado.custoreal,0);
      v_custo_Nf      = coalesce(v_resultado.custonf,0);
      v_custo_Dig     = coalesce(v_resultado.custodigitado,0);
      v_custo_Ant     = coalesce(v_resultado.saldo2,0) * coalesce(v_resultado.customedio,0);
      v_custo_AntU    = coalesce(v_Resultado.custodigitado,0);
   ELSE
      msaldo2          = 0;
   END IF;
-- atualiza registros a partir da data base

   FOR r_movto IN SELECT fat_produto_movto.fat_docto_c_controle,
                             fat_produto_movto.cad_filial_id,
                             fat_produto_movto.Devolucao,
                             fat_produto_movto.DataMovto,
                             fat_produto_movto.fat_transacao_id,
                             fat_produto_movto.natureza,
                             fat_produto_movto.atualizaestoque,
                             fat_produto_movto.alteracustomedio,
                             fat_produto_movto.alteracustoultimacompra,
                             fat_produto_movto.tipomovto,
                             fat_produto_movto.Qtde,
                             fat_produto_movto.Valorunitario,
                             fat_produto_movto.ValorTotal,
                             fat_produto_movto.customedio,
                             fat_produto_movto.customediototal,
                             fat_produto_movto.custonf,
                             fat_produto_movto.custodigitado,
                             fat_produto_movto.custoreal,
                             fat_produto_movto.custoinventario,
                             fat_produto_movto.custoinventariototal
                        FROM fat_produto_movto
                       WHERE fat_produto_movto.fat_produto_id  = v_Produto
                         AND ((fat_produto_movto.cad_filial_id = filial) OR (fat_produto_movto.cad_filial_id  = filial2))
                         AND fat_produto_movto.DataMovto      > v_DataBase
                       ORDER BY fat_produto_movto.DataMovto, fat_produto_movto.fat_docto_c_controle
                       LOOP


   -- Carrega custos, valores e qtdes
      mcustonf   = coalesce(r_movto.custoNF,0);
      mcustoDig  = coalesce(r_movto.custodigitado,0);
      mcustoreal = coalesce(r_movto.custoreal,0);
      mValor     = coalesce(r_movto.Valorunitario,0);
      mValorTot  = coalesce(r_movto.ValorTotal,0);

      IF mcustonf   = 0 THEN mcustonf   = v_custo_Nf;    END IF;
      IF mcustoDig  = 0 THEN mcustoDig  = v_custo_Dig;   END IF;
      IF mcustoreal = 0 THEN mcustoreal = v_custo_Real;  END IF;

     -- raise exception 'Carrega custos,v_Produto %, v_filial %, v_DataBase % ', v_Produto,v_filial, v_DataBase ;
      --raise exception 'dentro do for ,mValor %, mValorTot %, mcustoMed % ', mValor,mValorTot, mcustoMed ;

   -- Calculo das entradas
      IF substring(r_movto.natureza from 1 for 1) = 'E' THEN -- Entrada
         IF r_movto.AtualizaEstoque = true THEN -- Se Atualiza Estoque
           IF r_movto.alteracustomedio = true OR r_movto.TipoMovto IN ('0','8','X','Y') THEN
              IF msaldoInv < 0 THEN -- se o saldo inventario estiver negativo
                 mValorTot = Coalesce(r_movto.custoinventario,0) * (r_movto.Qtde + msaldoInv);
                 IF r_movto.Qtde = 0 THEN
                    mValor    = 0;
                 ELSE
                    IF r_movto.TipoMovto IN ('Y') THEN -- Reavaliacao de Custo
                       mvalor = coalesce(r_movto.custoNF,0);
                    ELSE
                       mValor    = Round((coalesce(r_movto.custoinventario,0) * (coalesce(r_movto.Qtde + msaldoInv,0))) / r_movto.Qtde,6);
                    END IF;
                 END IF;
              ELSE
                 IF r_movto.Devolucao = true THEN
                    IF msaldoInv = 0 THEN
                       mValor    = 0;
                       mValorTot = 0;
                    ELSE
                       mValor    = Round(v_custo_Ant / msaldoInv,6);
                       mValorTot = v_custo_Ant / msaldoInv * r_movto.Qtde;
                    END IF;
                 Else
                    IF r_movto.TipoMovto IN ('Y') THEN -- Reavaliacao de Custo
                       	  mValor    = coalesce(r_movto.custoNF,0);
                          mValorTot = coalesce(r_movto.custoNF,0);
                    ELSE
                         mValorTot = coalesce(r_movto.custodigitado,0) *  r_movto.Qtde;
                         mValor    = coalesce(r_movto.custodigitado,0);
                    END IF;
                 END IF;
              END IF;
           ELSE
              IF msaldoInv = 0 THEN
                 mValorTot = 0;
                 mValor    = 0;
              ELSE
                 mValorTot = Trunc(v_custo_Ant / msaldoInv,2) * r_movto.Qtde;
                 mValor    = Round(v_custo_Ant / msaldoInv,6);
              END IF;
           END IF;

           mcustoMedT = v_custo_Ant + mValorTot;

           IF r_movto.TipoMovto IN ('X') THEN -- Contagem/Acerto de estoque
           		msaldo     = r_movto.Qtde;
           		msaldo2    = r_movto.Qtde;
           		IF r_movto.alteracustomedio = true THEN
           	       mcustoMedT = Round(coalesce(r_movto.Valorunitario,0) *  r_movto.Qtde,4);
           	    END IF;
           ELSE
           	   IF r_movto.TipoMovto IN ('Y') THEN -- Reavaliacao de Custo
           	       mcustoMedT = Round(coalesce(r_movto.Valorunitario,0) *  r_movto.Qtde,4);
               ELSE
           		   msaldo  = Round(coalesce(msaldo,0)  + r_movto.Qtde,4);
           		   msaldo2 = Round(coalesce(msaldo2,0) + r_movto.Qtde,4);
           	   END IF;

		   END IF;
          --raise exception 'dentro do for ,msaldo %, msaldo2 %, mcustoMed % ', msaldo,msaldo2, mcustoMed ;


            IF r_movto.TipoMovto IN ('0','8','X','Y') THEN -- normal-entregaFutura-Contagem/acerto-saldoInicial
              IF r_movto.TipoMovto IN ('X') THEN -- Contagem/Acerto de estoque
           	  	   msaldoInv     = r_movto.Qtde;
           	  	   IF r_movto.alteracustomedio = true THEN
           	  	       mcustoMedT = Round(coalesce(r_movto.Valorunitario,0) *  r_movto.Qtde,4);
           	  	      v_custo_Ant  = mValorTot;
           	  	   END IF;
              ELSE
                IF r_movto.TipoMovto IN ('Y') THEN -- Reavaliacao de Custo
           	       mcustoMedT = Round(coalesce(r_movto.Valorunitario,0) *  r_movto.Qtde,4);
           	       msaldoInv = Round(coalesce(msaldoInv,0),4);
                ELSE
           		   msaldoInv = Round(coalesce(msaldoInv,0) + r_movto.Qtde,4);
           		END IF;
		      END IF;

            --
            -- Acha Ultimo custo Medio para acertar o caso da devolucao - ou se o customedio estiver zerado
            --
              IF mcustoMedT = 0 AND msaldoInv <> 0 THEN
                 SELECT customedio
                   INTO mcustoMedT
                   FROM fat_produto_movto
                  WHERE (cad_filial_id  = filial OR fat_produto_movto.cad_filial_id  = filial2)
                    AND fat_produto_id  = v_Produto
                    AND DataMovto      <= r_movto.DataMovto
                    AND customedio     <> 0
                  ORDER BY DataMovto DESC, fat_docto_c_controle DESC
                   LIMIT 1;

                  IF NOT FOUND THEN
                     mcustoMedT = 0;
                  ELSE
                     IF coalesce(msaldoInv,0) > 0 THEN
                        --RAISE NOTICE '>>>>>> CALCULO mcustoMedT = Round(mcustoMedT * coalesce(msaldoInv,0),4); (-> % =Round( % * %,4) <-)', mcustoMedT, mcustoMedT,  coalesce(msaldoInv,0) ;
                        --mcustoMedT = Round(mcustoMedT * coalesce(msaldoInv,0),4);
                        mcustoMedT = Round(msaldoInv * coalesce(r_movto.custodigitado,0),4);
                     ELSE
                        mcustoMedT = 0;
                     END IF;

                  END IF;
              END IF;
            ELSE
               mcustoMedT = v_custo_Ant;
            END IF;

            IF r_movto.alteracustoultimacompra = false THEN
               mcustoDig  = v_custo_Dig;
               mcustonf   = v_custo_Nf;
               mcustoreal = v_custo_Real;
            END IF;
         ELSE
            If msaldoInv = 0 THEN
               mValorTot = 0;
               mValor    = 0;
            ELSE
               mValorTot  = Round( v_custo_Ant / coalesce(msaldoInv,0),2);

               If r_movto.Qtde = 0 THEN
                  mValor  = 0;
               ELSE
                  mValor  = Round((v_custo_Ant / msaldoInv) / r_movto.Qtde,6);
               END IF;
            END IF;

            mcustoMedT = v_custo_Ant;

            IF r_movto.alteracustoultimacompra = false THEN
               mcustoDig  = v_custo_Dig;
               mcustonf   = v_custo_Nf;
               mcustoreal = v_custo_Real;
            END IF;
         END IF;
      ELSEIF substring(r_movto.natureza from 1 for 1) = 'S' THEN
    	 IF r_movto.TipoMovto IN ('X') THEN
    	 	 RAISE EXCEPTION 'Nao foi possivel persistir estoque. TIPO DE MOVIMENTO X-Contagem/Acerto de estoquo e invalido movimentacoes de natureza SAIDA. (NATUREZA:%) (TIPO MOVTO ESTOQUE:%',r_movto.natureza, r_movto.TipoMovto;
    	 END IF;

    	 IF r_movto.TipoMovto IN ('Y') THEN
    	 	 RAISE EXCEPTION 'Nao foi possivel persistir estoque. TIPO DE MOVIMENTO Y-Reavalicao de Custo e invalido movimentacoes de natureza SAIDA. (NATUREZA:%) (TIPO MOVTO ESTOQUE:%',r_movto.natureza, r_movto.TipoMovto;
    	 END IF;

         IF r_movto.AtualizaEstoque = true THEN
           If r_movto.alteracustomedio = true AND r_movto.Devolucao = true THEN
           /* Se for para alterar custo medio e for devolucao de compra
              O custo deve ser gravado o mesmo na nota de compra.
              Isso para poder fechar o valor corretamente com a
              contabilidade. */

              mValorTot = r_movto.custoinventariototal;
           ELSE
              IF r_movto.alteracustomedio = true THEN -- Mater o valor digitado no item do documento
                 IF msaldoInv = 0 THEN
                    mValorTot = 0;
                 ELSE

                    mValorTot = Round( (v_custo_Ant / msaldoInv) * r_movto.Qtde,4);

                 END IF;
              END IF;
           END IF;


           msaldo     = Round(coalesce(msaldo,0)  - r_movto.Qtde,4);
           msaldo2    = Round(coalesce(msaldo2,0) - r_movto.Qtde,4);

           mcustoDig  = v_custo_Dig;
           mcustonf   = v_custo_Nf;
           mcustoreal = v_custo_Real;
           mcustoMedT = v_custo_Ant;
           mcustoMed  = v_custo_AntU;

            IF r_movto.TipoMovto IN ('0','8') THEN -- normal - entrega futura - saldo Inicial
               msaldoInv = Round(coalesce(msaldoInv,0) - r_movto.Qtde,4);

               IF msaldoInv = 0 THEN
                  mValorTot  = v_custo_Ant;
                  mcustoMedT = 0;
               END IF;
            END IF;
         ELSE
            IF msaldoInv = 0 THEN
               mValorTot = 0;
            ELSE
               mValorTot  = v_custo_Ant / msaldoInv;
            END IF;

            mcustoMedT = v_custo_Ant;
            mcustoMed  = v_custo_AntU;
            mcustoDig  = v_custo_Dig;
            mcustonf   = v_custo_Nf;
            mcustoreal = v_custo_Real;
         END IF;

      /* Se for lancamento de saida e usar custo medio tem que gravar no
         campo Valor exatamente o valor do custo medio unitario do
         lancamento anterior do produto para nao ter diferencas de
         centavos.*/
         mValor = v_custo_AntU;
      END IF;

      IF msaldo = 0 THEN
         mcustoMed = 0;
      ELSE
          IF r_movto.alteracustomedio = true THEN
             mcustoMed = Trunc(coalesce(mcustoMedT,0) / coalesce(msaldo,0),4);
          END IF;
      END IF;


      IF msaldoInv <= 0 THEN
         mcustoMed  = 0;
         mcustoMedT = 0;
      END IF;

      v_custo_Real    = coalesce(mcustoreal,0);
      v_custo_Nf      = coalesce(mcustonf,0);
      v_custo_Dig     = coalesce(mcustoDig,0);

      msaldoInv       = Round(coalesce(msaldo,0),4);
      v_custo_Ant     = Round(coalesce(msaldo,0) * coalesce(mcustoMed,0),6);
      v_custo_AntU    = Round(coalesce(mcustoMed,0),6);

      controle = r_movto.fat_docto_c_controle;

	--RAISE NOTICE 'GRAVA SALDO - CONTROLE: (-> % <-) - PRODUTO: (-> % <-)  >>>>>> msaldo2= %, msaldoInv= %,mcustonf = %, mcustoDig=%, mcustoreal=%, mcustoMed= %, mcustoMedT= %',r_movto.fat_docto_c_controle,v_produto, msaldo2,msaldoInv, mcustonf,mcustoDig,mcustoreal,mcustoMed, mcustoMedT   ;

   -- Atualizar fat_produto_movto
      UPDATE fat_produto_movto
         SET saldo             = msaldo,
	         saldo2            = msaldo2,
	         saldoinventario   = msaldoInv,
	         custonf           = mcustonf,
	         custodigitado     = mcustoDig,
	         custoreal         = mcustoreal,
	         customedio        = mcustoMed,
	         customediototal   = mcustoMedT
	    WHERE fat_docto_c_controle  = r_movto.fat_docto_c_controle
	      AND fat_produto_id   = v_produto
	      AND cad_filial_id     = r_movto.cad_filial_id;

	--Atualiza movto e produto atendido
    IF r_movto.TipoMovto IN ('2','4','6','8', '9', 'M') THEN
       -- checa existencia de produtos atendido
       SELECT fat_produto_id, fat_docto_pc_controle, fat_docto_c_controle,qtde
         INTO r_doctoPA
         FROM fat_docto_pa
        WHERE fat_docto_c_controle = r_movto.fat_docto_c_controle
          AND fat_produto_id       = v_produto;


       IF FOUND  THEN
	      UPDATE fat_docto_pa
             SET fat_produto_id          = v_produto,
                 fat_docto_c_controle    = r_movto.fat_docto_c_controle,
			     qtde                    = r_movto.qtde
           WHERE fat_produto_id          = v_produto
             AND fat_docto_c_controle    = r_movto.fat_docto_c_controle;
	   END IF;
	ELSE
	   -- DELETE
	   IF r_movto.TipoMovto IN ('0','X','Y') THEN

	        --  DELETA ITENS ATENDIDO
	        DELETE FROM fat_docto_pa WHERE fat_docto_pc_controle = r_movto.fat_docto_c_controle;

	        --  DELETA ITENS
	        DELETE FROM fat_docto_pi  WHERE fat_docto_pc_fat_docto_c_controle = r_movto.fat_docto_c_controle;

	        --  DELETA PEDIDO PENDENTE
	        DELETE FROM fat_docto_pc  WHERE fat_docto_c_controle = r_movto.fat_docto_c_controle;

       END IF;
    END IF;


   END LOOP;

   -- raise exception 'depois do for ,mValor %, mValorTot %, mcustoMed % ', mValor,mValorTot, mcustoMed ;
     -- Atualiza saldos e custo Medio no fat_produto_saldo

	   If msaldoInv = 0 THEN
	      mcustoMedT = 0;
	      mcustoMed  = 0;
	   ElSE
	      mcustoMed  = Trunc(v_custo_Ant / msaldoInv, 6);
	      mcustoMedT = v_custo_Ant;
	   END IF;

	   UPDATE fat_produto_saldo
	      SET saldo            = msaldo,
		      saldo2           = msaldo2,
		      saldoinventario  = msaldoInv,
		      custonf          = mcustonf,
		      custodigitado    = mcustoDig,
		      custoreal        = mcustoreal,
		      customedio       = mcustoMed,
		      customediototal  = mcustoMedT
		WHERE fat_produto_id   = v_produto
		  AND (cad_filial_id   = filial OR cad_filial_id  = filial2) ;
--	   	  AND fat_almoxarifado_id = 0 OR fat_almoxarifado_id IS NULL;


   RETURN NULL;
END;
$BODY$
   LANGUAGE 'plpgsql' VOLATILE;