Importar um XLS via AdvPL | Ti Responde 040

No vídeo de hoje, vamos demonstrar em como importar um arquivo XLS (do Excel) via AdvPL.

Hoje, a dúvida foi feita por alguns alunos, se existia alguma forma de importar um Excel sem ter que salvar manualmente com a extensão CSV.

Então foi criado um batch com um script que converte automaticamente o arquivo selecionado de XLS para CSV e assim realiza a importação.

E abaixo o código fonte desenvolvido para exemplificar:

//Bibliotecas
#Include "TOTVS.ch"

/*/{Protheus.doc} User Function zVid0040
Função que realiza a leitura de um XLS convertendo ele para CSV para importar os dados (como se fosse um CSV)
@type  Function
@author Atilio
@since 01/09/2022
@obs Pessoal, essa função é um paliativo de exemplo, para ler um XLS transformando ele em CSV
    Caso você queira se aprofundar, ou ver uma classe que vai além, com opção de leitura e
    escrita, eu recomendo que você conheça a YExcel, que é um projeto excelente do Saulo Martins

    + Link para download: https://github.com/saulogm/advpl-excel/blob/master/src/YEXCEL.prw
    + Link de exemplo:    https://github.com/saulogm/advpl-excel/blob/master/exemplo/tstyexcel.prw
    + Documentação:       https://github.com/saulogm/advpl-excel
/*/

User Function zVid0040()
	Local aArea     := FWGetArea()
	Local cDirIni   := GetTempPath()
	Local cTipArq   := 'Arquivos Excel (*.xlsx) | Arquivos Excel 97-2003 (*.xls)'
	Local cTitulo   := 'Seleção de Arquivos para Processamento'
	Local lSalvar   := .F.
	Local cArqSel   := ''
	Private cArqCSV := ""
 
	//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)
			//Faz a conversão de XLS para CSV
            cArqCSV := fXLStoCSV(cArqSel)

			//Se o arquivo XLS existir
            If File(cArqCSV)
	            Processa({|| fImporta(cArqCSV) }, 'Importando...')
            EndIf
	    EndIf
	EndIf
	
	FWRestArea(aArea)
Return
	
/*/{Protheus.doc} fImporta
Função que processa o arquivo e realiza a importação para o sistema
@author Daniel Atilio
@since 16/07/2022
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fImporta(cArqSel)
	Local cDirTmp    := GetTempPath()
	Local cArqLog    := 'importacao_' + dToS(Date()) + '_' + StrTran(Time(), ':', '-') + '.log'
	Local nTotLinhas := 0
	Local cLinAtu    := ''
	Local nLinhaAtu  := 0
	Local aLinha     := {}
	Local oArquivo
	Local cPastaErro := '\x_logs\'
	Local cNomeErro  := ''
	Local cTextoErro := ''
	Local aLogErro   := {}
	Local nLinhaErro := 0
	Local cLog       := ''
	//Variáveis do ExecAuto
	Private aDados         := {}
	Private lMSHelpAuto    := .T.
	Private lAutoErrNoFile := .T.
	Private lMsErroAuto    := .F.
	//Variáveis da Importação
	Private cAliasImp  := 'SBM'
	Private cSeparador := ','

	//Abre as tabelas que serão usadas
	DbSelectArea(cAliasImp)
	(cAliasImp)->(DbSetOrder(1))
	(cAliasImp)->(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  := Separa(cLinAtu, cSeparador)

					//Se houver posições no array
					If Len(aLinha) > 0
						aDados := {}
						aAdd(aDados, {'BM_GRUPO', aLinha[1], Nil})
						aAdd(aDados, {'BM_DESC', aLinha[2], Nil})

						lMsErroAuto := .F.
						MSExecAuto({|x, y| MATA035(x, y)}, aDados, 3)

						//Se houve erro, gera o log
						If lMsErroAuto
							cPastaErro := '\x_logs\'
							cNomeErro  := 'erro_' + cAliasImp + '_' + 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 registro, linha [' + cValToChar(nLinhaAtu) + '], arquivo de log em ' + cPastaErro + cNomeErro + CRLF
						Else
							cLog += '+ Sucesso no Execauto na linha ' + cValToChar(nLinhaAtu) + ';' + CRLF
						EndIf

					EndIf

				EndDo
			End Transaction

			//Se tiver log, mostra ele
			If ! Empty(cLog)
				MemoWrite(cDirTmp + cArqLog, cLog)
				ShellExecute('OPEN', cArqLog, '', cDirTmp, 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

//Essa função foi baseada como referência no seguinte link: https://stackoverflow.com/questions/1858195/convert-xls-to-csv-on-command-line
Static Function fXLStoCSV(cArqXLS)
	Local cArqCSV    := ""
	Local cDirTemp   := GetTempPath()
	Local cArqScript := cDirTemp + "XlsToCsv.vbs"
	Local cScript    := ""
	Local cDrive     := ""
	Local cDiretorio := ""
	Local cNome      := ""
	Local cExtensao  := ""

	//Monta o Script para converter
	cScript := 'if WScript.Arguments.Count < 2 Then' + CRLF
	cScript += '    WScript.Echo "Error! Please specify the source path and the destination. Usage: XlsToCsv SourcePath.xls Destination.csv"' + CRLF
	cScript += '    Wscript.Quit' + CRLF
	cScript += 'End If' + CRLF
	cScript += 'Dim oExcel' + CRLF
	cScript += 'Set oExcel = CreateObject("Excel.Application")' + CRLF
	cScript += 'Dim oBook' + CRLF
	cScript += 'Set oBook = oExcel.Workbooks.Open(Wscript.Arguments.Item(0))' + CRLF
	cScript += 'oBook.SaveAs WScript.Arguments.Item(1), 6' + CRLF
	cScript += 'oBook.Close False' + CRLF
	cScript += 'oExcel.Quit' + CRLF
	MemoWrite(cArqScript, cScript)

	//Pega os detalhes do arquivo original em XLS
	SplitPath(cArqXLS, @cDrive, @cDiretorio, @cNome, @cExtensao)

	//Monta o nome do CSV, conforme os detalhes do XLS
	cArqCSV := cDrive + cDiretorio + cNome + ".csv"

	//Executa a conversão, exemplo: 
	//   c:\totvs\Testes\XlsToCsv.vbs "C:\Users\danat\Downloads\tste2.xls" "C:\Users\danat\Downloads\tst2_csv.csv"
	ShellExecute("OPEN", cArqScript, ' "' + cArqXLS + '" "' + cArqCSV + '"', cDirTemp, 0 )

Return cArqCSV

Observação:

Pessoal, essa função é um paliativo de exemplo, para ler um XLS transformando ele em CSV.
Caso você queira se aprofundar, ou ver uma classe que vai além, com opção de leitura e escrita, eu recomendo que você conheça a YExcel, que é um projeto excelente do Saulo Martins

Bom pessoal, por hoje é só.

Abraços e até a próxima.

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

Deixe uma resposta