Query para extrair o KARDEX de um produto

Se você já precisou fazer alguma query para agilizar o KARDEX do Protheus, hoje iremos demonstrar um exemplo de como fazer.

Esse exemplo foi criado e gentilmente disponibilizado pelo grande Bruno Cesar Mendes Jacinto ( LinkedIn ).

O primeiro passo é criar a função no SQL Server, sendo que o nome dela é KARDEX_SQL. Ela irá receber 4 parâmetros, a data da SB9, a data inicial de movimentações, a data final de movimentações e a filial.

Abaixo o código da função completa (substitua o 99 pelo seu grupo de empresas, por exemplo, SB2990 para SB2010):

USE [NOME_DA_SUA_BASE]
GO
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE OR ALTER FUNCTION [dbo].[KARDEX_SQL]
( @dtB9 char(8), @dtade char(8), @dtate char(8), @Filial char(2) )
RETURNS table
 
AS
RETURN (
 
    SELECT B2_COD AS PRODUTO,
    B2_LOCAL AS ARMAZEM,
    B1_TIPO,
    0 AS SALDO_INI,
    0 AS ENTRADA_INI,
    0 AS ENTRADA_FIM,
    0 AS SAIDA_INI,
    0 AS SAIDA_FIM,
 
    0 AS CUSTO_INI,
    0 AS CUS_ENT_INI,
    0 AS CUS_ENT_FIM,
    0 AS CUS_SAI_INI,
    0 AS CUS_SAI_FIM
 
    FROM SB2990 (NOLOCK) AS B2
    INNER JOIN SB1990 (NOLOCK) AS B1 ON B2_COD = B1_COD
    WHERE B2.D_E_L_E_T_ != '*'
    AND B1.D_E_L_E_T_ != '*'
    GROUP BY B2_COD, B2_LOCAL, B1_TIPO
 
    UNION ALL
 
    SELECT B9_COD AS PRODUTO, 
    B9_LOCAL AS ARMAZEM,
    B1_TIPO,
    SUM(B9_QINI) AS SALDO_INI,
    0  AS ENTRADA_INI,
    0  AS ENTRADA_FIM,
    0 AS SAIDA_INI,
    0 AS SAIDA_FIM,
 
    SUM(B9_VINI1) AS CUSTO_INI,
    0 AS CUS_ENT_INI,
    0 AS CUS_ENT_FIM,
    0 AS CUS_SAI_INI,
    0 AS CUS_SAI_FIM
 
    FROM SB9990 (NOLOCK) AS B9
    INNER JOIN SB1990 (NOLOCK) AS B1 ON B9_COD = B1_COD
    WHERE B9.D_E_L_E_T_ != '*'
    AND B1.D_E_L_E_T_ != '*'
    AND B9_DATA = @dtB9
    AND B9_FILIAL = @Filial
    GROUP BY B9_COD, B9_LOCAL, B1_TIPO
 
    UNION ALL
 
    SELECT D1_COD AS PRODUTO, 
    D1_LOCAL AS ARMAZEM,
    B1_TIPO,
    0 AS SALDO_INI, 
    ROUND(SUM(CASE WHEN D1_DTDIGIT BETWEEN @dtade AND @dtate-1 THEN D1_QUANT ELSE 0 END ),2)  AS ENTRADA_INI,
    ROUND(SUM(CASE WHEN D1_DTDIGIT BETWEEN @dtade AND @dtate THEN D1_QUANT ELSE 0 END ),2)  AS ENTRADA_FIM,
    0 AS SAIDA_INI,
    0 AS SAIDA_FIM,
 
    0 AS CUSTO_INI,
    ROUND(SUM(CASE WHEN D1_DTDIGIT BETWEEN @dtade AND @dtate-1 THEN D1_CUSTO ELSE 0 END ),2) AS CUS_ENT_INI,
    ROUND(SUM(CASE WHEN D1_DTDIGIT BETWEEN @dtade AND @dtate THEN D1_CUSTO ELSE 0 END ),2)   AS CUS_ENT_FIM,
    0 AS CUS_SAI_INI,
    0 AS CUS_SAI_FIM
 
    FROM SD1990 (NOLOCK) AS D1
    INNER JOIN SF4990 (NOLOCK) AS F4 ON D1_TES = F4_CODIGO
    INNER JOIN SB1990 (NOLOCK) AS B1 ON D1_COD = B1_COD
    WHERE D1.D_E_L_E_T_ != '*'
    AND B1.D_E_L_E_T_ != '*'
    AND F4.D_E_L_E_T_ != '*'
    AND D1_DTDIGIT BETWEEN @dtade AND @dtate
    AND D1_FILIAL = @Filial
    AND D1_ORIGLAN != 'LF'
    AND F4.F4_TIPO = 'E'
    AND F4_ESTOQUE = 'S'
    GROUP BY D1_COD, D1_LOCAL, B1_TIPO
 
    UNION ALL
 
 
 
    SELECT D2_COD, 
    D2_LOCAL AS ARMAZEM,
    B1_TIPO,
    0 AS SALDO_INI,
    0 AS ENTRADA_INI,
    0 AS ENTRADA_FIM,
    ROUND(SUM(CASE WHEN D2_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D2_QUANT ELSE 0 END ),2)  AS SAIDA_INI,
    ROUND(SUM(CASE WHEN D2_EMISSAO BETWEEN @dtade AND @dtate THEN D2_QUANT ELSE 0 END ),2)   AS SAIDA_FIM,
 
    0 AS CUSTO_INI,
    0 AS CUS_ENT_INI,
    0 AS CUS_ENT_FIM,
    ROUND(SUM(CASE WHEN D2_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D2_CUSTO1 ELSE 0 END ),2) AS CUS_SAI_INI,
    ROUND(SUM(CASE WHEN D2_EMISSAO BETWEEN @dtade AND @dtate THEN D2_CUSTO1 ELSE 0 END ),2)   AS CUS_SAI_FIM
 
 
    FROM SD2990 (NOLOCK) AS D2
    INNER JOIN SF4990 (NOLOCK) AS F4 ON D2_TES = F4_CODIGO
    INNER JOIN SB1990 (NOLOCK) AS B1 ON D2_COD = B1_COD
    WHERE D2.D_E_L_E_T_ != '*'
    AND F4.D_E_L_E_T_ != '*'
    AND B1.D_E_L_E_T_ != '*'
    AND D2_EMISSAO BETWEEN @dtade AND @dtate
    AND F4.F4_TIPO = 'S'
    AND D2.D2_ORIGLAN != 'LF'
    AND D2_FILIAL = @Filial
    AND F4_ESTOQUE = 'S'
    GROUP BY D2_COD, D2_LOCAL, B1_TIPO
 
    UNION ALL
 
    SELECT D3_COD, 
    D3_LOCAL AS ARMAZEM,
    B1_TIPO,
    0 AS SALDO_INI,
    ROUND(SUM(CASE WHEN D3_TM <='499' AND D3_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D3_QUANT ELSE 0 END ),2)  AS ENTRADA_INI,
    ROUND(SUM(CASE WHEN D3_TM <='499' AND D3_EMISSAO BETWEEN @dtade AND @dtate THEN D3_QUANT ELSE 0 END ),2)  AS ENTRADA_FIM,
    ROUND(SUM(CASE WHEN D3_TM >'499' AND D3_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D3_QUANT ELSE 0 END ),2)  AS SAIDA_INI,
    ROUND(SUM(CASE WHEN D3_TM >'499' AND D3_EMISSAO BETWEEN @dtade AND @dtate THEN D3_QUANT ELSE 0 END ),2)  AS SAIDA_FIM,
 
    0 AS CUSTO_INI,
    ROUND(SUM(CASE WHEN D3_TM <='499' AND D3_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D3_CUSTO1 ELSE 0 END ),2)  AS CUS_ENT_INI,
    ROUND(SUM(CASE WHEN D3_TM <='499' AND D3_EMISSAO BETWEEN @dtade AND @dtate THEN D3_CUSTO1 ELSE 0 END ),2)  AS CUS_ENT_FIM,
    ROUND(SUM(CASE WHEN D3_TM >'499' AND D3_EMISSAO BETWEEN @dtade AND @dtate-1 THEN D3_CUSTO1 ELSE 0 END ),2)  AS CUS_SAI_INI,
    ROUND(SUM(CASE WHEN D3_TM >'499' AND D3_EMISSAO BETWEEN @dtade AND @dtate THEN D3_CUSTO1 ELSE 0 END ),2)  AS CUS_SAI_FIM
 
    FROM SD3990 (NOLOCK) AS D3
    INNER JOIN SB1990 (NOLOCK) AS B1 ON D3_COD = B1_COD
    WHERE D3.D_E_L_E_T_ != '*'
    AND B1.D_E_L_E_T_ != '*'
    AND D3_ESTORNO != 'S'
    AND D3_FILIAL = @Filial
    AND D3_EMISSAO  BETWEEN @dtade AND @dtate
    GROUP BY D3_COD, D3_LOCAL, B1_TIPO
 
)

Como o retorno da função é uma tabela, para executar, basta dar um select no resultado dela, conforme o seguinte comando:

SELECT * FROM KARDEX_SQL('20211102', '20210101', '20211231', '01');

Abaixo um print do resultado:

Exemplo de execução

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

10 Responses

  1. lucas simao da silva disse:

    Fiz uma query dessa tbm realmente agiliza muito.

  2. Alexandre Behling disse:

    Muito bom, vai ajudar bastante, obrigado Dan e Bruno por compartilhar!

  3. Dan muito bom o exemplo, gostei bastante, tenho alguns exemplos aqui de Cursor no SQL e gostaria de compartilhar contigo, para onde posso enviar se vc achar interessante, publique no seu site.

  4. Francisco disse:

    Sensacional !! ! Outro Nível de programação.

    Por acaso vocês tem uma query que cruza produto final vs consumido.

  5. Luiz Carlos disse:

    Muito útil, porém alguém teria uma versão “melhorada” que traga também as movimentações de endereçamento (SDB) ?

Deixe uma resposta para Dan Atilio (Daniel Atilio)Cancelar resposta

Terminal de Informação