Como fazer a importação de um arquivo csv ou txt via AdvPL

No artigo de hoje, irei mostrar para vocês como importar um arquivo csv ou txt via AdvPL.

Muitos alunos me perguntam como seria a lógica para importar registros em AdvPL com o conteúdo de um arquivo txt ou csv.

Então jovens, a lógica seria assim:

  1. Buscar o caminho do arquivo apontado pelo usuário (TFileDialog)
  2. Tentar realizar a abertura do arquivo (FWFileReader)
  3. Ler todas as linhas do arquivo (While com método HasLine)
  4. Na linha atual, transformar o texto em um array (StrTokArr ou Separa)
  5. Tentar posicionar no registro (DbSeek)
  6. Fazer a gravação dos dados (via MsExecAuto ou RecLock)

Com esse contexto acima, iremos montar um exemplo, onde no arquivo CSV, existam as seguintes colunas:

  • Código do Fornecedor (A2_COD)
  • Loja do Fornecedor (A2_LOJA)
  • Razão Social (A2_NOME)
  • Observações (A2_X_OBS) – campo customizado que será gravado
//Bibliotecas
#Include "TOTVS.ch"
#Include "TopConn.ch"
 
//Posições do Array
Static nPosCodigo := 1 //Coluna A no Excel
Static nPosLojFor := 2 //Coluna B no Excel
Static nPosRazSoc := 3 //Coluna C no Excel
Static nPosObserv := 4 //Coluna D no Excel
 
/*/{Protheus.doc} zImpCSV
Função para importar informações do fornecedor via csv
@author Atilio
@since 07/06/2021
@version 1.0
@type function
/*/
 
User Function zImpCSV()
    Local aArea     := GetArea()
    Private cArqOri := ""
 
    //Mostra o Prompt para selecionar arquivos
    cArqOri := tFileDialog( "CSV files (*.csv) ", 'Seleção de Arquivos', , , .F., )
     
    //Se tiver o arquivo de origem
    If ! Empty(cArqOri)
         
        //Somente se existir o arquivo e for com a extensão CSV
        If File(cArqOri) .And. Upper(SubStr(cArqOri, RAt('.', cArqOri) + 1, 3)) == 'CSV'
            Processa({|| fImporta() }, "Importando...")
        Else
            MsgStop("Arquivo e/ou extensão inválida!", "Atenção")
        EndIf
    EndIf
     
    RestArea(aArea)
Return
 
/*-------------------------------------------------------------------------------*
 | Func:  fImporta                                                               |
 | Desc:  Função que importa os dados                                            |
 *-------------------------------------------------------------------------------*/
 
Static Function fImporta()
    Local aArea      := GetArea()
    Local cArqLog    := "zImpCSV_" + dToS(Date()) + "_" + StrTran(Time(), ':', '-') + ".log"
    Local nTotLinhas := 0
    Local cLinAtu    := ""
    Local nLinhaAtu  := 0
    Local aLinha     := {}
    Local oArquivo
    Local aLinhas
    Local cCodForn   := ""
    Local cLojForn   := ""
    Local cObserv    := ""
    Local cRazaoSoc  := ""
    Private cDirLog    := GetTempPath() + "x_importacao\"
    Private cLog       := ""
     
    //Se a pasta de log não existir, cria ela
    If ! ExistDir(cDirLog)
        MakeDir(cDirLog)
    EndIf
 
    //Definindo o arquivo a ser lido
    oArquivo := FWFileReader():New(cArqOri)
     
    //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(cArqOri)
            oArquivo:Open()
 
            //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 não for o cabeçalho (encontrar o texto "Código" na linha atual)
                If ! "código" $ Lower(cLinAtu)
 
                    //Zera as variaveis
                    cCodForn   := aLinha[nPosCodigo]
                    cLojForn   := aLinha[nPosLojFor]
                    cRazaoSoc  := aLinha[nPosRazSoc]
                    cObserv    := aLinha[nPosObserv]
 
                    DbSelectArea('SA2')
                    SA2->(DbSetOrder(1)) // Filial + Código + Loja
 
                    //Se conseguir posicionar no fornecedor
                    If SA2->(DbSeek(FWxFilial('SA2') + cCodForn + cLojForn))
                        cLog += "+ Lin" + cValToChar(nLinhaAtu) + ", fornecedor [" + cCodForn + cLojForn + " - " + Upper(SA2->A2_NREDUZ) + "] " +;
                            "a observação foi alterada, antes: [" + Alltrim(SA2->A2_X_OBS) + "], agora: [" + Alltrim(cObserv) + "];" + CRLF
 
                        //Realiza a alteração do fornecedor
                        RecLock('SA2', .F.)
                            SA2->A2_X_OBS  := cObserv
                        SA2->(MsUnlock())
 
                    Else
                        cLog += "- Lin" + cValToChar(nLinhaAtu) + ", fornecedor e loja [" + cCodForn + cLojForn + "] não encontrados no Protheus;" + CRLF
                    EndIf
                     
                Else
                    cLog += "- Lin" + cValToChar(nLinhaAtu) + ", linha não processada - cabeçalho;" + CRLF
                EndIf
                 
            EndDo
 
            //Se tiver log, mostra ele
            If ! Empty(cLog)
                cLog := "Processamento finalizado, abaixo as mensagens de log: " + CRLF + 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
 
    RestArea(aArea)
Return

Obs.: No nosso exemplo, fizemos um RecLock, pois iremos gravar um campo customizado na SA1, o A1_X_OBS. Se no seu caso for uma inclusão em tabela padrão ou alteração de campos padrões, tente utilizar o ExecAuto por boas práticas.

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.

2 Responses

  1. Edson Novaes disse:

    Olá Atilio,
    Fiz a alteração no fonte para tabela CC2, o campo que você chama A2_X_OBS alterei para CC2_PRZENT , esse campo está em branco na tabela mais no CSV ele está preenchido com dois caracteres e precisa preencher somente ele na tabela que é referente ao prazo de entrega mais o log informar que linha não foi processada.

    //Posições do Array
    Static nPosEstMun := 1 //Coluna A no Excel
    Static nPosCodMun := 2 //Coluna B no Excel
    Static nPosMun := 3 //Coluna C no Excel
    Static nPosPrzEnt := 4 //Coluna D no Excel

    User Function zImpCSV()
    Local aArea := GetArea()
    Private cArqOri := “”

    //Mostra o Prompt para selecionar arquivos
    cArqOri := tFileDialog( “CSV files (*.csv) “, ‘Seleção de Arquivos’, , , .F., )

    //Se tiver o arquivo de origem
    If ! Empty(cArqOri)

    //Somente se existir o arquivo e for com a extensão CSV
    If File(cArqOri) .And. Upper(SubStr(cArqOri, RAt(‘.’, cArqOri) + 1, 3)) == ‘CSV’
    Processa({|| fImporta() }, “Importando…”)
    Else
    MsgStop(“Arquivo e/ou extensão inválida!”, “Atenção”)
    EndIf
    EndIf

    RestArea(aArea)
    Return

    Static Function fImporta()
    Local aArea := GetArea()
    Local cArqLog := “zImpCSV_” + dToS(Date()) + “_” + StrTran(Time(), ‘:’, ‘-‘) + “.log”
    Local nTotLinhas := 0
    Local cLinAtu := “”
    Local nLinhaAtu := 0
    Local aLinha := {}
    Local oArquivo
    Local aLinhas
    Local cEstMun := “”
    Local cCodmun := “”
    Local cPrzEnt := “”
    Local cMun := “”
    Private cDirLog := GetTempPath() + “x_importacao\”
    Private cLog := “”

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

    //Definindo o arquivo a ser lido
    oArquivo := FWFileReader():New(cArqOri)

    //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(cArqOri)
    oArquivo:Open()

    //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 não for o cabeçalho (encontrar o texto “Código” na linha atual)
    If “código” $ Lower(cLinAtu)

    //Zera as variaveis
    cEstMun := aLinha[nPosEstMun]
    cCodmun := aLinha[nPosCodMun]
    cMun := aLinha[nPosMun]
    cPrzEnt := aLinha[nPosPrzEnt]

    DbSelectArea(‘CC2’)
    CC2->(DbSetOrder(1)) // Filial + Código + Loja

    //Se conseguir posicionar no fornecedor
    If CC2->(DbSeek(FWxFilial(‘CC2’) + cEstMun + cCodmun))
    cLog += “+ Lin” + cValToChar(nLinhaAtu) + “, fornecedor [” + cEstMun + cCodmun + ” – ” + Upper(CC2->CC2_MUN) + “] ” +;
    “a observação foi alterada, antes: [” + Alltrim(CC2->CC2_PRZENT) + “], agora: [” + Alltrim(cPrzEnt) + “];” + CRLF

    //Realiza a alteração do fornecedor
    RecLock(‘CC2’, .F.)
    CC2->CC2_PRZENT := cPrzEnt
    CC2->(MsUnlock())

    Else
    cLog += “- Lin” + cValToChar(nLinhaAtu) + “, fornecedor e loja [” + cEstMun + cCodmun + “] não encontrados no Protheus;” + CRLF
    EndIf

    Else
    cLog += “- Lin” + cValToChar(nLinhaAtu) + “, linha não processada – cabeçalho;” + CRLF
    EndIf

    EndDo

    //Se tiver log, mostra ele
    If ! Empty(cLog)
    cLog := “Processamento finalizado, abaixo as mensagens de log: ” + CRLF + 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

    RestArea(aArea)
    Return

    • Boa tarde Edson, tudo joia?

      Opa, vimos aqui, faltou uma exclamação no if do exemplo. Então ao invés disso:

      If "código" $ Lower(cLinAtu)
      

      Tem que ser assim:

      If ! "código" $ Lower(cLinAtu)
      

      Já corrigimos no artigo, obrigado pelo feedback.

      Um grande abraço.

Deixe uma resposta

Terminal de Informação