Importação de pré nota de entrada (via csv ou txt) | Ti Responde 013

No vídeo de hoje, vou demonstrar em como importar uma pré nota de entrada (SF1 e SD1) via csv ou txt.

Hoje, a dúvida foi feita pelo grande Roberto, onde ele tinha uma dúvida, em como fazer uma importação de csv ou txt para uma pré nota de entrada (SF1 e SD1).

Com isso em mente, fiz um vídeo explicando como fazer a leitura linha a linha e acionar a importação dos dados de forma correta via ExecAuto.

E abaixo o código fonte desenvolvido para exemplificar:

//Bibliotecas
#Include "TOTVS.ch"

/*/{Protheus.doc} User Function zVid0013
Função que realiza a importação do CSV como Pré Nota
@type  Function
@author Atilio
@since 23/02/2022
/*/

User Function zVid0013()
    Local aArea   := FWGetArea()
    Local cDirIni := GetTempPath()
    Local cTipArq := "Arquivos com separações (*.csv)"
    Local cTitulo := "Seleção de Arquivos para Processamento"
    Local lSalvar := .F.
    Local cArqSel := ""
 
    //Se não estiver sendo executado via job
    If ! IsBlind()
 
        //Chama a função para buscar arquivos
        cArqSel := tFileDialog(;
            cTipArq,;  // Filtragem de tipos de arquivos que serão selecionados
            cTitulo,;  // Título da Janela para seleção dos arquivos
            ,;         // Compatibilidade
            cDirIni,;  // Diretório inicial da busca de arquivos
            lSalvar,;  // Se for .T., será uma Save Dialog, senão será Open Dialog
            ;          // Se não passar parâmetro, irá pegar apenas 1 arquivo; Se for informado GETF_MULTISELECT será possível pegar mais de 1 arquivo; Se for informado GETF_RETDIRECTORY será possível selecionar o diretório
        )

        //Se tiver o arquivo selecionado e ele existir
        If ! Empty(cArqSel) .And. File(cArqSel)
            Processa({|| fImporta(cArqSel) }, "Importando...")
        EndIf
    EndIf

    FWRestArea(aArea)
Return

Static Function fImporta(cArqSel)
    Local cDirLog    := GetTempPath()
    Local cArqLog    := "zFat07Im_" + dToS(Date()) + "_" + StrTran(Time(), ':', '-') + ".log"
	Local nTotLinhas := 0
	Local cLinAtu    := ""
	Local nLinhaAtu  := 0
	Local aLinha     := {}
    Local oArquivo
    Local aLinhas
    Local aLinhaSD1 := {}
    Local cItem     := StrTran(Space(TamSX3('D1_ITEM')[01]), ' ', '0')
    //Primeira coluna do Excel, será o tipo da linha
    Private nPosTip  := 1 //SF1 ou SD1
    //Posições do Cabeçalho (SF1)
    Private nCabTipo := 2 //F1_TIPO
    Private nCabForm := 3 //F1_FORMUL
    Private nCabDocu := 4 //F1_DOC
    Private nCabSeri := 5 //F1_SERIE
    Private nCabEmis := 6 //F1_EMISSAO
    Private nCabForn := 7 //F1_FORNECE
    Private nCabLoja := 8 //F1_LOJA
    Private nCabEspe := 9 //F1_ESPECIE
    Private nCabCond := 10 //F1_COND
    //Posições dos Itens (SD1)
    Private nIteProd := 2 //D1_COD
    Private nIteQtde := 3 //D1_QUANT
    Private nIteVUni := 4 //D1_VUNIT
    Private nIteTpES := 5 //D1_TES
    //Variáveis do ExecAuto
    Private aCabecSF1       := {}
    Private aItensSD1       := {}
    Private lMSHelpAuto     := .T.
    Private lAutoErrNoFile  := .T.
    Private lMsErroAuto     := .F.
    Private cLog            := ""
    Private cChaveSF1       := ""
	
    //Abre as tabelas que serão usadas
	DbSelectArea('SF1')
	SF1->(DbSetOrder(1)) //F1_FILIAL + F1_DOC + F1_SERIE + F1_FORNECE + F1_LOJA + F1_TIPO
	SF1->(DbGoTop())
			
    //Definindo o arquivo a ser lido
    oArquivo := FWFileReader():New(cArqSel)
    
    //Se o arquivo pode ser aberto
    If (oArquivo:Open())

        //Se não for fim do arquivo
        If ! (oArquivo:EoF())

            //Definindo o tamanho da régua
            aLinhas := oArquivo:GetAllLines()
            nTotLinhas := Len(aLinhas)
            ProcRegua(nTotLinhas)
            
            //Método GoTop não funciona (dependendo da versão da LIB), deve fechar e abrir novamente o arquivo
            oArquivo:Close()
            oArquivo := FWFileReader():New(cArqSel)
            oArquivo:Open()

            //Iniciando controle de transação
            Begin Transaction

                //Enquanto tiver linhas
                While (oArquivo:HasLine())

                    //Incrementa na tela a mensagem
                    nLinhaAtu++
                    IncProc("Analisando linha " + cValToChar(nLinhaAtu) + " de " + cValToChar(nTotLinhas) + "...")
                    
                    //Pegando a linha atual e transformando em array
                    cLinAtu := oArquivo:GetLine()
                    aLinha  := StrTokArr(cLinAtu, ";")
                    
                    //Se houver posições no array
                    If Len(aLinha) > 0
                        //Se for cabeçalho
                        If Upper(aLinha[nPosTip]) == "SF1"
                            //Se tiver cabeçalho e itens, chama a rotina para acionar o ExecAuto
                            fPreNota()

                            //Zera as variáveis
                            aCabecSF1 := {}
                            aItensSD1 := {}
                            cItem     := StrTran(Space(TamSX3('D1_ITEM')[01]), ' ', '0')
                            cChaveSF1 := ""

                            //Se tiver o mesmo numero de colunas, adiciona no array da sf1, e monta a chave que será pesquisada no seek
                            If Len(aLinha) == nCabCond
                                //Transforma a data
                                If "/" $ aLinha[nCabEmis]
                                    aLinha[nCabEmis] := cToD(aLinha[nCabEmis])
                                Else
                                    aLinha[nCabEmis] := sToD(aLinha[nCabEmis])
                                EndIf

                                aAdd(aCabecSF1, {"F1_TIPO"    , aLinha[nCabTipo] , Nil})
                                aAdd(aCabecSF1, {"F1_FORMUL"  , aLinha[nCabForm] , Nil})
                                aAdd(aCabecSF1, {"F1_DOC"     , aLinha[nCabDocu] , Nil})
                                aAdd(aCabecSF1, {"F1_SERIE"   , aLinha[nCabSeri] , Nil})
                                aAdd(aCabecSF1, {"F1_EMISSAO" , aLinha[nCabEmis] , Nil})
                                aAdd(aCabecSF1, {"F1_FORNECE" , aLinha[nCabForn] , Nil})
                                aAdd(aCabecSF1, {"F1_LOJA"    , aLinha[nCabLoja] , Nil})
                                aAdd(aCabecSF1, {"F1_ESPECIE" , aLinha[nCabEspe] , Nil})
                                aAdd(aCabecSF1, {"F1_COND"    , aLinha[nCabCond] , Nil})

                                cChaveSF1 := FWxFilial("SF1") +;
                                    PadR(aLinha[nCabDocu], TamSX3("F1_DOC")[1], ' ') +;
                                    PadR(aLinha[nCabSeri], TamSX3("F1_SERIE")[1], ' ') +;
                                    PadR(aLinha[nCabForn], TamSX3("F1_FORNECE")[1], ' ') +;
                                    PadR(aLinha[nCabLoja], TamSX3("F1_LOJA")[1], ' ') +;
                                    PadR(aLinha[nCabTipo], TamSX3("F1_TIPO")[1], ' ')
                            EndIf

                        //Se for itens (e tiver todas as posições)
                        ElseIf Upper(aLinha[nPosTip]) == "SD1" .And. Len(aLinha) == nIteTpES
                            //Campos numéricos, retira ponto, transforma vírgula em ponto e converte para numérico
                            aLinha[nIteQtde] := Alltrim(aLinha[nIteQtde])
                            aLinha[nIteQtde] := StrTran(aLinha[nIteQtde], ".", "")
                            aLinha[nIteQtde] := StrTran(aLinha[nIteQtde], ",", ".")
                            aLinha[nIteQtde] := Val(aLinha[nIteQtde])
                            aLinha[nIteVUni] := Alltrim(aLinha[nIteVUni])
                            aLinha[nIteVUni] := StrTran(aLinha[nIteVUni], ".", "")
                            aLinha[nIteVUni] := StrTran(aLinha[nIteVUni], ",", ".")
                            aLinha[nIteVUni] := Val(aLinha[nIteVUni])

                            //Adiciona no array de Itens
                            cItem := Soma1(cItem)
                            aLinhaSD1 := {}
                            aAdd(aLinhaSD1, {"D1_ITEM"  , cItem     		                  , Nil} )
                            aAdd(aLinhaSD1, {"D1_COD"   , aLinha[nIteProd]                    , Nil} )
                            aAdd(aLinhaSD1, {"D1_QUANT" , aLinha[nIteQtde]                    , Nil} )
                            aAdd(aLinhaSD1, {"D1_VUNIT" , aLinha[nIteVUni]                    , Nil} )
                            aAdd(aLinhaSD1, {"D1_TOTAL" , aLinha[nIteQtde] * aLinha[nIteVUni] , Nil} )
                            aAdd(aLinhaSD1, {"D1_TES"   , aLinha[nIteTpES]                    , Nil} )
                            aAdd(aItensSD1, aClone(aLinhaSD1))
                        EndIf
                    EndIf
                    
                EndDo

                //Chama novamente a rotina de pré-nota pois pode ser que sobrou um cabec e itens para processar
                fPreNota()
            End Transaction
        
            //Se tiver log, mostra ele
            If ! Empty(cLog)
                MemoWrite(cDirLog + cArqLog, cLog)
                ShellExecute("OPEN", cArqLog, "", cDirLog, 1)
            EndIf

        Else
            MsgStop("Arquivo não tem conteúdo!", "Atenção")
        EndIf

        //Fecha o arquivo
        oArquivo:Close()
    Else
        MsgStop("Arquivo não pode ser aberto!", "Atenção")
    EndIf

Return

Static Function fPreNota()
    Local cPastaErro := ""
    Local cNomeErro  := ""
    Local cTextoErro := ""
    Local aLogErro   := {}
    Local nLinhaErro := 0

    //Se tiver cabeçalho e itens
    If Len(aCabecSF1) > 0 .And. Len(aItensSD1) > 0
        //Se conseguir posicionar na nota, grava no log que já existe
        If SF1->(MsSeek(cChaveSF1))
            cLog += "- NF já existe na base, chave de pesquisa: " + cChaveSF1 + CRLF

        //Aciona o ExecAuto
        Else
            lMsErroAuto := .F.
            MSExecAuto({|x, y, z| MATA140(x, y, z)}, aCabecSF1, aItensSD1, 3)
            
            //Se houve erro, gera o log
            If lMsErroAuto
                cPastaErro := "\x_logs\"
                cNomeErro  := "pre_nota_" + dToS(Date()) + "_" + StrTran(Time(), ":", "-") + ".txt"

                //Se a pasta de erro não existir, cria ela
                If ! ExistDir(cPastaErro)
                    MakeDir(cPastaErro)
                EndIf

                //Pegando log do ExecAuto, percorrendo e incrementando o texto
                aLogErro := GetAutoGRLog()
                For nLinhaErro := 1 To Len(aLogErro)
                    cTextoErro += aLogErro[nLinhaErro] + CRLF
                Next
                
                //Criando o arquivo txt e incrementa o log
                MemoWrite(cPastaErro + cNomeErro, cTextoErro)
                cLog += "- Falha ao incluir NF, chave de pesquisa: " + cChaveSF1 + ", arquivo de log em '" + cPastaErro + cNomeErro + "' " + CRLF
            Else
                cLog += "- NF incluida como pré-nota, chave de pesquisa: " + cChaveSF1 + CRLF
            EndIf
        EndIf
    EndIf
Return

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

8 Responses

  1. Roberto disse:

    Como sempre, mais um excelente artigo.

  2. Leandro disse:

    Este artigo foi muito útil para nossa empresa, gostaria de saber se aceita proposta para desenvolver e incluir mais algumas necessidades nesse fonte

  3. Felipe P. disse:

    Bom dia Dan, tudo bem?
    Achei esse vídeo porque é o fim para os meios que eu preciso rs… porém eu estive pensando em outra coisa, e não sei se já fez algo do tipo.

    Tem conhecimento de algum ponto de entrada na MATA103/MATA140 onde eu posso trazer os itens de uma devolução porém todos deletados, para que eu possa desmarcar manualmente? No caso da devolução seria usando o Retornar (devolução de vendas).

    Obrigado pelo post!

    • Boa noite Felipe, tudo joia graças a Deus e você?
      Eu nunca precisei fazer um p.e. para esse caso. Tentei procurar e não consegui encontrar.
      Caso não encontre, você pode utilizar o ChkExec e adicionar um atalho, por exemplo, um F8 ou F9, ai ao pressionar, ele excluiria tudo da grid para o usuário depois selecionar.
      Grande abraço.

  4. Felipe P. disse:

    Fala Dan! Achei outra forma de fazer isso. O deletar é típico do aHeader, até aí ok.. mas pra validar os valores do aCols quando eu desmarco, precisei passar o MaFisDel ali pois ele não estava considerando pro F1_TOTAL e pra SF3 também.

    Segue o que customizei:

    User Function DeletaLinha

    Local nX

    If FwIsInCallStack(“SA103Devol”)
    For nX := 1 to Len(aCols)
    aCols[nX][Len(aHeader)+1] := .T.
    MaFisDel(nX,aCols[nX][Len(aCols[nX])])
    Next
    EndIf

    Return

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