Relatório de Pedidos de Venda com Impostos em TReport | Ti Responde 0220

No vídeo de hoje, vamos demonstrar em como criar um relatório em TReport que lista informações de pedido de venda com impostos.

A dúvida de hoje, nos perguntaram, como poderia em um relatório usando a classe TReport, fazer o cálculo de impostos com as funções MaFis* e exibir as informações.

 

Pensando nisso, montamos um exemplo, onde vamos mostrar em como fazer a lógica buscando os pedidos, itens e calculando os impostos e exibindo.

 

Segue abaixo o vídeo exemplificando:

 

E abaixo o código fonte desenvolvido:

//Bibliotecas
#Include "tlpp-core.th"
#Include "Totvs.ch"

//Declaração da namespace
Namespace custom.terminal.youtube

/*/{Protheus.doc} User Function video0220
Relatório de Produtos Vendidos
@author Atilio
@since 30/10/2024
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
@example custom.terminal.youtube.u_video0220()
/*/

User Function video0220()
	Local aArea         := FWGetArea()                      As Array
	Local oReport                                           As Object
	Local aParameters   := {}                               As Array
	Local cInitOrder    := Space(TamSX3('C5_NUM')[1])       As Character
	Local cLastOrder    := StrTran(cInitOrder, ' ', 'Z')    As Character
	Local cInitCustomer := Space(TamSX3('C5_CLIENTE')[1])   As Character
	Local cLastCustomer := StrTran(cInitCustomer, ' ', 'Z') As Character
	Local dInitDate     := FirstDate(Date())                As Date
	Local dLastDate     := LastDate(dInitDate)              As Date
	Local cInitProduct  := Space(TamSX3('C6_PRODUTO')[1])   As Character
	Local cLastProduct  := StrTran(cInitProduct, ' ', 'Z')  As Character
	Private lEvenNumber := .F.                              As Logical
	
	//Adicionando os parametros do ParamBox
	aAdd(aParameters, {1, "Pedido De",   cInitOrder,     "", ".T.", "SC5", ".T.", 60,  .F.}) // MV_PAR01
	aAdd(aParameters, {1, "Pedido Até",  cLastOrder,     "", ".T.", "SC5", ".T.", 60,  .T.}) // MV_PAR02
	aAdd(aParameters, {1, "Cliente De",  cInitCustomer,  "", ".T.", "SA1", ".T.", 60,  .F.}) // MV_PAR03
	aAdd(aParameters, {1, "Cliente Até", cLastCustomer,  "", ".T.", "SA1", ".T.", 60,  .T.}) // MV_PAR04
	aAdd(aParameters, {1, "Emissão De",  dInitDate,      "", ".T.", "",    ".T.", 80,  .T.}) // MV_PAR05
	aAdd(aParameters, {1, "Emissão Até", dLastDate,      "", ".T.", "",    ".T.", 80,  .T.}) // MV_PAR06
	aAdd(aParameters, {1, "Produto De",  cInitProduct,   "", ".T.", "SB1", ".T.", 90,  .F.}) // MV_PAR07
	aAdd(aParameters, {1, "Produto Até", cLastProduct,   "", ".T.", "SB1", ".T.", 90,  .T.}) // MV_PAR08
	
	//Se a pergunta for confirma, cria as definicoes do relatorio
	If ParamBox(aParameters, 'Informe os parâmetros', /*aRet*/, /*bOk*/, /*aButtons*/, /*lCentered*/, /*nPosx*/, /*nPosy*/, /*oDlgWizard*/, /*cLoad*/, .F., .F.)
		oReport := reportDef()
		oReport:PrintDialog()
	EndIf
	
	FWRestArea(aArea)
Return

/*/{Protheus.doc} reportDef
Definicoes do relatorio video0220
@author Atilio
@since 30/10/2024
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function reportDef()
	Local oReport                                    As Object
	Local oSection        := Nil                     As Object
	Local bPageBreakBlock := {|| lEvenNumber := .F.} As CodeBlock
	
	//Criacao do componente de impressao
	oReport := TReport():New( "video0220",;
		"Produtos Vendidos",;
		,;
		{|oReport| reportPrint(oReport),};
		)
	oReport:SetTotalInLine(.F.)
	oReport:lParamPage := .F.
	oReport:oPage:SetPaperSize(9)
	
	//Orientacao do Relatorio
	oReport:SetPortrait()
	
	//Criando a secao de dados
	oSection := TRSection():New( oReport,;
		"Dados",;
		{"QRY_REP", "SC6"})
	oSection:SetTotalInLine(.F.)
	
	//Colunas do relatorio
	TRCell():New(oSection, "C5_NUM",     "QRY_REP", "Pedido",             /*cPicture*/,         6, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C5_EMISSAO", "QRY_REP", "Data Emissão",       /*cPicture*/,        10, /*lPixel*/, /*{|| code-block de impressao }*/, "CENTER", /*lLineBreak*/, "CENTER", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C6_PRODUTO", "SC6",     "Produto",            /*cPicture*/,        15, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C6_DESCRI",  "SC6",     "Descrição",          /*cPicture*/,        30, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C6_QTDVEN",  "SC6",     "Qtde. Vendida",      "@E 999,999,999.99", 12, /*lPixel*/, /*{|| code-block de impressao }*/, "RIGHT", /*lLineBreak*/, "RIGHT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C6_PRCVEN",  "SC6",     "Preço Unit.",        "@E 999,999,999.99", 12, /*lPixel*/, /*{|| code-block de impressao }*/, "RIGHT", /*lLineBreak*/, "RIGHT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "C6_VALOR",   "SC6",     "Valor Total",        "@E 999,999,999.99", 12, /*lPixel*/, /*{|| code-block de impressao }*/, "RIGHT", /*lLineBreak*/, "RIGHT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "XX_VALTOT",  "",        "Valor com Impostos", "@E 999,999,999.99", 12, /*lPixel*/, /*{|| code-block de impressao }*/, "RIGHT", /*lLineBreak*/, "RIGHT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "XX_PEDTOT",  "",        "Valor do Pedido",    "@E 999,999,999.99", 12, /*lPixel*/, /*{|| code-block de impressao }*/, "RIGHT", /*lLineBreak*/, "RIGHT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	
	//Totalizadores
	TRFunction():New(oSection:Cell("C6_VALOR"),  /*cName*/, "SUM", /*oBreak*/, /*cTitle*/, "@E 999,999,999.99", /*uFormula*/, .F.)
	TRFunction():New(oSection:Cell("XX_VALTOT"), /*cName*/, "SUM", /*oBreak*/, /*cTitle*/, "@E 999,999,999.99", /*uFormula*/, .F.)
	
	
	//Define que na quebra de página, a flag volta a ser ímpar
	oReport:OnPageBreak(bPageBreakBlock, .T.)
	
Return oReport

/*/{Protheus.doc} reportPrint
Impressao do relatorio video0220
@author Atilio
@since 30/10/2024
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function reportPrint(oReport)
	Local aArea              := FWGetArea()
	Local cQueryReport       := ""
	Local oSection           := Nil                                As Object
	Local nCurrent           := 0                                  As Numeric
	Local nTotal             := 0                                  As Numeric
	//Variáveis usadas no brush, documentações no TDN - https://tdn.totvs.com/display/public/framework/TReport
	Local nBrushCurrentLine  := 0                                  As Numeric 
	Local nBrushInitColumn   := oReport:LeftMargin()               As Numeric //Margem da esquerda, vai ser a coluna inicial
	Local nBrushWidth        := oReport:PageWidth()                As Numeric //Largura da página, vai ser a coluna final
	Local nBrushHeight       := oReport:LineHeight()               As Numeric //Espaçamento entre as linhas, para saber a altura que irá pintar
	Local oBrush             := TBrush():New(, RGB(223, 255, 249)) As Object //Pincel em um tom de claro
    //Variáveis que vão ser usadas na geração dos dados dos impostos
    Local cCurrentOrder      := ""                                 As Character
    Local aDataSC6           := {}                                 As Array
    Local cQuerySC6          := ""                                 As Character
    Local nOrderItem         := 0                                  As Numeric
    Local nOrderValueWithTax := 0                                  As Numeric
	
	//Pegando as secoes do relatorio
	oSection := oReport:Section(1)
	
	//Montando consulta de dados, buscando os pedidos conforme os filtros do parâmetro
	cQueryReport := "SELECT " + CRLF
	cQueryReport += "    C5_NUM, " + CRLF
	cQueryReport += "    C5_EMISSAO " + CRLF
	cQueryReport += "FROM " + CRLF
	cQueryReport += "    " + RetSQLName("SC5") + " SC5 " + CRLF
	cQueryReport += "WHERE " + CRLF
	cQueryReport += "    C5_FILIAL = '" + FWxFilial("SC5") + "' " + CRLF
	cQueryReport += "    AND C5_NUM >= '" + MV_PAR01 + "' " + CRLF
	cQueryReport += "    AND C5_NUM <= '" + MV_PAR02 + "' " + CRLF
	cQueryReport += "    AND C5_CLIENTE >= '" + MV_PAR03 + "' " + CRLF
	cQueryReport += "    AND C5_CLIENTE <= '" + MV_PAR04 + "' " + CRLF
	cQueryReport += "    AND C5_EMISSAO >= '" + dToS(MV_PAR05) + "' " + CRLF
	cQueryReport += "    AND C5_EMISSAO <= '" + dToS(MV_PAR06) + "' " + CRLF
	cQueryReport += "    AND ( " + CRLF
	cQueryReport += "        SELECT COUNT(C6_NUM) " + CRLF
	cQueryReport += "        FROM " + RetSQLName("SC6") + " SC6 " + CRLF
	cQueryReport += "        WHERE C6_FILIAL = '" + FWxFilial("SC6") + "' " + CRLF
	cQueryReport += "            AND C6_NUM = C5_NUM " + CRLF
	cQueryReport += "            AND C6_PRODUTO >= '" + MV_PAR07 + "' " + CRLF
	cQueryReport += "            AND C6_PRODUTO <= '" + MV_PAR08 + "' " + CRLF
	cQueryReport += "            AND SC6.D_E_L_E_T_ = ' ' " + CRLF
	cQueryReport += "    ) > 0 " + CRLF
	cQueryReport += "    AND SC5.D_E_L_E_T_ = ' ' " + CRLF
	cQueryReport += "ORDER BY " + CRLF
	cQueryReport += "    C5_NUM ASC " + CRLF

    //Pergunta se o usuário quer ver a query
    If FWIsAdmin() .And. FWAlertYesNo("Como admin, você deseja ver a query?", "Continua?")
        ShowLog(cQueryReport)
    EndIf
	
	//Executando consulta e setando o total da regua
	PlsQuery(cQueryReport, "QRY_REP")
	DbSelectArea("QRY_REP")
	Count to nTotal
	oReport:SetMeter(nTotal)

	//Enquanto houver dados
	oSection:Init()
	QRY_REP->(DbGoTop())
	While ! QRY_REP->(Eof())

        //Se o pedido mudou, vai ter que carregar os impostos para o novo pedido
        If cCurrentOrder != QRY_REP->C5_NUM

            //Se já teve pedido, encerra o cálculo fiscal
            If ! Empty(cCurrentOrder)
                MaFisEnd()
            EndIf

            //Atualiza o pedido e zera os dados da SC6
            cCurrentOrder := QRY_REP->C5_NUM
            aDataSC6      := {}
            nOrderItem    := 0

            //Inicializa o cálculo fiscal
            DbSelectArea("SC5")
            SC5->(MsSeek(FWxFilial("SC5") + QRY_REP->C5_NUM))
            MaFisIni(SC5->C5_CLIENTE,;                   // 01 - Codigo Cliente/Fornecedor
                SC5->C5_LOJACLI,;                        // 02 - Loja do Cliente/Fornecedor
                Iif(SC5->C5_TIPO $ "D;B", "F", "C"),;    // 03 - C:Cliente , F:Fornecedor
                SC5->C5_TIPO,;                           // 04 - Tipo da NF
                SC5->C5_TIPOCLI,;                        // 05 - Tipo do Cliente/Fornecedor
                MaFisRelImp("MT100", {"SF2", "SD2"}),;   // 06 - Relacao de Impostos que suportados no arquivo
                ,;                                       // 07 - Tipo de complemento
                ,;                                       // 08 - Permite Incluir Impostos no Rodape .T./.F.
                "SB1",;                                  // 09 - Alias do Cadastro de Produtos - ("SBI" P/ Front Loja)
                "MATA461")                               // 10 - Nome da rotina que esta utilizando a funcao

            //Seleciona agora os itens do pedido
            cQuerySC6 := " SELECT "                                      + CRLF
            cQuerySC6 += "    SC6.R_E_C_N_O_ AS SC6_ID, "                + CRLF
            cQuerySC6 += "    SB1.R_E_C_N_O_ AS SB1_ID, "                 + CRLF
            cQuerySC6 += "    SF4.R_E_C_N_O_ AS SF4_ID "                 + CRLF
            cQuerySC6 += " FROM "                                        + CRLF
            cQuerySC6 += "    "+RetSQLName("SC6")+" SC6 "                + CRLF
            cQuerySC6 += "    LEFT JOIN "+RetSQLName("SB1")+" SB1 ON ( " + CRLF
            cQuerySC6 += "        B1_FILIAL = '"+FWxFilial("SB1")+"' "   + CRLF
            cQuerySC6 += "        AND B1_COD = SC6.C6_PRODUTO "          + CRLF
            cQuerySC6 += "        AND SB1.D_E_L_E_T_ = ' ' "             + CRLF
            cQuerySC6 += "    ) "                                        + CRLF
            cQuerySC6 += "    LEFT JOIN "+RetSQLName("SF4")+" SF4 ON ( " + CRLF
            cQuerySC6 += "        F4_FILIAL = SC6.C6_FILIAL "   + CRLF
            cQuerySC6 += "        AND F4_CODIGO = SC6.C6_TES "          + CRLF
            cQuerySC6 += "        AND SF4.D_E_L_E_T_ = ' ' "             + CRLF
            cQuerySC6 += "    ) "                                        + CRLF
            cQuerySC6 += " WHERE "                                       + CRLF
            cQuerySC6 += "    C6_FILIAL = '"+SC5->C5_FILIAL+"' "       + CRLF
            cQuerySC6 += "    AND C6_NUM = '"+SC5->C5_NUM+"' "       + CRLF
            cQuerySC6 += "    AND SC6.D_E_L_E_T_ = ' ' "                 + CRLF
            cQuerySC6 += " ORDER BY "                                    + CRLF
            cQuerySC6 += "    C6_ITEM "                                  + CRLF
            PlsQuery(cQuerySC6, "QRY_ITE")
            
            //Enquanto houver itens
            QRY_ITE->(DbGoTop())
            While ! QRY_ITE->(EoF())
                nOrderItem++
                
                //Posiciona nas tabelas
                DbSelectArea("SB1")
                DbSelectArea("SC6")
                DbSelectArea("SF4")
                SB1->(DbGoTo(QRY_ITE->SB1_ID))
                SC6->(DbGoTo(QRY_ITE->SC6_ID))
                SF4->(DbGoTo(QRY_ITE->SF4_ID))

                //Adiciona os tratamentos de impostos
                MaFisAdd(SC6->C6_PRODUTO,;    // 01 - Codigo do Produto                    ( Obrigatorio )
                    SC6->C6_TES,;             // 02 - Codigo do TES                        ( Opcional )
                    SC6->C6_QTDVEN,;          // 03 - Quantidade                           ( Obrigatorio )
                    SC6->C6_PRCVEN,;          // 04 - Preco Unitario                       ( Obrigatorio )
                    SC6->C6_VALDESC,;         // 05 - Desconto
                    SC6->C6_NFORI,;           // 06 - Numero da NF Original                ( Devolucao/Benef )
                    SC6->C6_SERIORI,;         // 07 - Serie da NF Original                 ( Devolucao/Benef )
                    0,;                       // 08 - RecNo da NF Original no arq SD1/SD2
                    0,;                       // 09 - Valor do Frete do Item               ( Opcional )
                    0,;                       // 10 - Valor da Despesa do item             ( Opcional )
                    0,;                       // 11 - Valor do Seguro do item              ( Opcional )
                    0,;                       // 12 - Valor do Frete Autonomo              ( Opcional )
                    SC6->C6_VALOR,;           // 13 - Valor da Mercadoria                  ( Obrigatorio )
                    0,;                       // 14 - Valor da Embalagem                   ( Opcional )
                    SB1->(RecNo()),;          // 15 - RecNo do SB1
                    SF4->(RecNo()))           // 16 - RecNo do SF4
                
                nQtdPeso := SC6->C6_QTDVEN * SB1->B1_PESO
                MaFisLoad("IT_VALMERC", SC6->C6_VALOR, nOrderItem)				
                MaFisAlt("IT_PESO", nQtdPeso, nOrderItem)

                //Adiciona no array de referências
                aAdd(aDataSC6, {nOrderItem, SC6->(RecNo())})
                
                QRY_ITE->(DbSkip())
            EndDo
            QRY_ITE->(DbCloseArea())

            //Ajusta informações dos cálculos dos impostos
            MaFisAlt("NF_FRETE", SC5->C5_FRETE)
            MaFisAlt("NF_SEGURO", SC5->C5_SEGURO)
            MaFisAlt("NF_DESPESA", SC5->C5_DESPESA) 
            MaFisAlt("NF_AUTONOMO", SC5->C5_FRETAUT)
            If SC5->C5_DESCONT > 0
                MaFisAlt("NF_DESCONTO", Min(MaFisRet(, "NF_VALMERC")-0.01, SC5->C5_DESCONT+MaFisRet(, "NF_DESCONTO")) )
            EndIf
            If SC5->C5_PDESCAB > 0
                MaFisAlt("NF_DESCONTO", A410Arred(MaFisRet(, "NF_VALMERC")*SC5->C5_PDESCAB/100, "C6_VALOR") + MaFisRet(, "NF_DESCONTO"))
            EndIf
        EndIf
	
		//Incrementando a regua
		nCurrent++
		oReport:SetMsgPrint("Imprimindo pedido " + cValToChar(nCurrent) + " de " + cValToChar(nTotal) + "...")
		oReport:IncMeter()

        //Agora percorre as informações da SC6
        For nOrderItem := 1 To Len(aDataSC6)
            //Posiciona no item do pedido
            DbSelectArea("SC6")
            SC6->(DbGoTo(aDataSC6[nOrderItem][2]))

            //Se o produto NÃO estiver entre os parâmetros, vai pular para não imprimir
            If ! (SC6->C6_PRODUTO >= MV_PAR07 .And. SC6->C6_PRODUTO <= MV_PAR08)
                Loop
            EndIf
		
            //Se for uma linha Par, irá imprimir o fundo
            If lEvenNumber
                nBrushCurrentLine := oReport:Row() //Pega a linha atual no relatório
                oReport:FillRect({nBrushCurrentLine, nBrushInitColumn, nBrushCurrentLine + nBrushHeight, nBrushWidth}, oBrush)
            EndIf

            //Busca o valor com impostos
            nOrderValueWithTax := MaFisRet(nOrderItem, "IT_TOTAL")
			nValPedTot := MaFisRet(, "NF_TOTAL")
            
            //Imprimindo a linha atual
            oSection:Cell("XX_VALTOT"):SetValue(nOrderValueWithTax)
			oSection:Cell("XX_PEDTOT"):SetValue(nValPedTot)
            oSection:PrintLine()
            
            //Atualiza entre par e ímpar
            lEvenNumber := ! lEvenNumber
        Next
		
		QRY_REP->(DbSkip())
	EndDo
	oSection:Finish()
	QRY_REP->(DbCloseArea())
	
	FWRestArea(aArea)
Return

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.

Deixe uma resposta

Terminal de Informação