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 Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

Deixe uma resposta