Comparativo de impostos da reforma tributária (escriturado x XML do CT-e/NF-e)¶
Avaliada
Alguém testou este código. O registro de como e do resultado está no fim da página.
- Tipo:
exemplo - Estado:
avaliada - Autor:
Ponto Ini Sistemas - Criado:
2026-10-22T18:30:00Z - Tabelas:
SF1,SD1,SF2,SD2,SB1,SA1,SA2,CD2,F2D,F2B,CJ2,SFT,CV3,CT2,CKO
Relatório de conferência fiscal da reforma tributária: para cada item de nota, mostra o tributo como foi escriturado e, ao lado, os mesmos números como vieram no XML transmitido. É a diferença entre as duas metades da linha que o relatório existe para expor.
O lado escriturado vem do CD2 (IBS/CBS do item), da regra do tributo em F2D/F2B (com
vigência) e da incidência de escrituração em CJ2; o lado transmitido vem do XML do CT-e ou
da NF-e, lido da CKO. Entrada (SF1/SD1) e saída (SF2/SD2) são dois caminhos
separados, escolhidos pelo filtro, e saem juntos por UNION ALL quando o filtro é Ambos.
Fonte canônico: examples/fontes/PIFINR01.tlpp, CP-1252 sem BOM, exatamente como recebido —
o código abaixo é o mesmo arquivo.
Escopo do teste — leia antes de confiar
- CT-e: validado, entrada e saída, no ambiente de origem.
- NF-e e NFS-e: não testados — não havia nota desses tipos no ambiente quando o
relatório foi avaliado. O caminho
TIPOXML = 'NFE'está no fonte e foi lido, mas não foi exercitado. - Volume: com retorno grande, a gravação do arquivo demora. A saída é planilha
(
SetDevice(4)) e o resultado é materializado inteiro de uma vez, com o XML lido uma única vez por documento.
Como está montado¶
| Peça | Papel |
|---|---|
PIFINR01() |
User Function de entrada: perguntas do ParamBox (Data De/Até, Tipo Movimento, Nota De/Até), Try/Catch com FWAlertError e abertura do relatório. |
ReportDef() |
Monta o TReport (paisagem, saída em planilha) e as 32 TRCell da seção; 11 delas não têm alias e recebem bloco em ReportPrint. |
ReportPrint() |
Monta os WHERE, carrega os mapas auxiliares, executa a consulta principal, itera com a régua (SetMeter/IncMeter/SetMsgPrint) e imprime linha a linha. |
QryEntrada() / QrySaida() |
As duas consultas principais; colunas com os mesmos nomes e a mesma ordem, o que é o que permite o UNION ALL. |
UneQuery() |
Escolhe entrada, saída ou o UNION ALL conforme nTipoMov. |
LoadF2D() / LoadSFT() / LoadCT2() |
Cargas complementares, cada uma montando um JsonObject indexado por chave. |
LoadMap() |
Executa uma consulta e monta o mapa; em chave repetida, mantém o primeiro registro. |
MontaChave() |
Concatena as partes da chave com \|, sem espaços. |
LoadXml() / GetImpXml() |
Localizam o XML na CKO (índice 5) e extraem base, alíquota e valor conforme o tributo. |
DescMov() / DescIncide() / DescRedBs() |
Decodificam códigos em texto (E/S, incidência no livro, parcela reduzida). |
Chaves de junção¶
- Regra do tributo —
D1_IDTRIB(entrada) ouD2_IDTRIB(saída) casa comF2D.F2D_IDREL; o mapa é indexado porF2D_IDREL|F2B_TRIBe consultado porIDTRIB|CD2_IMP. Os dois lados restringem aos mesmos três tributos (CBSFED,IBSEST,IBSMUN), e é isso que faz as chaves casarem. - Classificação fiscal —
SFTporTpMov|Doc|Série|Cli-Fornec|Loja|Item|Produto, com fallback para oCLASFISdo próprio item da nota quando o livro não traz nada. - Conta contábil —
CV3liga o item ao lançamento (CV3_TABORISE2na entrada,SD2na saída) e oCT2destino fornece a conta: débito, senão crédito. ChaveTpMov|R_E_C_N_O_ do item; fallback para a conta do item da nota. - O tipo de movimento entra nas chaves de
SFTeCT2porque documento e recno se repetem entre entrada e saída. - XML — um
DbSeeknaCKOpor documento (chave +.xml); o objeto é reaproveitado enquanto o documento não muda e liberado comFreeObj()na troca e no fim.
Tabelas tocadas¶
| Tabela | Uso | Estrutura conferida no wiki? |
|---|---|---|
SF1 / SD1 |
Cabeçalho e itens da NF de entrada | sim — campos presentes em docs/dados/tabelas/ |
SF2 / SD2 |
Cabeçalho e itens da NF de saída | sim |
SB1 |
Descrição do produto | sim (B1_DESC) |
SA1 |
Cliente, na saída (razão social e CNPJ) | sim |
SA2 |
Fornecedor (entrada; e saída em devolução/beneficiamento) | sim |
CD2 |
Tributo escriturado do item (IBS/CBS) | não |
F2D / F2B |
Regra e vigência do tributo | não |
CJ2 |
Incidência na escrituração | não |
SFT |
Classificação fiscal do livro | não |
CV3 / CT2 |
Vínculo item → lançamento contábil | não |
CKO |
XML transmitido (índice 5) | não |
As tabelas marcadas como não estão fora do catálogo de dicionário publicado neste wiki.
A estrutura delas não foi conferida aqui — para conferir, olhe o SX2/SX3 do seu
ambiente. R_E_C_N_O_ é o recno interno das tabelas, não um campo de SX3, e por isso não
aparece no dicionário.
Pontos de atenção¶
Leitura do código, não testada — o que vale saber ao usar e ao manter:
LoadMap()não confere seMPSysOpenQuery()devolveu alias vazio (oReportPrintconfere, nas linhas do alias principal). Se uma consulta auxiliar falhar, oWhilecai em erro em vez de degradar. Não foi observado no ambiente de origem.LoadMap()guarda o primeiro registro de cada chave; havendo mais de uma linha para a mesma chave, as seguintes são descartadas em silêncio.- Na NF-e, o item do XML é localizado por
_cProdcomparado aD1_COD/D2_COD. Produto que não case devolve{0, 0, 0}deGetImpXml()— indistinguível de "declarado zero" na hora de ler o relatório. - Na saída,
DTDIGITrecebeF2_EMISSAO: a coluna rotulada Dt. Digitação é a emissão, porque aSF2não tem data de digitação. - Razão social e CNPJ da saída vêm do cliente (
SA1) — ou do fornecedor (SA2) quandoF2_TIPOéD(devolução) ouB(beneficiamento); oISNULLaninhado faz a escolha. GetImpXml()navega o XML comAttIsMemberOf()a cada nível antes de tocar o atributo, em vez de acessar direto: um XML sem o grupo_IBSCBS(ou com a tag ausente) devolve zeros em vez de estourar. Vale o mesmo cuidado ao estender a rotina para novos grupos.
Código¶
#include "tlpp-core.th"
#include "totvs.ch"
/*/{Protheus.doc} PIFINR01
Relatório de Comparativo de Impostos (Reforma Tributária).
Apresenta tela de filtros via ParamBox (Data De/Até, Tipo Movimento e Nota De/Até)
e executa o relatório exibindo o progresso dos registros na régua de processamento.
@type function
@version 1.0
@author Ponto Ini Sistemas
@since 07/10/2026
@return logical, .T.
/*/
User Function PIFINR01()
Local aPergs := {}
Local aRet := {}
Local dDataDe := dDatabase
Local dDataAte := dDatabase
Local nTipoMov := 1
Local cNotaDe := Space(TamSX3("F1_DOC")[1])
Local cNotaAte := Replicate("Z", TamSX3("F1_DOC")[1])
Local oReport := Nil
Local oError := ErrorClass():New()
aAdd(aPergs, {1, "Data De" , dDataDe , "", ".T.", "" , ".T.", 60, .F.})
aAdd(aPergs, {1, "Data Até" , dDataAte, "", ".T.", "" , ".T.", 60, .T.})
aAdd(aPergs, {2, "Tipo Movimento" , nTipoMov, {"1=Entrada", "2=Saída", "3=Ambos"}, 60, ".T.", .F.})
aAdd(aPergs, {1, "Nota Fiscal De" , cNotaDe , "@!", ".T.", "SF1", ".T.", 70, .F.})
aAdd(aPergs, {1, "Nota Fiscal Até", cNotaAte, "@!", ".T.", "SF1", ".T.", 70, .T.})
Try
If ParamBox(aPergs, "Filtro - Comparativo de Impostos", @aRet, /*bOk*/, /*aButtons*/, /*lCentered*/, /*nPosx*/, /*nPosy*/, /*oDlgWizard*/, /*cLoad*/, .F., .F.)
dDataDe := aRet[1]
dDataAte := aRet[2]
nTipoMov := Val(cValToChar(aRet[3]))
If nTipoMov == 0
nTipoMov := 1
EndIf
cNotaDe := aRet[4]
cNotaAte := aRet[5]
If Empty(cNotaDe)
cNotaDe := Space(TamSX3("F1_DOC")[1])
EndIf
If Empty(cNotaAte)
cNotaAte := Replicate("Z", TamSX3("F1_DOC")[1])
EndIf
oReport := ReportDef(dDataDe, dDataAte, nTipoMov, cNotaDe, cNotaAte)
oReport:PrintDialog()
EndIf
Catch oError
FWAlertError(oError:DESCRIPTION + ": " + oError:ERRORSTACK, "Atenção")
EndTry
Return .T.
/*/{Protheus.doc} ReportDef
Definição da estrutura e células do relatório TReport.
Os nomes das colunas da consulta têm no máximo 10 caracteres (limite do alias).
As células sem alias são alimentadas em ReportPrint a partir das cargas auxiliares.
@type function
@version 1.0
@param dDataDe, date, Data inicial
@param dDataAte, date, Data final
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cNotaDe, character, Nota fiscal inicial
@param cNotaAte, character, Nota fiscal final
@return object, Objeto TReport configurado
/*/
Static Function ReportDef(dDataDe, dDataAte, nTipoMov, cNotaDe, cNotaAte)
Local oReport
Local oSection1
Local cTitulo := "Comparativo de Impostos"
Local cDesc := "Relatório de Comparativo de Impostos - Reforma Tributária"
Local cNome := "PIFINR01"
Local cAliasRep := "QRY_IMP"
oReport := TReport():New(cNome, cTitulo, /*uParam*/, {|oReport| ReportPrint(oReport, dDataDe, dDataAte, nTipoMov, cNotaDe, cNotaAte, cAliasRep)}, cDesc)
oReport:SetTotalInLine(.F.)
oReport:SetLandscape()
oReport:SetDevice(4)
oReport:SetTpPlanilha({.F., .F., .F., .T.})
oReport:lParamReadOnly := .T.
oReport:lPreview := .F.
oReport:lBold := .T.
oSection1 := TRSection():New(oReport, cTitulo, {cAliasRep})
oSection1:SetTotalInLine(.F.)
oSection1:SetHeaderBreak(.T.)
oSection1:SetPageBreak(.F.)
oSection1:lBold := .T.
TRCell():New(oSection1, "TIPOMOV" , /*cAlias*/, "Movimento" , "@!" , 10)
TRCell():New(oSection1, "FILIAL" , cAliasRep , "Filial" , "@!" , 04)
TRCell():New(oSection1, "DOC" , cAliasRep , "Num. Doc." , "@!" , 09)
TRCell():New(oSection1, "SERIE" , cAliasRep , "Série" , "@!" , 03)
TRCell():New(oSection1, "ESPECIE" , cAliasRep , "Espécie" , "@!" , 05)
TRCell():New(oSection1, "DTDIGIT" , cAliasRep , "Dt. Digitação" , "@D" , 10)
TRCell():New(oSection1, "EMISSAO" , cAliasRep , "Dt. Emissão" , "@D" , 10)
TRCell():New(oSection1, "FORNECE" , cAliasRep , "Cli/Fornec" , "@!" , 06)
TRCell():New(oSection1, "LOJA" , cAliasRep , "Loja" , "@!" , 02)
TRCell():New(oSection1, "NOMEFOR" , cAliasRep , "Razão Social" , "@!" , 30)
TRCell():New(oSection1, "CNPJ" , cAliasRep , "CNPJ/CPF" , "@!" , 18)
TRCell():New(oSection1, "TRIBUTO" , cAliasRep , "Tributo" , "@!" , 10)
TRCell():New(oSection1, "IDTRIB" , cAliasRep , "Id Rel. Trib." , "@!" , 36)
TRCell():New(oSection1, "CODREGRA" , /*cAlias*/, "Cód. Regra" , "@!" , 15)
TRCell():New(oSection1, "DESCREGRA" , /*cAlias*/, "Desc. Regra" , "@!" , 30)
TRCell():New(oSection1, "CD2BC" , cAliasRep , "Base Cálc." , "@E 999,999,999.99" , 14)
TRCell():New(oSection1, "CD2ALIQ" , cAliasRep , "Alíq. (%)" , "@E 999.9999" , 10)
TRCell():New(oSection1, "CD2VALOR" , cAliasRep , "Vlr. Tributo" , "@E 999,999,999.99" , 14)
TRCell():New(oSection1, "BASETRANS" , /*cAlias*/, "Base Cálc. Trans." , "@E 999,999,999.99" , 14)
TRCell():New(oSection1, "ALIQTRANS" , /*cAlias*/, "Alíq. Trans. (%)" , "@E 999.9999" , 10)
TRCell():New(oSection1, "VLRTRANS" , /*cAlias*/, "Vlr. Trib. Trans." , "@E 999,999,999.99" , 14)
TRCell():New(oSection1, "CCLASSTRIB" , /*cAlias*/, "Class. Trib." , "@!" , 10)
TRCell():New(oSection1, "CHVNFE" , cAliasRep , "Chave NFe" , "@!" , 44)
TRCell():New(oSection1, "TIPONF" , cAliasRep , "Tipo Nota" , "@!" , 02)
TRCell():New(oSection1, "CFOP" , cAliasRep , "CFOP" , "@!" , 05)
TRCell():New(oSection1, "CODINCID" , /*cAlias*/, "Cód. Incidência" , "@!" , 02)
TRCell():New(oSection1, "INCIDLIVRO" , /*cAlias*/, "Incidência Livro" , "@!" , 30)
TRCell():New(oSection1, "INCIDREDUZ" , /*cAlias*/, "Incid. Parc. Red." , "@!" , 40)
TRCell():New(oSection1, "PRODUTO" , cAliasRep , "Produto" , "@!" , 15)
TRCell():New(oSection1, "DESCPROD" , cAliasRep , "Desc. Produto" , "@!" , 30)
TRCell():New(oSection1, "ITEM" , cAliasRep , "Item" , "@!" , 04)
TRCell():New(oSection1, "CONTA" , /*cAlias*/, "Conta Contábil" , "@!" , 20)
Return oReport
/*/{Protheus.doc} ReportPrint
Executa a consulta principal (entrada: SF1/SD1/SB1/SA2/CD2; saída: SF2/SD2/SB1/SA1-SA2/CD2,
unidas quando "Ambos") e carrega em memória, por consultas separadas, os dados complementares
(F2D/F2B/CJ2, SFT, CV3/CT2). O XML (CKO) é lido uma única vez por documento. Imprime os registros
com atualização contínua da régua de processamento.
@type function
@version 1.0
@param oReport, object, Objeto TReport
@param dDataDe, date, Data inicial
@param dDataAte, date, Data final
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cNotaDe, character, Nota inicial
@param cNotaAte, character, Nota final
@param cAliasRep, character, Nome do alias da seção
@return variant, Nil
/*/
Static Function ReportPrint(oReport, dDataDe, dDataAte, nTipoMov, cNotaDe, cNotaAte, cAliasRep)
Local oSection1 := oReport:Section(1)
Local cQuery := ""
Local cAliasQry := ""
Local cWhereF1 := ""
Local cWhereF2 := ""
Local nTotal := 0
Local nAtual := 0
Local oMapF2D := Nil
Local oMapSFT := Nil
Local oMapCT2 := Nil
Local oLinha := JsonObject():New()
Local oXml := Nil
Local cUltXml := ""
Local cArqXml := ""
Local cChvItem := ""
Local aF2D := {}
Local aTransm := {}
Local cAux := ""
// Filtros comuns de cabeçalho (entrada e saída), reutilizados em todas as consultas
cWhereF1 := " F1.D_E_L_E_T_ = ' ' "
cWhereF1 += " AND F1.F1_FILIAL = '" + xFilial("SF1") + "' "
cWhereF1 += " AND F1.F1_EMISSAO BETWEEN '" + dToS(dDataDe) + "' AND '" + dToS(dDataAte) + "' "
cWhereF1 += " AND F1.F1_DOC BETWEEN '" + cNotaDe + "' AND '" + cNotaAte + "' "
cWhereF2 := " F2.D_E_L_E_T_ = ' ' "
cWhereF2 += " AND F2.F2_FILIAL = '" + xFilial("SF2") + "' "
cWhereF2 += " AND F2.F2_EMISSAO BETWEEN '" + dToS(dDataDe) + "' AND '" + dToS(dDataAte) + "' "
cWhereF2 += " AND F2.F2_DOC BETWEEN '" + cNotaDe + "' AND '" + cNotaAte + "' "
oReport:SetMsgPrint("Carregando dados complementares...")
oMapF2D := LoadF2D(nTipoMov, cWhereF1, cWhereF2)
oMapSFT := LoadSFT(nTipoMov, cWhereF1, cWhereF2)
oMapCT2 := LoadCT2(nTipoMov, cWhereF1, cWhereF2)
cQuery := UneQuery(nTipoMov, QryEntrada(cWhereF1), QrySaida(cWhereF2))
cQuery += " ORDER BY "
cQuery += " FILIAL, "
cQuery += " TPMOV, "
cQuery += " DTDIGIT, "
cQuery += " DOC, "
cQuery += " SERIE, "
cQuery += " ITEM, "
cQuery += " TRIBUTO "
cAliasQry := MPSysOpenQuery(cQuery, cAliasRep)
If Empty(cAliasQry)
cAliasQry := cAliasRep
EndIf
DbSelectArea(cAliasQry)
Count To nTotal
(cAliasQry)->(DbGoTop())
If nTotal == 0
FWAlertWarning("Nenhum registro encontrado para os parâmetros informados.", "Comparativo de Impostos")
(cAliasQry)->(DbCloseArea())
Return Nil
EndIf
oSection1:Cell("TIPOMOV"):SetBlock( { || DescMov((cAliasQry)->TPMOV) })
oSection1:Cell("DTDIGIT"):SetBlock( { || sToD((cAliasQry)->DTDIGIT) })
oSection1:Cell("EMISSAO"):SetBlock( { || sToD((cAliasQry)->EMISSAO) })
oSection1:Cell("CODREGRA"):SetBlock( { || oLinha["CODREGRA"] })
oSection1:Cell("DESCREGRA"):SetBlock( { || oLinha["DESCREGRA"] })
oSection1:Cell("BASETRANS"):SetBlock( { || oLinha["BASETRANS"] })
oSection1:Cell("ALIQTRANS"):SetBlock( { || oLinha["ALIQTRANS"] })
oSection1:Cell("VLRTRANS"):SetBlock( { || oLinha["VLRTRANS"] })
oSection1:Cell("CCLASSTRIB"):SetBlock( { || oLinha["CCLASSTRIB"] })
oSection1:Cell("CODINCID"):SetBlock( { || oLinha["CODINCID"] })
oSection1:Cell("INCIDLIVRO"):SetBlock( { || oLinha["INCIDLIVRO"] })
oSection1:Cell("INCIDREDUZ"):SetBlock( { || oLinha["INCIDREDUZ"] })
oSection1:Cell("CONTA"):SetBlock( { || oLinha["CONTA"] })
CKO->(DbSetOrder(5))
oReport:SetMeter(nTotal)
oSection1:Init()
oReport:lBold := .F.
oSection1:lBold := .F.
While !(cAliasQry)->(Eof())
nAtual++
oReport:SetMsgPrint("Processando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + " - Doc: " + AllTrim((cAliasQry)->DOC) + "...")
oReport:IncMeter()
If oReport:Cancel()
Exit
EndIf
// Chaves de SFT e CT2 levam o tipo de movimento (E/S): doc/recno podem repetir entre entrada e saída
cChvItem := MontaChave({(cAliasQry)->TPMOV, (cAliasQry)->DOC, (cAliasQry)->SERIE, (cAliasQry)->FORNECE, (cAliasQry)->LOJA, (cAliasQry)->ITEM, (cAliasQry)->PRODUTO})
// F2D/F2B/CJ2 - Regra do tributo e incidência no livro
aF2D := Nil
If !Empty((cAliasQry)->IDTRIB) .And. !Empty((cAliasQry)->TRIBUTO)
aF2D := oMapF2D[MontaChave({(cAliasQry)->IDTRIB, (cAliasQry)->TRIBUTO})]
EndIf
If ValType(aF2D) == "A"
oLinha["CODREGRA"] := aF2D[1]
oLinha["DESCREGRA"] := aF2D[2]
oLinha["CODINCID"] := aF2D[3]
oLinha["INCIDLIVRO"] := DescIncide(aF2D[3])
oLinha["INCIDREDUZ"] := DescRedBs(aF2D[4])
Else
oLinha["CODREGRA"] := ""
oLinha["DESCREGRA"] := ""
oLinha["CODINCID"] := ""
oLinha["INCIDLIVRO"] := DescIncide("")
oLinha["INCIDREDUZ"] := DescRedBs("")
EndIf
// SFT - Classificação fiscal, com fallback para o item da nota
cAux := oMapSFT[cChvItem]
oLinha["CCLASSTRIB"] := IIf(Empty(cAux), (cAliasQry)->CLASFIS, cAux)
// CV3/CT2 - Conta contábil, com fallback para o item da nota
cAux := oMapCT2[MontaChave({(cAliasQry)->TPMOV, cValToChar((cAliasQry)->RECNOITEM)})]
oLinha["CONTA"] := IIf(Empty(cAux), (cAliasQry)->CONTAITEM, cAux)
// XML transmitido - parse uma única vez por documento
cArqXml := AllTrim((cAliasQry)->CHVNFE) + ".xml"
If cArqXml != cUltXml
FreeObj(oXml)
oXml := LoadXml(cArqXml)
cUltXml := cArqXml
EndIf
aTransm := GetImpXml(oXml, (cAliasQry)->TIPOXML, (cAliasQry)->TRIBUTO, (cAliasQry)->PRODUTO)
oLinha["BASETRANS"] := aTransm[1]
oLinha["ALIQTRANS"] := aTransm[2]
oLinha["VLRTRANS"] := aTransm[3]
oSection1:PrintLine()
(cAliasQry)->(DbSkip())
EndDo
oSection1:Finish()
(cAliasQry)->(DbCloseArea())
FreeObj(oXml)
FreeObj(oMapF2D)
FreeObj(oMapSFT)
FreeObj(oMapCT2)
FreeObj(oLinha)
Return Nil
/*/{Protheus.doc} QryEntrada
Consulta principal das notas de entrada (SF1/SD1/SB1/SA2/CD2).
As colunas têm os mesmos nomes e a mesma ordem de QrySaida, para permitir o UNION ALL.
@type function
@version 1.0
@param cWhereF1, character, Filtro do cabeçalho SF1 (alias F1)
@return character, Consulta SQL (sem ORDER BY)
/*/
Static Function QryEntrada(cWhereF1)
Local cQuery := ""
cQuery := " SELECT "
cQuery += " 'E' AS TPMOV, "
cQuery += " F1.F1_FILIAL AS FILIAL, "
cQuery += " F1.F1_DOC AS DOC, "
cQuery += " F1.F1_SERIE AS SERIE, "
cQuery += " F1.F1_ESPECIE AS ESPECIE, "
cQuery += " F1.F1_DTDIGIT AS DTDIGIT, "
cQuery += " F1.F1_EMISSAO AS EMISSAO, "
cQuery += " F1.F1_FORNECE AS FORNECE, "
cQuery += " F1.F1_LOJA AS LOJA, "
cQuery += " ISNULL(A2.A2_NOME, ' ') AS NOMEFOR, "
cQuery += " ISNULL(A2.A2_CGC, ' ') AS CNPJ, "
cQuery += " ISNULL(CD2.CD2_IMP, ' ') AS TRIBUTO, "
cQuery += " D1.D1_IDTRIB AS IDTRIB, "
cQuery += " ISNULL(CD2.CD2_BC, 0) AS CD2BC, "
cQuery += " ISNULL(CD2.CD2_ALIQ, 0) AS CD2ALIQ, "
cQuery += " ISNULL(CD2.CD2_VLTRIB, 0) AS CD2VALOR, "
cQuery += " F1.F1_CHVNFE AS CHVNFE, "
cQuery += " F1.F1_TIPO AS TIPONF, "
cQuery += " D1.D1_CF AS CFOP, "
cQuery += " D1.D1_CLASFIS AS CLASFIS, "
cQuery += " D1.D1_CONTA AS CONTAITEM, "
cQuery += " D1.D1_COD AS PRODUTO, "
cQuery += " B1.B1_DESC AS DESCPROD, "
cQuery += " D1.D1_ITEM AS ITEM, "
cQuery += " D1.R_E_C_N_O_ AS RECNOITEM, "
cQuery += " 'CTE' AS TIPOXML "
cQuery += " FROM "
cQuery += " " + RetSqlName("SF1") + " F1 "
cQuery += " INNER JOIN " + RetSqlName("SD1") + " D1 ON D1.D1_FILIAL = F1.F1_FILIAL "
cQuery += " AND D1.D1_DOC = F1.F1_DOC "
cQuery += " AND D1.D1_SERIE = F1.F1_SERIE "
cQuery += " AND D1.D1_FORNECE = F1.F1_FORNECE "
cQuery += " AND D1.D1_LOJA = F1.F1_LOJA "
cQuery += " AND D1.D_E_L_E_T_ = ' ' "
cQuery += " INNER JOIN " + RetSqlName("SB1") + " B1 ON B1.B1_FILIAL = '" + xFilial("SB1") + "' "
cQuery += " AND B1.B1_COD = D1.D1_COD "
cQuery += " AND B1.D_E_L_E_T_ = ' ' "
cQuery += " LEFT JOIN " + RetSqlName("SA2") + " A2 ON A2.A2_FILIAL = '" + xFilial("SA2") + "' "
cQuery += " AND A2.A2_COD = F1.F1_FORNECE "
cQuery += " AND A2.A2_LOJA = F1.F1_LOJA "
cQuery += " AND A2.D_E_L_E_T_ = ' ' "
cQuery += " LEFT JOIN " + RetSqlName("CD2") + " CD2 ON CD2.CD2_FILIAL = D1.D1_FILIAL "
cQuery += " AND CD2.CD2_TPMOV = 'E' "
cQuery += " AND CD2.CD2_DOC = D1.D1_DOC "
cQuery += " AND CD2.CD2_SERIE = D1.D1_SERIE "
cQuery += " AND CD2.CD2_CODFOR = D1.D1_FORNECE "
cQuery += " AND CD2.CD2_LOJFOR = D1.D1_LOJA "
cQuery += " AND CD2.CD2_ITEM = D1.D1_ITEM "
cQuery += " AND CD2.CD2_CODPRO = D1.D1_COD "
cQuery += " AND CD2.CD2_IMP IN ('CBSFED', 'IBSEST', 'IBSMUN') "
cQuery += " AND CD2.D_E_L_E_T_ = ' ' "
cQuery += " WHERE "
cQuery += cWhereF1
Return cQuery
/*/{Protheus.doc} QrySaida
Consulta principal das notas de saída (SF2/SD2/SB1/SA1-SA2/CD2).
Razão social e CNPJ vêm do cliente (SA1) ou, em devolução/beneficiamento (F2_TIPO D/B), do fornecedor (SA2).
Sem data de digitação na saída: DTDIGIT recebe a emissão.
@type function
@version 1.0
@param cWhereF2, character, Filtro do cabeçalho SF2 (alias F2)
@return character, Consulta SQL (sem ORDER BY)
/*/
Static Function QrySaida(cWhereF2)
Local cQuery := ""
cQuery := " SELECT "
cQuery += " 'S' AS TPMOV, "
cQuery += " F2.F2_FILIAL AS FILIAL, "
cQuery += " F2.F2_DOC AS DOC, "
cQuery += " F2.F2_SERIE AS SERIE, "
cQuery += " F2.F2_ESPECIE AS ESPECIE, "
cQuery += " F2.F2_EMISSAO AS DTDIGIT, "
cQuery += " F2.F2_EMISSAO AS EMISSAO, "
cQuery += " F2.F2_CLIENTE AS FORNECE, "
cQuery += " F2.F2_LOJA AS LOJA, "
cQuery += " ISNULL(A1.A1_NOME, ISNULL(A2.A2_NOME, ' ')) AS NOMEFOR, "
cQuery += " ISNULL(A1.A1_CGC, ISNULL(A2.A2_CGC, ' ')) AS CNPJ, "
cQuery += " ISNULL(CD2.CD2_IMP, ' ') AS TRIBUTO, "
cQuery += " D2.D2_IDTRIB AS IDTRIB, "
cQuery += " ISNULL(CD2.CD2_BC, 0) AS CD2BC, "
cQuery += " ISNULL(CD2.CD2_ALIQ, 0) AS CD2ALIQ, "
cQuery += " ISNULL(CD2.CD2_VLTRIB, 0) AS CD2VALOR, "
cQuery += " F2.F2_CHVNFE AS CHVNFE, "
cQuery += " F2.F2_TIPO AS TIPONF, "
cQuery += " D2.D2_CF AS CFOP, "
cQuery += " D2.D2_CLASFIS AS CLASFIS, "
cQuery += " D2.D2_CONTA AS CONTAITEM, "
cQuery += " D2.D2_COD AS PRODUTO, "
cQuery += " B1.B1_DESC AS DESCPROD, "
cQuery += " D2.D2_ITEM AS ITEM, "
cQuery += " D2.R_E_C_N_O_ AS RECNOITEM, "
cQuery += " 'NFE' AS TIPOXML "
cQuery += " FROM "
cQuery += " " + RetSqlName("SF2") + " F2 "
cQuery += " INNER JOIN " + RetSqlName("SD2") + " D2 ON D2.D2_FILIAL = F2.F2_FILIAL "
cQuery += " AND D2.D2_DOC = F2.F2_DOC "
cQuery += " AND D2.D2_SERIE = F2.F2_SERIE "
cQuery += " AND D2.D2_CLIENTE = F2.F2_CLIENTE "
cQuery += " AND D2.D2_LOJA = F2.F2_LOJA "
cQuery += " AND D2.D_E_L_E_T_ = ' ' "
cQuery += " INNER JOIN " + RetSqlName("SB1") + " B1 ON B1.B1_FILIAL = '" + xFilial("SB1") + "' "
cQuery += " AND B1.B1_COD = D2.D2_COD "
cQuery += " AND B1.D_E_L_E_T_ = ' ' "
cQuery += " LEFT JOIN " + RetSqlName("SA1") + " A1 ON A1.A1_FILIAL = '" + xFilial("SA1") + "' "
cQuery += " AND A1.A1_COD = F2.F2_CLIENTE "
cQuery += " AND A1.A1_LOJA = F2.F2_LOJA "
cQuery += " AND F2.F2_TIPO NOT IN ('D', 'B') "
cQuery += " AND A1.D_E_L_E_T_ = ' ' "
cQuery += " LEFT JOIN " + RetSqlName("SA2") + " A2 ON A2.A2_FILIAL = '" + xFilial("SA2") + "' "
cQuery += " AND A2.A2_COD = F2.F2_CLIENTE "
cQuery += " AND A2.A2_LOJA = F2.F2_LOJA "
cQuery += " AND F2.F2_TIPO IN ('D', 'B') "
cQuery += " AND A2.D_E_L_E_T_ = ' ' "
cQuery += " LEFT JOIN " + RetSqlName("CD2") + " CD2 ON CD2.CD2_FILIAL = D2.D2_FILIAL "
cQuery += " AND CD2.CD2_TPMOV = 'S' "
cQuery += " AND CD2.CD2_DOC = D2.D2_DOC "
cQuery += " AND CD2.CD2_SERIE = D2.D2_SERIE "
cQuery += " AND CD2.CD2_CODCLI = D2.D2_CLIENTE "
cQuery += " AND CD2.CD2_LOJCLI = D2.D2_LOJA "
cQuery += " AND CD2.CD2_ITEM = D2.D2_ITEM "
cQuery += " AND CD2.CD2_CODPRO = D2.D2_COD "
cQuery += " AND CD2.CD2_IMP IN ('CBSFED', 'IBSEST', 'IBSMUN') "
cQuery += " AND CD2.D_E_L_E_T_ = ' ' "
cQuery += " WHERE "
cQuery += cWhereF2
Return cQuery
/*/{Protheus.doc} UneQuery
Seleciona a consulta conforme o tipo de movimento: 1=Entrada, 2=Saída, 3=Ambos (UNION ALL).
@type function
@version 1.0
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cQryEnt, character, Consulta de entrada
@param cQrySai, character, Consulta de saída
@return character, Consulta resultante
/*/
Static Function UneQuery(nTipoMov, cQryEnt, cQrySai)
Local cQuery := ""
Do Case
Case nTipoMov == 2
cQuery := cQrySai
Case nTipoMov == 3
cQuery := cQryEnt + " UNION ALL " + cQrySai
Otherwise
cQuery := cQryEnt
EndCase
Return cQuery
/*/{Protheus.doc} LoadF2D
Carrega a regra do tributo (F2D/F2B, respeitando a vigência) e a incidência da escrituração (CJ2)
dos itens das notas do filtro (D1_IDTRIB na entrada, D2_IDTRIB na saída).
Chave: IDTRIB|F2B_TRIB
@type function
@version 1.0
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cWhereF1, character, Filtro do cabeçalho SF1 (alias F1)
@param cWhereF2, character, Filtro do cabeçalho SF2 (alias F2)
@return object, JsonObject com {Código Regra, Descrição Regra, CJ2_INCIDE, CJ2_IREDBS}
/*/
Static Function LoadF2D(nTipoMov, cWhereF1, cWhereF2)
Local cQryEnt := ""
Local cQrySai := ""
cQryEnt := " SELECT "
cQryEnt += " F2D.F2D_IDREL, F2B.F2B_TRIB, F2D.F2D_TRIB, F2B.F2B_DESC, "
cQryEnt += " ISNULL(CJ2.CJ2_INCIDE, ' ') AS CJ2_INCIDE, "
cQryEnt += " ISNULL(CJ2.CJ2_IREDBS, ' ') AS CJ2_IREDBS "
cQryEnt += " FROM "
cQryEnt += " " + RetSqlName("SF1") + " F1 "
cQryEnt += " INNER JOIN " + RetSqlName("SD1") + " D1 ON D1.D1_FILIAL = F1.F1_FILIAL "
cQryEnt += " AND D1.D1_DOC = F1.F1_DOC "
cQryEnt += " AND D1.D1_SERIE = F1.F1_SERIE "
cQryEnt += " AND D1.D1_FORNECE = F1.F1_FORNECE "
cQryEnt += " AND D1.D1_LOJA = F1.F1_LOJA "
cQryEnt += " AND D1.D1_IDTRIB <> ' ' "
cQryEnt += " AND D1.D_E_L_E_T_ = ' ' "
cQryEnt += " INNER JOIN " + RetSqlName("F2D") + " F2D ON F2D.F2D_FILIAL = D1.D1_FILIAL "
cQryEnt += " AND F2D.F2D_IDREL = D1.D1_IDTRIB "
cQryEnt += " AND F2D.D_E_L_E_T_ = ' ' "
cQryEnt += " INNER JOIN " + RetSqlName("F2B") + " F2B ON F2B.F2B_FILIAL = '" + xFilial("F2B") + "' "
cQryEnt += " AND F2B.F2B_REGRA = F2D.F2D_TRIB "
cQryEnt += " AND F2B.F2B_TRIB IN ('CBSFED', 'IBSEST', 'IBSMUN') "
cQryEnt += " AND (F2B.F2B_VIGINI = ' ' OR F2B.F2B_VIGINI <= D1.D1_EMISSAO) "
cQryEnt += " AND (F2B.F2B_VIGFIM = ' ' OR F2B.F2B_VIGFIM >= D1.D1_EMISSAO) "
cQryEnt += " AND F2B.D_E_L_E_T_ = ' ' "
cQryEnt += " AND F2B.F2B_ALTERA = '2' "
cQryEnt += " LEFT JOIN " + RetSqlName("CJ2") + " CJ2 ON CJ2.CJ2_CODIGO = F2B.F2B_CODESC "
cQryEnt += " AND CJ2.CJ2_FILIAL = F1.F1_FILIAL "
cQryEnt += " AND CJ2.D_E_L_E_T_ = ' ' "
cQryEnt += " WHERE "
cQryEnt += cWhereF1
cQrySai := " SELECT "
cQrySai += " F2D.F2D_IDREL, F2B.F2B_TRIB, F2D.F2D_TRIB, F2B.F2B_DESC, "
cQrySai += " ISNULL(CJ2.CJ2_INCIDE, ' ') AS CJ2_INCIDE, "
cQrySai += " ISNULL(CJ2.CJ2_IREDBS, ' ') AS CJ2_IREDBS "
cQrySai += " FROM "
cQrySai += " " + RetSqlName("SF2") + " F2 "
cQrySai += " INNER JOIN " + RetSqlName("SD2") + " D2 ON D2.D2_FILIAL = F2.F2_FILIAL "
cQrySai += " AND D2.D2_DOC = F2.F2_DOC "
cQrySai += " AND D2.D2_SERIE = F2.F2_SERIE "
cQrySai += " AND D2.D2_CLIENTE = F2.F2_CLIENTE "
cQrySai += " AND D2.D2_LOJA = F2.F2_LOJA "
cQrySai += " AND D2.D2_IDTRIB <> ' ' "
cQrySai += " AND D2.D_E_L_E_T_ = ' ' "
cQrySai += " INNER JOIN " + RetSqlName("F2D") + " F2D ON F2D.F2D_FILIAL = D2.D2_FILIAL "
cQrySai += " AND F2D.F2D_IDREL = D2.D2_IDTRIB "
cQrySai += " AND F2D.D_E_L_E_T_ = ' ' "
cQrySai += " INNER JOIN " + RetSqlName("F2B") + " F2B ON F2B.F2B_FILIAL = '" + xFilial("F2B") + "' "
cQrySai += " AND F2B.F2B_REGRA = F2D.F2D_TRIB "
cQrySai += " AND F2B.F2B_TRIB IN ('CBSFED', 'IBSEST', 'IBSMUN') "
cQrySai += " AND (F2B.F2B_VIGINI = ' ' OR F2B.F2B_VIGINI <= D2.D2_EMISSAO) "
cQrySai += " AND (F2B.F2B_VIGFIM = ' ' OR F2B.F2B_VIGFIM >= D2.D2_EMISSAO) "
cQrySai += " AND F2B.D_E_L_E_T_ = ' ' "
cQrySai += " AND F2B.F2B_ALTERA = '2' "
cQrySai += " LEFT JOIN " + RetSqlName("CJ2") + " CJ2 ON CJ2.CJ2_CODIGO = F2B.F2B_CODESC "
cQrySai += " AND CJ2.CJ2_FILIAL = F2.F2_FILIAL "
cQrySai += " AND CJ2.D_E_L_E_T_ = ' ' "
cQrySai += " WHERE "
cQrySai += cWhereF2
Return LoadMap(UneQuery(nTipoMov, cQryEnt, cQrySai), ;
{|cAli| MontaChave({(cAli)->F2D_IDREL, (cAli)->F2B_TRIB}) }, ;
{|cAli| {AllTrim((cAli)->F2D_TRIB), AllTrim((cAli)->F2B_DESC), AllTrim((cAli)->CJ2_INCIDE), AllTrim((cAli)->CJ2_IREDBS)} })
/*/{Protheus.doc} LoadSFT
Carrega a classificação fiscal (FT_CLASFIS) dos itens do livro (SFT) das notas do filtro.
Chave: TpMov(E/S)|Doc|Série|Cli/Fornec|Loja|Item|Produto
@type function
@version 1.0
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cWhereF1, character, Filtro do cabeçalho SF1 (alias F1)
@param cWhereF2, character, Filtro do cabeçalho SF2 (alias F2)
@return object, JsonObject com a classificação fiscal
/*/
Static Function LoadSFT(nTipoMov, cWhereF1, cWhereF2)
Local cQryEnt := ""
Local cQrySai := ""
cQryEnt := " SELECT "
cQryEnt += " SFT.FT_TIPOMOV, SFT.FT_NFISCAL, SFT.FT_SERIE, SFT.FT_CLIEFOR, SFT.FT_LOJA, "
cQryEnt += " SFT.FT_ITEM, SFT.FT_PRODUTO, SFT.FT_CLASFIS "
cQryEnt += " FROM "
cQryEnt += " " + RetSqlName("SF1") + " F1 "
cQryEnt += " INNER JOIN " + RetSqlName("SFT") + " SFT ON SFT.FT_FILIAL = F1.F1_FILIAL "
cQryEnt += " AND SFT.FT_TIPOMOV = 'E' "
cQryEnt += " AND SFT.FT_SERIE = F1.F1_SERIE "
cQryEnt += " AND SFT.FT_NFISCAL = F1.F1_DOC "
cQryEnt += " AND SFT.FT_CLIEFOR = F1.F1_FORNECE "
cQryEnt += " AND SFT.FT_LOJA = F1.F1_LOJA "
cQryEnt += " AND SFT.FT_CLASFIS <> ' ' "
cQryEnt += " AND SFT.D_E_L_E_T_ = ' ' "
cQryEnt += " WHERE "
cQryEnt += cWhereF1
cQrySai := " SELECT "
cQrySai += " SFT.FT_TIPOMOV, SFT.FT_NFISCAL, SFT.FT_SERIE, SFT.FT_CLIEFOR, SFT.FT_LOJA, "
cQrySai += " SFT.FT_ITEM, SFT.FT_PRODUTO, SFT.FT_CLASFIS "
cQrySai += " FROM "
cQrySai += " " + RetSqlName("SF2") + " F2 "
cQrySai += " INNER JOIN " + RetSqlName("SFT") + " SFT ON SFT.FT_FILIAL = F2.F2_FILIAL "
cQrySai += " AND SFT.FT_TIPOMOV = 'S' "
cQrySai += " AND SFT.FT_SERIE = F2.F2_SERIE "
cQrySai += " AND SFT.FT_NFISCAL = F2.F2_DOC "
cQrySai += " AND SFT.FT_CLIEFOR = F2.F2_CLIENTE "
cQrySai += " AND SFT.FT_LOJA = F2.F2_LOJA "
cQrySai += " AND SFT.FT_CLASFIS <> ' ' "
cQrySai += " AND SFT.D_E_L_E_T_ = ' ' "
cQrySai += " WHERE "
cQrySai += cWhereF2
Return LoadMap(UneQuery(nTipoMov, cQryEnt, cQrySai), ;
{|cAli| MontaChave({(cAli)->FT_TIPOMOV, (cAli)->FT_NFISCAL, (cAli)->FT_SERIE, (cAli)->FT_CLIEFOR, (cAli)->FT_LOJA, (cAli)->FT_ITEM, (cAli)->FT_PRODUTO}) }, ;
{|cAli| AllTrim((cAli)->FT_CLASFIS) })
/*/{Protheus.doc} LoadCT2
Carrega a conta contábil (débito, senão crédito) do lançamento vinculado ao item via CV3/CT2.
Entrada: CV3_TABORI 'SE2' com o recno da SD1. Saída: CV3_TABORI 'SD2' com o recno da SD2.
Chave: TpMov(E/S)|R_E_C_N_O_ do item
@type function
@version 1.0
@param nTipoMov, numeric, 1=Entrada, 2=Saída, 3=Ambos
@param cWhereF1, character, Filtro do cabeçalho SF1 (alias F1)
@param cWhereF2, character, Filtro do cabeçalho SF2 (alias F2)
@return object, JsonObject com a conta contábil
/*/
Static Function LoadCT2(nTipoMov, cWhereF1, cWhereF2)
Local cQryEnt := ""
Local cQrySai := ""
cQryEnt := " SELECT "
cQryEnt += " 'E' AS TPMOV, D1.R_E_C_N_O_ AS RECNOITEM, CT2.CT2_DEBITO, CT2.CT2_CREDIT "
cQryEnt += " FROM "
cQryEnt += " " + RetSqlName("SF1") + " F1 "
cQryEnt += " INNER JOIN " + RetSqlName("SD1") + " D1 ON D1.D1_FILIAL = F1.F1_FILIAL "
cQryEnt += " AND D1.D1_DOC = F1.F1_DOC "
cQryEnt += " AND D1.D1_SERIE = F1.F1_SERIE "
cQryEnt += " AND D1.D1_FORNECE = F1.F1_FORNECE "
cQryEnt += " AND D1.D1_LOJA = F1.F1_LOJA "
cQryEnt += " AND D1.D_E_L_E_T_ = ' ' "
cQryEnt += " INNER JOIN " + RetSqlName("CV3") + " CV3 ON CV3.CV3_RECORI = D1.R_E_C_N_O_ "
cQryEnt += " AND CV3.CV3_TABORI = 'SE2' "
cQryEnt += " AND CV3.D_E_L_E_T_ = ' ' "
cQryEnt += " INNER JOIN " + RetSqlName("CT2") + " CT2 ON CT2.R_E_C_N_O_ = CV3.CV3_RECDES "
cQryEnt += " AND CT2.D_E_L_E_T_ = ' ' "
cQryEnt += " WHERE "
cQryEnt += cWhereF1
cQrySai := " SELECT "
cQrySai += " 'S' AS TPMOV, D2.R_E_C_N_O_ AS RECNOITEM, CT2.CT2_DEBITO, CT2.CT2_CREDIT "
cQrySai += " FROM "
cQrySai += " " + RetSqlName("SF2") + " F2 "
cQrySai += " INNER JOIN " + RetSqlName("SD2") + " D2 ON D2.D2_FILIAL = F2.F2_FILIAL "
cQrySai += " AND D2.D2_DOC = F2.F2_DOC "
cQrySai += " AND D2.D2_SERIE = F2.F2_SERIE "
cQrySai += " AND D2.D2_CLIENTE = F2.F2_CLIENTE "
cQrySai += " AND D2.D2_LOJA = F2.F2_LOJA "
cQrySai += " AND D2.D_E_L_E_T_ = ' ' "
cQrySai += " INNER JOIN " + RetSqlName("CV3") + " CV3 ON CV3.CV3_RECORI = D2.R_E_C_N_O_ "
cQrySai += " AND CV3.CV3_TABORI = 'SD2' "
cQrySai += " AND CV3.D_E_L_E_T_ = ' ' "
cQrySai += " INNER JOIN " + RetSqlName("CT2") + " CT2 ON CT2.R_E_C_N_O_ = CV3.CV3_RECDES "
cQrySai += " AND CT2.D_E_L_E_T_ = ' ' "
cQrySai += " WHERE "
cQrySai += cWhereF2
Return LoadMap(UneQuery(nTipoMov, cQryEnt, cQrySai), ;
{|cAli| MontaChave({(cAli)->TPMOV, cValToChar((cAli)->RECNOITEM)}) }, ;
{|cAli| IIf(Empty((cAli)->CT2_DEBITO), AllTrim((cAli)->CT2_CREDIT), AllTrim((cAli)->CT2_DEBITO)) })
/*/{Protheus.doc} LoadMap
Executa a consulta e carrega o resultado em um JsonObject indexado pela chave informada.
Em caso de chave repetida, mantém o primeiro registro encontrado.
@type function
@version 1.0
@param cQuery, character, Consulta SQL
@param bChave, codeblock, Bloco que recebe o alias e retorna a chave
@param bValor, codeblock, Bloco que recebe o alias e retorna o valor
@return object, JsonObject com os registros
/*/
Static Function LoadMap(cQuery, bChave, bValor)
Local oMap := JsonObject():New()
Local cAli := MPSysOpenQuery(cQuery)
Local cKey := ""
Local xVal := Nil
While !(cAli)->(Eof())
cKey := Eval(bChave, cAli)
If oMap[cKey] == Nil
xVal := Eval(bValor, cAli)
If !Empty(xVal)
oMap[cKey] := xVal
EndIf
EndIf
(cAli)->(DbSkip())
EndDo
(cAli)->(DbCloseArea())
Return oMap
/*/{Protheus.doc} MontaChave
Monta chave de busca concatenando as partes sem espaços.
@type function
@version 1.0
@param aPartes, array, Partes da chave
@return character, Chave montada
/*/
Static Function MontaChave(aPartes)
Local cChave := ""
Local nI := 0
For nI := 1 To Len(aPartes)
cChave += IIf(nI > 1, "|", "") + AllTrim(aPartes[nI])
Next nI
Return cChave
/*/{Protheus.doc} DescMov
Descreve o tipo de movimento retornado pela consulta (E/S).
@type function
@version 1.0
@param cTpMov, character, 'E' (entrada) ou 'S' (saída)
@return character, Descrição do movimento
/*/
Static Function DescMov(cTpMov)
Local cRet := "Entrada"
If cTpMov == "S"
cRet := "Saída"
EndIf
Return cRet
/*/{Protheus.doc} DescIncide
Descreve a incidência no livro fiscal a partir do CJ2_INCIDE.
@type function
@version 1.0
@param cCod, character, Código da incidência (CJ2_INCIDE)
@return character, Descrição da incidência
/*/
Static Function DescIncide(cCod)
Local cRet := ""
Do Case
Case cCod == "1"
cRet := "Tributado"
Case cCod == "2"
cRet := "Isento"
Case cCod == "3"
cRet := "Outros"
Case cCod == "4"
cRet := "Tributado e Isento"
Case cCod == "5"
cRet := "Tributado e Outros"
Case cCod == "6"
cRet := "Isento e Outros"
Case cCod == "7"
cRet := "Tributado, Isento e Outros"
Otherwise
cRet := "Não informado"
EndCase
Return cRet
/*/{Protheus.doc} DescRedBs
Descreve a incidência da parcela reduzida a partir do CJ2_IREDBS.
@type function
@version 1.0
@param cCod, character, Código da incidência da parcela reduzida (CJ2_IREDBS)
@return character, Descrição da incidência
/*/
Static Function DescRedBs(cCod)
Local cRet := ""
Do Case
Case cCod == "1"
cRet := "Isento"
Case cCod == "2"
cRet := "Outros"
Otherwise
cRet := "Outros (padrão em operação com redução)"
EndCase
Return cRet
/*/{Protheus.doc} LoadXml
Localiza o XML na CKO (índice 5) e retorna o objeto já interpretado.
@type function
@version 1.0
@param cArqXml, character, Nome do arquivo XML (chave + ".xml")
@return object, Objeto XML ou Nil quando não localizado
/*/
Static Function LoadXml(cArqXml)
Local oXml := Nil
Local cAviso := ""
Local cErro := ""
If !Empty(cArqXml) .And. CKO->(DbSeek(cArqXml))
oXml := XmlParser(CKO->CKO_XMLRET, "", @cAviso, @cErro)
EndIf
Return oXml
/*/{Protheus.doc} GetImpXml
Extrai dados tributarios do XML (CT-e/NF-e) conforme o tributo.
@type function
@version 1.0
@param oXml, object, Objeto XML interpretado (LoadXml)
@param cTipo, character, Tipo do documento ('CTE' ou 'NFE')
@param cTrib, character, Codigo do tributo ('CBSFED', 'IBSEST', 'IBSMUN')
@param cProduto, character, Código do produto (NF-e)
@return array, {Base de Cálculo, Alíquota, Valor}
/*/
Static Function GetImpXml(oXml, cTipo, cTrib, cProduto)
Local aRet := {0, 0, 0}
Local oGIbsCbs := Nil
Local oInfNFe := Nil
Local aItens := {}
Local nI := 0
Local oItem := Nil
Local oImp := Nil
If ValType(oXml) != "O"
Return aRet
EndIf
cTrib := AllTrim(cTrib)
cTipo := AllTrim(cTipo)
If cTipo == 'CTE'
If AttIsMemberOf(oXml, "_cteProc")
If AttIsMemberOf(oXml:_cteProc, "_CTe")
If AttIsMemberOf(oXml:_cteProc:_CTe, "_infCte")
If AttIsMemberOf(oXml:_cteProc:_CTe:_infCte, "_imp")
If AttIsMemberOf(oXml:_cteProc:_CTe:_infCte:_imp, "_IBSCBS")
If AttIsMemberOf(oXml:_cteProc:_CTe:_infCte:_imp:_IBSCBS, "_gIBSCBS")
oGIbsCbs := oXml:_cteProc:_CTe:_infCte:_imp:_IBSCBS:_gIBSCBS
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
ElseIf cTipo == 'NFE'
If AttIsMemberOf(oXml, "_nfeProc")
If AttIsMemberOf(oXml:_nfeProc, "_NFe")
If AttIsMemberOf(oXml:_nfeProc:_NFe, "_infNFe")
oInfNFe := oXml:_nfeProc:_NFe:_infNFe
EndIf
EndIf
ElseIf AttIsMemberOf(oXml, "_NFe")
If AttIsMemberOf(oXml:_NFe, "_infNFe")
oInfNFe := oXml:_NFe:_infNFe
EndIf
EndIf
If oInfNFe != Nil .And. AttIsMemberOf(oInfNFe, "_det")
If ValType(oInfNFe:_det) == "A"
aItens := oInfNFe:_det
ElseIf ValType(oInfNFe:_det) == "O"
aItens := { oInfNFe:_det }
EndIf
For nI := 1 To Len(aItens)
oItem := aItens[nI]
If Empty(cProduto) .Or. ;
(AttIsMemberOf(oItem, "_prod") .And. AttIsMemberOf(oItem:_prod, "_cProd") .And. AllTrim(oItem:_prod:_cProd:TEXT) == AllTrim(cProduto))
If AttIsMemberOf(oItem, "_imposto")
oImp := oItem:_imposto
ElseIf AttIsMemberOf(oItem, "_imp")
oImp := oItem:_imp
Else
oImp := Nil
EndIf
If oImp != Nil .And. AttIsMemberOf(oImp, "_IBSCBS")
If AttIsMemberOf(oImp:_IBSCBS, "_gIBSCBS")
oGIbsCbs := oImp:_IBSCBS:_gIBSCBS
Exit
EndIf
EndIf
EndIf
Next nI
EndIf
EndIf
If oGIbsCbs != Nil
If AttIsMemberOf(oGIbsCbs, "_vBC")
aRet[1] := Val(oGIbsCbs:_vBC:TEXT)
EndIf
If Left(cTrib, 3) == 'CBS'
If AttIsMemberOf(oGIbsCbs, "_gCBS")
If AttIsMemberOf(oGIbsCbs:_gCBS, "_pCBS")
aRet[2] := Val(oGIbsCbs:_gCBS:_pCBS:TEXT)
EndIf
If AttIsMemberOf(oGIbsCbs:_gCBS, "_vCBS")
aRet[3] := Val(oGIbsCbs:_gCBS:_vCBS:TEXT)
EndIf
EndIf
ElseIf cTrib == 'IBSEST'
If AttIsMemberOf(oGIbsCbs, "_gIBSUF")
If AttIsMemberOf(oGIbsCbs:_gIBSUF, "_pIBSUF")
aRet[2] := Val(oGIbsCbs:_gIBSUF:_pIBSUF:TEXT)
EndIf
If AttIsMemberOf(oGIbsCbs:_gIBSUF, "_vIBSUF")
aRet[3] := Val(oGIbsCbs:_gIBSUF:_vIBSUF:TEXT)
EndIf
EndIf
ElseIf cTrib == 'IBSMUN'
If AttIsMemberOf(oGIbsCbs, "_gIBSMun")
If AttIsMemberOf(oGIbsCbs:_gIBSMun, "_pIBSMun")
aRet[2] := Val(oGIbsCbs:_gIBSMun:_pIBSMun:TEXT)
EndIf
If AttIsMemberOf(oGIbsCbs:_gIBSMun, "_vIBSMun")
aRet[3] := Val(oGIbsCbs:_gIBSMun:_vIBSMun:TEXT)
EndIf
EndIf
EndIf
EndIf
Return aRet
Como testar¶
- Compile em um ambiente Protheus com a reforma tributária habilitada e ao menos um CT-e
escriturado e transmitido (a
CKOprecisa ter o XML — índice 5). - Rode
u_PIFINR01()por um menu ou pelo SmartClient. - Filtre um período curto, Tipo Movimento = Entrada, e confira uma nota contra o XML original: base, alíquota e valor do IBS/CBS escriturado devem bater com o que o CT-e declara.
- Repita com Saída, e depois com Ambos, para exercitar o
UNION ALL. - Pendente: rodar com NF-e (modelo 55) e NFS-e. É o que falta para tirar a ressalva do escopo de teste acima.
- Retorno grande: acompanhe o tempo de gravação do arquivo antes de usar em produção num período longo.
Avaliação¶
- Data: 2026-10-22
- Por: Ponto Ini Sistemas (ambiente de origem); registrado por
hermesa pedido do usuário. - Como foi testado: execução do relatório em ambiente Protheus real, com notas escrituradas
e XML transmitido na
CKO. - Resultado: CT-e 100% (entrada e saída). NF-e e NFS-e não testados — não havia nota desses tipos no ambiente. Volume grande atrasa a gravação do arquivo.
Símbolos de API usados¶
Cada função, classe e método que este fonte chama tem página própria — a explicação da API, com citação:
AllTrim(funcao) → páginaAttIsMemberOf(funcao) → páginaDbCloseArea(funcao) → páginaDbGoTop(funcao) → páginaDbSeek(funcao) → páginaDbSelectArea(funcao) → páginaDbSetOrder(funcao) → páginaDbSkip(funcao) → páginaEmpty(funcao) → páginaEof(funcao) → páginaEval(funcao) → páginaFWAlertError(funcao) → páginaFWAlertWarning(funcao) → páginaFreeObj(funcao) → páginaIIf(funcao) → páginaLen(funcao) → páginaMPSysOpenQuery(funcao) → páginaParamBox(funcao) → páginaReplicate(funcao) → páginaRetSqlName(funcao) → páginaSpace(funcao) → páginaTamSX3(funcao) → páginaVal(funcao) → páginaValType(funcao) → páginaXmlParser(funcao) → páginaaAdd(funcao) → páginacValToChar(funcao) → páginadToS(funcao) → páginasToD(funcao) → páginaxFilial(funcao) → páginaErrorClass(classe) → páginaJsonObject(classe) → páginaTRCell(classe) → páginaTRSection(classe) → páginaTReport(classe) → páginaCancel(metodo) → página — receptor no código:oReportCell(metodo) → página — receptor no código:oSection1Finish(metodo) → página — receptor no código:oSection1IncMeter(metodo) → página — receptor no código:oReportInit(metodo) → página — receptor no código:oSection1New(metodo) → página — receptor no código:(construtor)PrintDialog(metodo) → página — receptor no código:oReportPrintLine(metodo) → página — receptor no código:oSection1Section(metodo) → página — receptor no código:oReportSetBlock(metodo) → página — receptor no código:(encadeado)SetDevice(metodo) → página — receptor no código:oReportSetHeaderBreak(metodo) → página — receptor no código:oSection1SetLandscape(metodo) → página — receptor no código:oReportSetMeter(metodo) → página — receptor no código:oReportSetMsgPrint(metodo) → página — receptor no código:oReportSetPageBreak(metodo) → página — receptor no código:oSection1SetTotalInLine(metodo) → página — receptor no código:oReport, oSection1SetTpPlanilha(metodo) → página — receptor no código:oReport
Todos os símbolos: Funções e classes da API.
Fonte¶
- Arquivo canônico:
examples/comparativo-impostos-reforma-tributaria.md - Voltar para Exemplos e templates