Desvendando o Fluxo de Compras no Protheus: Uma Análise Completa com SQL e UNION ALL

Profissionais que trabalham com o ERP Protheus sabem que a análise completa de um processo, como o de Compras, pode ser desafiadora. Frequentemente, precisamos cruzar informações de diversas tabelas (SC1, SC7, SD1, SA2, SBM, SDE, etc.) para ter uma visão 360 graus. Estou trabalhando atualmente em um relatório desse tipo e achei que seria interessante falar sobre esse desafio comum que surge ao tentar mapear o fluxo completo do processo de Compras no Protheus desde a Solicitação de Compra (SC1) até a Nota Fiscal de Entrada (SD1). A abordagem padrão com LEFT JOIN partindo da SC1 funciona bem para documentos vinculados, mas o que acontece com:

  • Pedidos de Compra (SC7) que foram gerados sem uma Solicitação de Compra?

  • Itens de Nota Fiscal (SD1) que foram lançados sem um Pedido de Compra associado?

Esses documentos "órfãos" não aparecem em relatórios que partem apenas da SC1. Para ter uma análise verdadeiramente completa, precisamos incluir esses cenários. É aqui que o poderoso operador UNION ALL do SQL se torna nosso grande aliado.

Claro, geralmente esses casos não fazem parte do "processo de negócio" padrão, mas sabemos que muitas situações no dia a dia do negócio fogem dos procedimentos convencionais e, no final das contas, é ao departamente de TI que recorrem para tentar colocar alguma ordem no Caos e conseguir visibilidade sobre as informações que o sistema armazena.

Este post é para você, analista de negócios, programador ADVPL ou profissional de TI que busca ir além na análise de dados do Protheus. Vamos explorar como usar o UNION ALL para criar uma query que combine dados de Solicitações, Pedidos sem Solicitação e Notas sem Pedido em um único resultado.

Um analista de dados com visual punk, trabalhando em frente a vários monitores em um ambiente futurista que mistura elementos de Cyberpunk e Matrix, com luzes neon e código digital verde.


O Desafio do LEFT JOIN Partindo da SC1

Sua query original, como muitas, provavelmente começa selecionando da tabela de Solicitações de Compra (SC1) e usando LEFT JOIN para trazer informações de Pedidos (SC7) e Notas (SD1) quando elas existem:

FROM SC1120 SC1
LEFT JOIN SC7120 SC7 ON SC7.C7_NUMSC = SC1.C1_NUM AND ...
LEFT JOIN SD1120 SD1 ON SD1.D1_PEDIDO = SC7.C7_NUM AND ...

Essa estrutura é excelente para ver o que aconteceu a partir de uma Solicitação. Mas se um Pedido (SC7) foi incluído diretamente, sem preencher os campos C7_NUMSC e C7_ITEMSC, ele simplesmente não será encontrado ao partir da SC1. Da mesma forma, uma Nota Fiscal (SD1) lançada sem um Pedido associado (campos D1_PEDIDO e D1_ITEMPC vazios) não aparecerá se a busca depender da ligação com SC7.

A Solução: Combinando Cenários com UNION ALL

Para resolver isso, podemos construir a análise em torno das três possíveis "chaves" principais que você deseja visualizar:

  1. Registros que possuem uma Solicitação de Compra (SC1).

  2. Registros que possuem um Pedido de Compra (SC7), mas não uma Solicitação de Compra (SC1).

  3. Registros que possuem um Item de Nota Fiscal (SD1), mas não um Pedido de Compra (SC7).

O UNION ALL nos permite executar uma query para cada um desses cenários e combinar todos os resultados em uma única lista.

Regra Crucial do UNION ALL: Todas as queries que você unir devem retornar o mesmo número de colunas, na mesma ordem, com os mesmos nomes (aliases) e tipos de dados compatíveis. Onde um campo não existe para um determinado cenário, você deve selecionar NULL ou um valor padrão compatível.

Diagrama do fluxo da query para analisar os documentos relacionados ao processo de compras

Construindo a Query Combinada

Vamos usar a query final que discutimos como exemplo. Ela é dividida em três partes (SELECTs), unidas por UNION ALL.

Observação Importante sobre Protheus e SQL: Em sistemas como o Protheus, campos de chave estrangeira que não possuem um vínculo correspondente frequentemente são preenchidos com espaços em branco em vez de NULLs verdadeiros. Nossas condições WHERE precisarão levar isso em conta.

tela do PLSQL, exibindo um trecho da query discutida

Aqui está um exemplo dessa query:


  
  -- Consulta combinada para obter SC1, SC7 sem SC1, e SD1 sem SC7

  -- Os resultados são unidos usando UNION ALL.

  -- Cada SELECT deve ter o mesmo número, ordem e tipo de colunas.

  -- BRANCH 1: Registros que possuem SC1 como base (similar à sua query original)

  SELECT

     'SC' AS TIPO_REGISTRO,

      -- Subquery para APROVADOR (depende de SC7)

      (SELECT CR_USER

       FROM SCR120 CRSUB2

       WHERE CR_NIVEL = (SELECT MAX(CR_NIVEL)

                         FROM SCR120 CRSUB

                         WHERE (SC7.C7_CONAPRO = 'L' OR SC7.C7_CONAPRO IS NULL) -- Adicionado IS NULL check para garantir que a subquery não quebre se SC7 for NULL

                         AND CRSUB.CR_NUM = SC7.C7_NUM

                         AND CRSUB.D_E_L_E_T_ = ' '

                         AND CRSUB.CR_STATUS = '03'

                         AND CRSUB.CR_FILIAL = SC7.C7_FILIAL)

       AND CRSUB2.CR_STATUS = '03'

       AND CRSUB2.CR_NUM = SC7.C7_NUM

       AND CRSUB2.CR_FILIAL = SC7.C7_FILIAL

       AND CRSUB2.D_E_L_E_T_ = ' ') AS APROVADOR,

      -- Subquery para DATA_APROV (depende de SC7)

      (SELECT CR_DATALIB

       FROM SCR120 CRSUB2

       WHERE CR_NIVEL = (SELECT MAX(CR_NIVEL)

                         FROM SCR120 CRSUB

                         WHERE (SC7.C7_CONAPRO = 'L' OR SC7.C7_CONAPRO IS NULL) -- Adicionado IS NULL check

                         AND CRSUB.CR_NUM = SC7.C7_NUM

                         AND CRSUB.D_E_L_E_T_ = ' '

                         AND CRSUB.CR_STATUS = '03'

                         AND CRSUB.CR_FILIAL = SC7.C7_FILIAL)

       AND CRSUB2.CR_STATUS = '03'

       AND CRSUB2.CR_NUM = SC7.C7_NUM

       AND CRSUB2.CR_FILIAL = SC7.C7_FILIAL

       AND CRSUB2.D_E_L_E_T_ = ' ') AS DATA_APROV,

      SC1.C1_FILIAL, SC7.C7_FILIAL, SD1.D1_FILIAL,

      SC1.C1_NUM, SC1.C1_ITEM,

      SC7.C7_NUM, SC7.C7_ITEM, SC7.C7_NUMSC, SC7.C7_ITEMSC, SC7.C7_EMISSAO, SC7.C7_FORNECE, SC7.C7_LOJA, SA2.A2_NOME,

      SD1.D1_DOC, SD1.D1_DTDIGIT, SD1.D1_SERIE, SD1.D1_FORNECE, SD1.D1_LOJA,

      SC7.C7_RESIDUO,

      SB1.B1_GRUPO, SBM.BM_DESC,

      SC1.C1_PRODUTO, SC1.C1_DESCRI, SB1.B1_ESPECIF, SC1.C1_UM, SC1.C1_SEGUM, SC1.C1_EMISSAO, SC1.C1_PEDIDO, SC1.C1_ITEMPED, SC1.C1_SOLICIT, SC1.C1_QUJE, SC1.C1_QUANT,

      SC7.C7_QUANT, SC7.C7_PRECO, SC7.C7_TOTAL,

      SD1.D1_VUNIT, SD1.D1_TOTAL,

      SC7.C7_CC, SC7.C7_ITEMCTA, SC7.C7_CONTA,

      SD1.D1_CC, SD1.D1_ITEMCTA, SD1.D1_CONTA,

      SC7.C7_TIPO,

      CASE WHEN SC7.C7_RATEIO = '1' THEN 'SIM' WHEN SC7.C7_RATEIO = '2' THEN 'NAO' ELSE SC7.C7_RATEIO END AS C7_RATEIO,

      CASE WHEN SD1.D1_RATEIO = '1' THEN 'SIM' WHEN SD1.D1_RATEIO = '2' THEN 'NAO' ELSE SD1.D1_RATEIO END AS D1_RATEIO,

      -- Selecionando D1_VUNIT e D1_TOTAL novamente com aliases distintos para consistência com a lista original

      SD1.D1_VUNIT AS D1_VUNIT_SEL, SD1.D1_TOTAL AS D1_TOTAL_SEL,

      SDE.DE_CC, SDE.DE_CONTA, SDE.DE_DOC, SDE.DE_FILIAL, SDE.DE_PERC, SDE.DE_FORNECE,

      SDE.DE_LOJA, -- Primeira menção de DE_LOJA

      SDE.DE_ITEM, SDE.DE_ITEMCTA, SDE.DE_ITEMNF,

      SDE.DE_LOJA AS DE_LOJA_ALIAS, -- Segunda menção de DE_LOJA com alias

      SDE.DE_SERIE,

      (SD1.D1_VUNIT * (SDE.DE_PERC/100)) AS VAL_UNIT_RAT,

      (SD1.D1_TOTAL * (SDE.DE_PERC/100)) AS VAL_TOT_RAT,

      CASE WHEN SC7.C7_CONAPRO = 'B' THEN 'Ñ APROVADO' WHEN SC7.C7_CONAPRO = 'L' THEN 'APROVADO' WHEN SC7.C7_CONAPRO = 'R' THEN 'ELIMINADO POR RESIDUO' ELSE SC7.C7_CONAPRO END AS C7_CONAPRO, -- Adicionado ELSE

      SC1.C1_SOLICIT AS C1_SOLICIT_SEL, -- C1_SOLICIT selecionado uma vez com alias

      SC1.C1_OBS, SC7.C7_OBS,

      SC1.C1_EMISSAO, SC7.C7_EMISSAO, SD1.D1_EMISSAO, SC1.C1_DTAPRO

  FROM SC1120 SC1

  LEFT JOIN SB1010 SB1 ON SB1.B1_COD = SC1.C1_PRODUTO AND SB1.D_E_L_E_T_ = ' '

  LEFT JOIN SBM010 SBM ON SBM.BM_GRUPO = SB1.B1_GRUPO AND SBM.D_E_L_E_T_ = ' '

  LEFT JOIN SC7120 SC7 ON SC7.C7_NUMSC = SC1.C1_NUM AND SC7.C7_ITEMSC = SC1.C1_ITEM AND SC7.C7_FILIAL = SC1.C1_FILIAL AND SC7.D_E_L_E_T_ = ' '

  LEFT JOIN SA2120 SA2 ON SA2.A2_COD = SC7.C7_FORNECE AND SA2.A2_LOJA = SC7.C7_LOJA AND SA2.D_E_L_E_T_ = ' '

  LEFT JOIN SD1120 SD1 ON SD1.D1_PEDIDO = SC7.C7_NUM AND SD1.D1_ITEMPC = SC7.C7_ITEM AND SD1.D1_FILIAL = SC7.C7_FILIAL AND SD1.D_E_L_E_T_ = ' '

  LEFT JOIN SDE120 SDE ON SDE.DE_DOC = SD1.D1_DOC AND SDE.DE_SERIE = SD1.D1_SERIE AND SDE.DE_FILIAL = SD1.D1_FILIAL AND SDE.DE_FORNECE = SD1.D1_FORNECE AND SDE.DE_LOJA = SD1.D1_LOJA AND SDE.DE_ITEMNF = SD1.D1_ITEM AND SDE.D_E_L_E_T_ = ' '

  WHERE SC1.D_E_L_E_T_ = ' '

  AND SC1.C1_FILIAL >= '  ' AND SC1.C1_FILIAL <= 'ZZ'

  AND SC1.C1_EMISSAO >= '20250201' AND SC1.C1_EMISSAO <= '20250424'

  AND SC1.C1_NUM >= '      ' AND SC1.C1_NUM <= '999999'

  AND SC1.C1_PRODUTO >= '                ' AND SC1.C1_PRODUTO <= 'ZZZZZZZZZZZZZZZ'

  AND SC1.C1_ITEMCTA >= '         ' AND SC1.C1_ITEMCTA <= 'ZZZZZZZZZ'

  AND SC1.C1_CC >= '         ' AND SC1.C1_CC <= 'ZZZZZZZZZ'

  AND SC7.C7_TIPO <> 3 -- Aplica filtro C7_TIPO apenas se SC7 existir

  UNION ALL

  -- BRANCH 2: Registros que possuem SC7 como base, mas NÃO possuem SC1 correspondente

  SELECT

     'PC/AE' AS TIPO_REGISTRO,

      -- Subquery para APROVADOR (depende de SC7)

      (SELECT CR_USER

       FROM SCR120 CRSUB2

       WHERE CR_NIVEL = (SELECT MAX(CR_NIVEL)

                         FROM SCR120 CRSUB

                         WHERE SC7.C7_CONAPRO = 'L' -- SC7 é a base, então C7_CONAPRO não será NULL aqui

                         AND CRSUB.CR_NUM = SC7.C7_NUM

                         AND CRSUB.D_E_L_E_T_ = ' '

                         AND CRSUB.CR_STATUS = '03'

                         AND CRSUB.CR_FILIAL = SC7.C7_FILIAL)

       AND CRSUB2.CR_STATUS = '03'

       AND CRSUB2.CR_NUM = SC7.C7_NUM

       AND CRSUB2.CR_FILIAL = SC7.C7_FILIAL

       AND CRSUB2.D_E_L_E_T_ = ' ') AS APROVADOR,

      -- Subquery para DATA_APROV (depende de SC7)

      (SELECT CR_DATALIB

       FROM SCR120 CRSUB2

       WHERE CR_NIVEL = (SELECT MAX(CR_NIVEL)

                         FROM SCR120 CRSUB

                         WHERE SC7.C7_CONAPRO = 'L' -- SC7 é a base

                         AND CRSUB.CR_NUM = SC7.C7_NUM

                         AND CRSUB.D_E_L_E_T_ = ' '

                         AND CRSUB.CR_STATUS = '03'

                         AND CRSUB.CR_FILIAL = SC7.C7_FILIAL)

       AND CRSUB2.CR_STATUS = '03'

       AND CRSUB2.CR_NUM = SC7.C7_NUM

       AND CRSUB2.CR_FILIAL = SC7.C7_FILIAL

       AND CRSUB2.D_E_L_E_T_ = ' ') AS DATA_APROV,

      NULL AS C1_FILIAL, SC7.C7_FILIAL, SD1.D1_FILIAL, -- C1_FILIAL é NULL

      NULL AS C1_NUM, NULL AS C1_ITEM, -- C1 campos são NULL

      SC7.C7_NUM, SC7.C7_ITEM, SC7.C7_NUMSC, SC7.C7_ITEMSC, SC7.C7_EMISSAO, SC7.C7_FORNECE, SC7.C7_LOJA, SA2.A2_NOME,

      SD1.D1_DOC, SD1.D1_DTDIGIT, SD1.D1_SERIE, SD1.D1_FORNECE, SD1.D1_LOJA,

      SC7.C7_RESIDUO,

      NULL AS B1_GRUPO, NULL AS BM_DESC, -- SB1/SBM são NULL (ligados via SC1)

      NULL AS C1_PRODUTO, NULL AS C1_DESCRI, NULL AS B1_ESPECIF, NULL AS C1_UM, NULL AS C1_SEGUM, NULL AS C1_EMISSAO, NULL AS C1_PEDIDO, NULL AS C1_ITEMPED, NULL AS C1_SOLICIT, NULL AS C1_QUJE, NULL AS C1_QUANT, -- C1/SB1 campos são NULL

      SC7.C7_QUANT, SC7.C7_PRECO, SC7.C7_TOTAL,

      SD1.D1_VUNIT, SD1.D1_TOTAL,

      SC7.C7_CC, SC7.C7_ITEMCTA, SC7.C7_CONTA,

      SD1.D1_CC, SD1.D1_ITEMCTA, SD1.D1_CONTA,

      SC7.C7_TIPO,

      CASE WHEN SC7.C7_RATEIO = '1' THEN 'SIM' WHEN SC7.C7_RATEIO = '2' THEN 'NAO' ELSE SC7.C7_RATEIO END AS C7_RATEIO,

      CASE WHEN SD1.D1_RATEIO = '1' THEN 'SIM' WHEN SD1.D1_RATEIO = '2' THEN 'NAO' ELSE SD1.D1_RATEIO END AS D1_RATEIO,

      SD1.D1_VUNIT AS D1_VUNIT_SEL, SD1.D1_TOTAL AS D1_TOTAL_SEL,

      SDE.DE_CC, SDE.DE_CONTA, SDE.DE_DOC, SDE.DE_FILIAL, SDE.DE_PERC, SDE.DE_FORNECE,

      SDE.DE_LOJA, -- Primeira menção de DE_LOJA

      SDE.DE_ITEM, SDE.DE_ITEMCTA, SDE.DE_ITEMNF,

      SDE.DE_LOJA AS DE_LOJA_ALIAS, -- Segunda menção de DE_LOJA com alias

      SDE.DE_SERIE,

      (SD1.D1_VUNIT * (SDE.DE_PERC/100)) AS VAL_UNIT_RAT,

      (SD1.D1_TOTAL * (SDE.DE_PERC/100)) AS VAL_TOT_RAT,

      CASE WHEN SC7.C7_CONAPRO = 'B' THEN 'Ñ APROVADO' WHEN SC7.C7_CONAPRO = 'L' THEN 'APROVADO' WHEN SC7.C7_CONAPRO = 'R' THEN 'ELIMINADO POR RESIDUO' ELSE SC7.C7_CONAPRO END AS C7_CONAPRO, -- Adicionado ELSE

      NULL AS C1_SOLICIT_SEL, -- C1_SOLICIT é NULL

      NULL AS C1_OBS, SC7.C7_OBS, -- C1_OBS é NULL

      NULL AS C1_EMISSAO, SC7.C7_EMISSAO, SD1.D1_EMISSAO, NULL AS C1_DTAPRO -- C1 datas são NULL

  FROM SC7120 SC7

  LEFT JOIN SA2120 SA2 ON SA2.A2_COD = SC7.C7_FORNECE AND SA2.A2_LOJA = SC7.C7_LOJA AND SA2.D_E_L_E_T_ = ' '

  LEFT JOIN SD1120 SD1 ON SD1.D1_PEDIDO = SC7.C7_NUM AND SD1.D1_ITEMPC = SC7.C7_ITEM AND SD1.D1_FILIAL = SC7.C7_FILIAL AND SD1.D_E_L_E_T_ = ' '

  LEFT JOIN SDE120 SDE ON SDE.DE_DOC = SD1.D1_DOC AND SDE.DE_SERIE = SD1.D1_SERIE AND SDE.DE_FILIAL = SD1.D1_FILIAL AND SDE.DE_FORNECE = SD1.D1_FORNECE AND SDE.DE_LOJA = SD1.D1_LOJA AND SDE.DE_ITEMNF = SD1.D1_ITEM AND SDE.D_E_L_E_T_ = ' '

  WHERE SC7.D_E_L_E_T_ = ' '

  AND SC7.C7_NUMSC = ' ' -- ESSENCIAL: Garante que NÃO há vínculo com SC1

  AND SC7.C7_ITEMSC = ' ' -- ESSENCIAL: Garante que NÃO há vínculo com SC1

  AND SC7.C7_FILIAL >= '  ' AND SC7.C7_FILIAL <= 'ZZ' -- Filtro de filial na base

  AND SC7.C7_EMISSAO >= '20250201' AND SC7.C7_EMISSAO <= '20250424' -- Filtro de data na base

  AND SC7.C7_NUM >= '      ' AND SC7.C7_NUM <= '999999' -- Filtro de número na base

  AND SC7.C7_TIPO <> 3

  UNION ALL

  -- BRANCH 3: Registros que possuem SD1 como base, mas NÃO possuem SC7 correspondente

  SELECT

     'NF' AS TIPO_REGISTRO,

      NULL AS APROVADOR, -- SC7 não existe, então NULL

      NULL AS DATA_APROV, -- SC7 não existe, então NULL

      NULL AS C1_FILIAL, NULL AS C7_FILIAL, SD1.D1_FILIAL, -- C1 e C7 filiais são NULL

      NULL AS C1_NUM, NULL AS C1_ITEM, -- C1 campos são NULL

      NULL AS C7_NUM, NULL AS C7_ITEM, NULL AS C7_NUMSC, NULL AS C7_ITEMSC, NULL AS C7_EMISSAO, NULL AS C7_FORNECE, NULL AS C7_LOJA, NULL AS A2_NOME, -- C7 e SA2 não existem, então NULL

      SD1.D1_DOC, SD1.D1_DTDIGIT, SD1.D1_SERIE, SD1.D1_FORNECE, SD1.D1_LOJA,

      NULL AS C7_RESIDUO, -- SC7 não existe, então NULL

      NULL AS B1_GRUPO, NULL AS BM_DESC, -- SB1/SBM não existem (ligados via SC1)

      NULL AS C1_PRODUTO, NULL AS C1_DESCRI, NULL AS B1_ESPECIF, NULL AS C1_UM, NULL AS C1_SEGUM, NULL AS C1_EMISSAO, NULL AS C1_PEDIDO, NULL AS C1_ITEMPED, NULL AS C1_SOLICIT, NULL AS C1_QUJE, NULL AS C1_QUANT, -- C1/SB1 campos são NULL

      NULL AS C7_QUANT, NULL AS C7_PRECO, NULL AS C7_TOTAL, -- SC7 não existe, então NULL

      SD1.D1_VUNIT, SD1.D1_TOTAL,

      NULL AS C7_CC, NULL AS C7_ITEMCTA, NULL AS C7_CONTA, -- C7 campos são NULL

      SD1.D1_CC, SD1.D1_ITEMCTA, SD1.D1_CONTA,

      NULL AS C7_TIPO, -- SC7 não existe, então NULL

      NULL AS C7_RATEIO, -- SC7 não existe, então NULL

      CASE WHEN SD1.D1_RATEIO = '1' THEN 'SIM' WHEN SD1.D1_RATEIO = '2' THEN 'NAO' ELSE SD1.D1_RATEIO END AS D1_RATEIO,

      SD1.D1_VUNIT AS D1_VUNIT_SEL, SD1.D1_TOTAL AS D1_TOTAL_SEL,

      SDE.DE_CC, SDE.DE_CONTA, SDE.DE_DOC, SDE.DE_FILIAL, SDE.DE_PERC, SDE.DE_FORNECE,

      SDE.DE_LOJA, -- Primeira menção de DE_LOJA

      SDE.DE_ITEM, SDE.DE_ITEMCTA, SDE.DE_ITEMNF,

      SDE.DE_LOJA AS DE_LOJA_ALIAS, -- Segunda menção de DE_LOJA com alias

      SDE.DE_SERIE,

      (SD1.D1_VUNIT * (SDE.DE_PERC/100)) AS VAL_UNIT_RAT,

      (SD1.D1_TOTAL * (SDE.DE_PERC/100)) AS VAL_TOT_RAT,

      NULL AS C7_CONAPRO, -- SC7 não existe, então NULL

      NULL AS C1_SOLICIT_SEL, -- C1_SOLICIT é NULL

      NULL AS C1_OBS, NULL AS C7_OBS, -- C1 e C7 OBS são NULL

      NULL AS C1_EMISSAO, NULL AS C7_EMISSAO, SD1.D1_EMISSAO, NULL AS C1_DTAPRO -- C1 e C7 datas são NULL

  FROM SD1120 SD1

  LEFT JOIN SDE120 SDE ON SDE.DE_DOC = SD1.D1_DOC AND SDE.DE_SERIE = SD1.D1_SERIE AND SDE.DE_FILIAL = SD1.D1_FILIAL AND SDE.DE_FORNECE = SD1.D1_FORNECE AND SDE.DE_LOJA = SD1.D1_LOJA AND SDE.DE_ITEMNF = SD1.D1_ITEM AND SDE.D_E_L_E_T_ = ' '

  WHERE SD1.D_E_L_E_T_ = ' '

  AND SD1.D1_PEDIDO = ' ' -- ESSENCIAL: Garante que NÃO há vínculo com SC7

  AND SD1.D1_ITEMPC = ' ' -- ESSENCIAL: Garante que NÃO há vínculo com SC7

  AND SD1.D1_FILIAL >= '  ' AND SD1.D1_FILIAL <= 'ZZ' -- Filtro de filial na base


  AND SD1.D1_DTDIGIT >= '20250201' AND SD1.D1_DTDIGIT <= '20250424' -- Filtro de data na base
  
  


Detalhes Importantes:

  • TIPO_REGISTRO: Adicionamos uma coluna literal em cada SELECT para identificar facilmente a origem da linha ('SC', 'PC/AE', 'NF'). Isso é crucial para a análise visual ou para processamento posterior (ex: em um relatório ADVPL).

  • Condições WHERE para "Sem Pai": As cláusulas WHERE nos branches 2 e 3 são a chave. Elas filtram os registros da tabela base (SC7 ou SD1) que não têm os campos de ligação preenchidos. A verificação (CAMPO IS NULL OR TRIM(CAMPO) = '') é robusta para lidar com NULLs e strings de espaços.

  • Filtros de Filial e Data: Os filtros na filial e data devem ser aplicados à tabela que é a base de cada branch (SC1 no Branch 1, SC7 no Branch 2, SD1 no Branch 3).

Aplicando em Relatórios ADVPL

Ao usar essa query em um relatório customizado ADVPL, você executará a query combinada e iterará sobre o resultado. A coluna TIPO_REGISTRO permitirá que você trate cada linha de forma diferente, exibindo as informações relevantes para SC, PC/AE ou NF, mesmo que muitos campos estejam NULL para determinados tipos de registro.

Por exemplo, no seu código ADVPL, ao ler uma linha da query, você pode usar um IF ou CASE baseado no valor da coluna TIPO_REGISTRO para determinar quais campos exibir ou como formatar a saída.

Conclusão

O UNION ALL é uma ferramenta poderosa para análise de dados em sistemas complexos como o Protheus. Ao quebrar seu problema em sub-problemas (os diferentes cenários de documentos) e combiná-los, você obtém uma visão completa e precisa do processo de Compras, identificando não apenas o fluxo padrão, mas também os documentos que existem fora dele.

Dominar essa técnica é um passo importante para qualquer profissional que busca extrair o máximo de informação do ERP Protheus e criar relatórios customizados eficientes em ADVPL.

Experimente a query e adapte os filtros de data e filial conforme a sua necessidade para obter a análise desejada!


Comentários