Como usar BI com TCloud | Ti Responde 016

No vídeo de hoje, vamos demonstrar em como exportar dados do TCloud para usar em um BI de uma base local.

Hoje, a dúvida foi feita pelo grande Bruno, onde havia a necessidade de integrar o TCloud com algum sistema de BI, como o PowerBI.

O procedimento feito, foi criar dois fontes, um que roda exportando os dados do Cloud, e outro que roda na base local, importando esses dados.

Acontece que esse procedimento demonstrado no vídeo, é um paliativo de rotinas de exportação (do Cloud) e importação (uma base local), o ideal mesmo é se quiser usar o BI com Cloud, é assinar com a TOTVS a versão que eles disponibilizam acesso ao banco de dados.

E abaixo o código fonte desenvolvido para exemplificar. Fonte zExporta (exportação dos dados do Cloud):

//Bibliotecas
#Include "TOTVS.ch"
#Include "TopConn.ch"
#Include "FileIO.ch"

/*/{Protheus.doc} User Function zExporta
Função que realiza a exportação das tabelas para arquivos txt dentro da Protheus Data
@type  Function
@author Atilio
@since 12/08/2021
@version version
@obs Esse fonte tem que ser compilado e executado na base TCloud
/*/

User Function zExporta()
    Local aArea
    Local lContinua := .F.
    Private lJobPvt := .F.

    //Se não tiver ambiente aberto, é job
    If Select("SX2") == 0
        //Reseta o ambiente e abre ele novamente
        RpcClearEnv()
        RpcSetEnv("01", "0101", "usuario", "senha", "")
        lJobPvt := .T.
        lContinua := .T.
    Else
        lContinua := MsgYesNo("Deseja executar a exportação geral de dados para o Banco Local?", "Atenção")
    EndIf
    aArea := GetArea()

    //Se for continuar, irá chamar a rotina de processamento
    If lContinua
        If ! LockByName("zExporta", .T., .F.)

        Else
            Processa({|| fExporta() }, "Exportando...")
            UnLockByName("zExporta", .T., .F.)
        EndIf
    EndIf

    RestArea(aArea)
Return

Static Function fExporta()
    Local aArea     := GetArea()
    Local cPasta    := "\x_bancolocal\"
    Local cArquivo  := ""
    Local nAtual    := 0
    Local nTotal    := 0
	Local cTabAtu   := ""
	Local aTabelas  := {}
    Local cQryExp   := ""    
    Local cCabecalho := ""
    Local nCampo     := 0
    Local aEstrutTab := {}
    Private cDelim   := ''

    //Se a pasta não existir, cria a pasta
    If ! ExistDir(cPasta)
        MakeDir(cPasta)
    EndIf

	aTabelas := {"SB1", "SBM"} //aqui você pode por as outras tabelas
	nTotal := Len(aTabelas)
    
    //Enquanto houver dados
    For nAtual := 1 To Len(aTabelas)
        //Incrementa a régua
        nAtual++
        IncProc("Exportando tabela " + Alltrim(cTabAtu) + " (" + cValToChar(nAtual) + " de " + cValToChar(nTotal) + ")...")

        //Busca a estrutura da tabela
		cTabAtu := aTabelas[nAtual]
        DbSelectArea(cTabAtu)
        aEstrutTab := (cTabAtu)->(DbStruct())

        //Monta a query de exportação
        cQryExp := " SELECT " + CRLF
        cQryExp += "     R_E_C_N_O_ AS RECNO, " + CRLF
        cQryExp += "     D_E_L_E_T_ AS DELET " + CRLF
        For nCampo := 1 To Len(aEstrutTab)
            cQryExp += ", " + CRLF

            //Se for campo caractere, irá remover as vírgulas para não dar problema na importação
            //  Por algum motivo, sem o substring, ele estourava o campo, como se 
            //  ele entendesse uma estrutura nova aumentando o tamanho do campo
            If aEstrutTab[nCampo][2] == "C"
                cQryExp += "     SUBSTRING(  REPLACE(REPLACE(REPLACE( REPLACE(" + aEstrutTab[nCampo][1] + ", ',', ''), CHAR(13), '\n'), CHAR(10), ''), '''', '')  , 1, " + cValToChar(aEstrutTab[nCampo][3]) + ") AS " + aEstrutTab[nCampo][1]

            ElseIf aEstrutTab[nCampo][2] == "M"
                cQryExp += "     REPLACE(REPLACE(REPLACE( REPLACE(ISNULL(CAST(CAST(" + aEstrutTab[nCampo][1] + " AS VARBINARY(8000)) AS VARCHAR(8000)),''), ',', ''), CHAR(13), '\n'), CHAR(10), ''), '''', '') AS " + aEstrutTab[nCampo][1]

            //Senão (numérico, lógico ou data), adiciona direto
            Else
                cQryExp += "     " + aEstrutTab[nCampo][1]
            EndIf
        Next
        cQryExp += CRLF
        cQryExp += " FROM " + CRLF
        cQryExp += "     " + RetSQLName(cTabAtu) + " TAB " + CRLF
        cQryExp += " WHERE " + CRLF
		cQryExp += "     D_E_L_E_T_ = '' " + CRLF
        cQryExp += " ORDER BY " + CRLF
        cQryExp += "     RECNO " + CRLF
		
		//Coloco a flag como T de Total, caso você mude a query para gerar poucos registros, ou com filtros de período, use a flag P de parcial
		cTipoExp := "T"

        //Executa a query e realiza a exportação
        PLSQuery(cQryExp, 'QRY_EXP')
        If ! QRY_EXP->(EoF())
            //Monta o nome do arquivo que será gerado, e se ele existir, já apaga da Protheus Data
            cArquivo := cTipoExp + "_" + Alltrim(cTabAtu) + "_" + dToS(Date()) + "_" + StrTran(Time(), ":", "-") + ".txt"
            If File(cPasta + cArquivo)
                FErase(cPasta + cArquivo)
            EndIf

            //Realiza a exportação
            Copy To (cPasta + cArquivo) DELIMITED WITH (cDelim)
            cCabecalho := fCabecalho()

            //Insere o cabeçalho com o nome das colunas no arquivo
            nHandle := FOpen(cPasta + cArquivo, FO_READWRITE + FO_SHARED )
            FSeek(nHandle, 0, FS_SET)
            FWrite(nHandle, cCabecalho + CRLF, Len(cCabecalho + CRLF))
            FClose(nHandle)
        EndIf
        QRY_EXP->(DbCloseArea())
		
    Next

    RestArea(aArea)
Return

Static Function fCabecalho()
    Local nCampo := 0
    Local aEstrut := QRY_EXP->(DbStruct())
    Local cCabecalho := ""

    //Percorre a estrutura de campos e adiciona na linha de cabeçalho
    For nCampo := 1 To Len(aEstrut)
        If ! Empty(cCabecalho)
            cCabecalho += ','
        EndIf
        cCabecalho += cDelim + Alltrim(aEstrut[nCampo][1]) + cDelim
    Next
Return cCabecalho

Fonte zImporta (importação dos dados para base local):

//Bibliotecas
#Include "TOTVS.ch"
#Include "TopConn.ch"

/*/{Protheus.doc} User Function zImporta
Função que realiza a importação dos arquivos da Protheus Data para a Base Local
@type  Function
@author Atilio
@since 19/08/2021
@version version
@obs Esse fonte tem que ser compilado e executado na base Local

	Para a gravação dos logs, foi necessário criar duas tabelas

    Tabela ZZ1 - Log da Integração
        ZZ1_CODIGO, Código Sequencial,  caractere, tamanho 9
        ZZ1_ARQUIV, Nome do Arquivo,    caractere, tamanho 50
        ZZ1_EXPDAT, Data da exportação, data,      tamanho 8
        ZZ1_EXPHOR, Hora da exportação, caractere, tamanho 8
        ZZ1_IMPDAT, Data da importação, data,      tamanho 8
        ZZ1_IMPHOR, Hora da importação, caractere, tamanho 8
        
    Tabela ZZ2 - Vinculo de Recnos
        ZZ2_CODIGO, Código Sequencial,   caractere, tamanho 9
        ZZ2_TABELA, Alias da Tabela,     caractere, tamanho 3
        ZZ2_EXPREC, Recno da exportação, numerico,  tamanho 16
        ZZ2_IMPREC, Recno da importação, numerico,  tamanho 16
        ZZ2_DATA,   Data,                data,      tamanho 8
        ZZ2_HORA,   Hora,                caractere, tamanho 8
/*/

User Function zImporta()
    Local aArea
    Local lContinua := .F.
    Private lJobPvt := .F.

    //Se não tiver ambiente aberto, é job
    If Select("SX2") == 0
        RpcSetEnv("99", "01", "usuario", "senha", "GPE")
        lJobPvt := .T.
        lContinua := .T.
    Else
        lContinua := MsgYesNo("Deseja executar a importação dos arquivos para o Banco Local?", "Atenção")
    EndIf
    aArea := GetArea()

    //Se for continuar, irá chamar a rotina de processamento
    If lContinua
        If ! LockByName("zImporta", .T., .F.)

        Else
            Processa({|| fImporta() }, "Importando...")
            UnLockByName("zImporta", .T., .F.)
        EndIf
    EndIf

    RestArea(aArea)
Return

Static Function fImporta()
    Local cPasta     := "\x_bancolocal\"
    Local aArquivos  := {}
    Local nAtual     := 0
    Local cArqAtu    := ""
    Local cTabela    := ""
    Local oArquivo
    Local nLinha     := 0
    Local nColuna    := 0
    Local aCabecalho := {}
    Local nPosRecno  := 0
    Local nPosDelet := 0
    Local lOperacao
    Local nIndAtu
    Local lFalhou
    Private cCodigoZZ2 := StrTran(Space(TamSX3("ZZ2_CODIGO")[1]), ' ', '0')

	//Se a pasta não existir, cria a pasta
    If ! ExistDir(cPasta)
        MakeDir(cPasta)
    EndIf
	
	fBxCloud(cPasta)

    //Buscando os arquivos txt
	aDir(cPasta + "*.txt", aArquivos)

    //Ordena o array, para pegar os totais primeiro T_**** e depois os parciais P_****
    aSort(aArquivos,,, { |x, y| x > y })

    //Define o tamanho da régua
    ProcRegua(Len(aArquivos))

    //Se houver arquivos
    If Len(aArquivos) != 0
        //Busca a última ZZ2 apenas 1 vez
        cQryUlt := " SELECT MAX(ZZ2_CODIGO) AS ULTIMO FROM " + RetSQLName("ZZ2") + " ZZ2 "
        TCQuery cQryUlt New Alias "QRY_ULT"

        //Se houver dados
        If ! QRY_ULT->(EoF())
            //E o último for válido
            If ! Empty(QRY_ULT->ULTIMO)
                cCodigoZZ2 := QRY_ULT->ULTIMO
            EndIf
        EndIf
        QRY_ULT->(DbCloseArea())
    EndIf

    //Percorre os arquivos
    For nAtual := 1 To Len(aArquivos)
        //Incrementa a régua
        IncProc("Importando arquivo " + cValToChar(nAtual) + " de " + cValToChar(Len(aArquivos)) + "...")

        //Pega o arquivo
        cArqAtu  := aArquivos[nAtual]

        //Se o arquivo já foi importado anteriormente, pula o registro
        If fJaImport(cArqAtu)
            FErase(cArqAtu)
            Loop
        EndIf

        //Pega a tabela
        cTipoExp := SubStr(cArqAtu, 1, 1)
        cTabela  := SubStr(cArqAtu, 3, 3)
        oArquivo := FWFileReader():New(cPasta + cArqAtu)
        
        //Se for Exportação Total (desde o primeiro recno), executa um ZAP para apagar os dados da tabela
        If cTipoExp == "T"
            DbSelectArea(cTabela)
            cQryDel := " DELETE FROM " + RetSQLName(cTabela)
            If TCSqlExec(cQryDel) != 0
                nAtual++
                Loop
            EndIf
        EndIf

        //Se o arquivo pode ser aberto
        If (oArquivo:Open())
            //Busca o índice 1 da tabela - Para algumas tabelas, é necessário mudar o índice, por exemplo SD2 usar o índice 3 por causa de chave única da tabela
            DbSelectArea(cTabela)
            If cTabela == "SD2"
                (cTabela)->(DbSetOrder(3))
            Else
                (cTabela)->(DbSetOrder(1))
            EndIf
            cIndice := IndexKey(IndexOrd())
            aIndice := Separa(cIndice, "+")
            aPosInd := {}

            //Se não for fim do arquivo
            If ! (oArquivo:EoF())
                nLinha     := 0
                nPosRecno  := 0
                nPosDelet := 0

                //Enquanto tiver linhas
                While (oArquivo:HasLine())
                    nLinha++
                    
                    //Pegando a linha atual e transformando em array
                    cLinAtu := oArquivo:GetLine()
                    aLinha  := Separa(cLinAtu, ",")
                    
                    //Se for a linha 1, é cabeçalho
                    If nLinha == 1
                        aCabecalho := aClone(aLinha)
                        nPosRecno  := aScan(aCabecalho, {|x| x == "RECNO"})
                        nPosDelet := aScan(aCabecalho, {|x| x == "DELET"})
                        For nIndAtu := 1 To Len(aIndice)
                            If "DTOS" $ aIndice[nIndAtu]
                                aIndice[nIndAtu] := StrTran(aIndice[nIndAtu], "DTOS(", "")
                                aIndice[nIndAtu] := StrTran(aIndice[nIndAtu], ")", "")
                            EndIf
                            //Posição 1 do aPosInd = campo, posição 2 = número da coluna do array
                            nPosInd := aScan(aCabecalho, {|x| Alltrim(x) == Alltrim(aIndice[nIndAtu])})
                            aAdd(aPosInd, {aIndice[nIndAtu], nPosInd})
                        Next
                    ElseIf Len(aLinha) > 0
                        DbSelectArea(cTabela)
                        lOperacao := .T.

                        //Se o recno for igual a 0, registro inválido, pula
                        If Val(aLinha[nPosRecno]) == 0 .Or. fRecInvalid(aLinha[nPosRecno]) .Or. Len(Alltrim(aLinha[nPosRecno])) > 18
                            Loop
                        EndIf
                        aLinha[nPosRecno] := Val(aLinha[nPosRecno])
                        aLinha[nPosRecno] := cValToChar(aLinha[nPosRecno])

                        //Se for Parcial
                        If cTipoExp == "P"
                            lFalhou := .F.
                            cQryTab := " SELECT " + CRLF
                            cQryTab += "     ZZ2_IMPREC " + CRLF
                            cQryTab += " FROM " + CRLF
                            cQryTab += "     " + RetSQLName("ZZ2") + " ZZ2 " + CRLF
                            cQryTab += " WHERE " + CRLF
                            cQryTab += "     ZZ2_FILIAL = '" + FWxFilial("ZZ2") + "' " + CRLF
                            cQryTab += "     AND ZZ2_TABELA = '" + cTabela + "' " + CRLF
                            cQryTab += "     AND ZZ2_EXPREC = '" + aLinha[nPosRecno] + "' " + CRLF
                            cQryTab += "     AND ZZ2.D_E_L_E_T_ = ' ' " + CRLF
                            cQryTab += " LIMIT 1 " + CRLF
                            TCQuery cQryTab New Alias "QRY_TAB"

                            //Se existir, será alteracao
                            If ! QRY_TAB->(EoF())
                                lOperacao := .F.
                                (cTabela)->(DbGoTo(QRY_TAB->ZZ2_IMPREC))

                            //Senão, faz uma segunda query pela chave unica para ver se será inclusão ou alteração
                            Else
                                //Faz uma query e veja se existe os dados
                                cQryTab := " SELECT " + CRLF
                                cQryTab += "     R_E_C_N_O_ AS REC " + CRLF
                                cQryTab += " FROM " + CRLF
                                cQryTab += "     " + RetSQLName(cTabela) + " AS TAB " + CRLF
                                cQryTab += " WHERE " + CRLF
                                cQryTab += "     1 = 1 " + CRLF
                                For nIndAtu := 1 To Len(aPosInd)
                                    cCampo   := aPosInd[nIndAtu][1]
                                    nColuna  := aPosInd[nIndAtu][2]

                                    If nColuna > Len(aLinha)
                                        lFalhou := .T.
                                        Exit
                                    Else
                                        cConteud := aLinha[nColuna]
                                        If "_FILIAL" $ cCampo
                                            cConteud := fTrataFil(cConteud)
                                        EndIf
                                        cQryTab += "     AND " + cCampo + " = '" + cConteud + "' " + CRLF
                                    EndIf
                                Next
                                TCQuery cQryTab New Alias "QRY_TAB2"

                                //Se existir, será alteracao
                                If ! QRY_TAB2->(EoF())
                                    lOperacao := .F.
                                    (cTabela)->(DbGoTo(QRY_TAB2->REC))
                                Else
                                    lOperacao := .T.
                                EndIf
                                QRY_TAB2->(DbCloseArea())
                            EndIf
                            QRY_TAB->(DbCloseArea())

                            //Se a linha for inválida, pula
                            If lFalhou
                                Loop
                            EndIf
                            
                        //Senão será inclusão
                        Else
                            lOperacao := .T.
                        EndIf

                        //Somente se bater o número de colunas da linha com o cabeçalho
                        If Len(aCabecalho) == Len(aLinha)

                            //Trava o registro
                            RecLock(cTabela, lOperacao)

                            //Percorre as colunas
                            For nColuna := 1 To Len(aLinha)

                                //Se a coluna existir
                                If nColuna <= Len(aCabecalho) .And. FieldPos(aCabecalho[nColuna]) > 0
                                    //Se o tipo dos campos for diferente
                                    If ValType((cTabela)->(&(aCabecalho[nColuna]))) != ValType(aLinha[nColuna])
                                        //Campo numérico
                                        If ValType((cTabela)->(&(aCabecalho[nColuna]))) == "N"
                                            aLinha[nColuna] := Val(aLinha[nColuna])

                                        //Campo data
                                        ElseIf ValType((cTabela)->(&(aCabecalho[nColuna]))) == "D"
                                            aLinha[nColuna] := sToD(aLinha[nColuna])

                                        //Campo caractere
                                        Else
                                            aLinha[nColuna] := cValToChar(aLinha[nColuna])
                                        EndIf
                                    EndIf

                                    //Se for um campo numérico
                                    If ValType((cTabela)->(&(aCabecalho[nColuna]))) == "N"
                                        //Se o conteúdo vindo, for estourar o campo, gravo como -1 para identificar
                                        //   depois para aumentar o campo
                                        If "*" $ Alltrim(Transform(aLinha[nColuna], PesqPict(cTabela, aCabecalho[nColuna])))
                                            aLinha[nColuna] := -1
                                        EndIf
                                    EndIf

                                    //Se for um campo MEMO, irá substituir o \n por char 13 e 10
                                    If GetSX3Cache(aCabecalho[nColuna], "X3_TIPO") == "M"
                                        aLinha[nColuna] := StrTran(aLinha[nColuna], "\n", CRLF)

                                    //Se for um campo Numérico, define o tamanho de decimais
                                    ElseIf GetSX3Cache(aCabecalho[nColuna], "X3_TIPO") == "N"
                                        aLinha[nColuna] := NoRound(aLinha[nColuna], TamSX3(aCabecalho[nColuna])[2])
                                    EndIf

                                    //Se for um campo filial, trata o conteudo para gravar
                                    If "_FILIAL" $ aCabecalho[nColuna]
                                        aLinha[nColuna] := fTrataFil(aLinha[nColuna])
                                    EndIf

                                    //Grava o campo
                                    (cTabela)->(&(aCabecalho[nColuna])) := aLinha[nColuna]
                                EndIf
                            Next

                            //Destrava o registro
                            (cTabela)->(MsUnlock())

                            //Se existir a posição do RecDel e ele estiver preenchido, deleta o registro
                            If nPosDelet != 0 .And. Len(aLinha) >= nPosDelet .And. ! Empty(aLinha[nPosDelet])
                                RecLock(cTabela, .F.)
                                    DbDelete()
                                (cTabela)->(MsUnlock())
                            EndIf

                            //Somente se for inclusão
                            If lOperacao
                                fIncLogTab(cTabela, Val(aLinha[nPosRecno]), (cTabela)->(RecNo()))
                            EndIf
                        EndIf
                    EndIf
                    
                EndDo

            Else
                // MsgStop("Arquivo [" + cArqAtu + "] não tem conteúdo!", "Atenção")
            EndIf

            //Fecha o arquivo e exclui da Protheus Data da base local
            oArquivo:Close()
            FErase(cPasta + cArqAtu)
            fIncLog(cArqAtu)
            
        Else
            // MsgStop("Arquivo [" + cArqAtu + "] não pode ser aberto!", "Atenção")
        EndIf

    Next
Return

Static Function fTrataFil(cFilDado)
    Local aArea   := GetArea()
    Local cFilNov := cFilDado
    
    cFilNov := StrTran(cFilNov, "01", "A")
    cFilNov := StrTran(cFilNov, "02", "B")
    cFilNov := StrTran(cFilNov, "03", "C")
    cFilNov := StrTran(cFilNov, "04", "D")
    cFilNov := StrTran(cFilNov, "05", "E")
    cFilNov := StrTran(cFilNov, "06", "F")
    cFilNov := StrTran(cFilNov, "07", "G")
    cFilNov := StrTran(cFilNov, "08", "H")
    cFilNov := StrTran(cFilNov, "09", "I")
    cFilNov := StrTran(cFilNov, "  ", " ")

    RestArea(aArea)
Return cFilNov

Static Function fIncLog(cArquivo)
    Local aArea    := GetArea()
    Local cCodigo  := GetSXENum("ZZ1", "ZZ1_CODIGO")
    Local dDataArq := sToD(SubStr(cArquivo, 7,  8))
    Local cHoraArq := StrTran(SubStr(cArquivo, 16, 8), '-', ':')

    //Inclui o Log
    DbSelectArea("ZZ1")
    RecLock("ZZ1", .T.)
        ZZ1->ZZ1_FILIAL := FWxFilial("ZZ1")
        ZZ1->ZZ1_CODIGO := cCodigo
        ZZ1->ZZ1_ARQUIV := cArquivo
        ZZ1->ZZ1_EXPDAT := dDataArq
        ZZ1->ZZ1_EXPHOR := cHoraArq
        ZZ1->ZZ1_IMPDAT := Date()
        ZZ1->ZZ1_IMPHOR := Time()
    ZZ1->(MsUnlock())

    RestArea(aArea)
Return

Static Function fIncLogTab(cTabela, nRecExpor, nRecImpor)
    //Local aArea    := GetArea()
    //Local cCodigo  := GetSXENum("ZZ2", "ZZ2_CODIGO")

    cCodigoZZ2 := Soma1(cCodigoZZ2)

    //Inclui o Log
    DbSelectArea("ZZ2")
    RecLock("ZZ2", .T.)
        ZZ2->ZZ2_FILIAL := FWxFilial("ZZ2")
        ZZ2->ZZ2_CODIGO := cCodigoZZ2
        ZZ2->ZZ2_TABELA := cTabela
        ZZ2->ZZ2_EXPREC := nRecExpor
        ZZ2->ZZ2_IMPREC := nRecImpor
        ZZ2->ZZ2_DATA   := Date()
        ZZ2->ZZ2_HORA   := Time()
    ZZ2->(MsUnlock())

    //RestArea(aArea)
Return

Static Function fJaImport(cArquivo)
    Local aArea   := GetArea()
    Local lExiste := .F.
    Local cQryArq := ""

    //Busca na base se o arquivo já foi importado
    cQryArq := " SELECT " + CRLF
    cQryArq += "     ZZ1_ARQUIV " + CRLF
    cQryArq += " FROM " + CRLF
    cQryArq += "     " + RetSQLName("ZZ1") + " ZZ1 " + CRLF
    cQryArq += " WHERE " + CRLF
    cQryArq += "     ZZ1_FILIAL = '" + FWxFilial("ZZ1") + "' " + CRLF
    cQryArq += "     AND ZZ1_ARQUIV = '" + cArquivo + "' " + CRLF
    cQryArq += "     AND ZZ1.D_E_L_E_T_ = ' ' " + CRLF
    TCQuery cQryArq New Alias "QRY_EXIST"

    //Se existem dados
    If ! QRY_EXIST->(EoF())
        lExiste := .T.
    EndIf
    QRY_EXIST->(DbCloseArea())

    RestArea(aArea)
Return lExiste

Static Function fRecInvalid(cRecno)
    Local lRet    := .F.
    Local cLetras := "ABCDEFGHIJKLMNOPQRSTUVWXYZ\()Ç"
    Local nLetra  := 0

    //Deixa tudo maiusculo o texto do recno
    cRecno := Alltrim(Upper(cRecno))

    //Percorre as letras
    For nLetra := 1 To Len(cLetras)
        //Se a letra atual estive no texto, o recno é inválido
        If SubStr(cLetras, nLetra, 1) $ cRecno
            lRet := .T.
            Exit
        EndIf
    Next
Return lRet

Static Function fBxCloud(cPastaLoc)
	Local cDirLocal := "C:\TOTVS\ERP\Protheus_Data" + cPastaLoc
    Local cDirCloud := "C:\TOTVS\Cloud\smartclient\"
	Local cPrograma := "u_zCloudBx"
	Local cComunica := "tcp"
	Local cAmbiente := "PRODUCAO01"

    //Executa o smartclient do Cloud para copiar os arquivos para a Protheus Data local
    WaitRun(cDirCloud + "smartclient.exe -c=" + cComunica + " -e=" + cAmbiente + " -m -q -p=" + cPrograma + " -a=" + cDirLocal , 1)
Return

/*/{Protheus.doc} User Function zCloudBx
Função que percorre a pasta na Protheus Data do Cloud e copia os arquivos para a máquina local
@type  Function
@author Atilio
@since 26/08/2021
@version version
/*/

User Function zCloudBx(cPastaLocal)
    Local cPasta        := "\x_bancolocal\"
    Local cPastaOld     := "\x_bancolocal\old\"
    Local aArquivos     := {}
    Local nAtual        := 0
    Local cDataArqui    := ""
    Default cPastaLocal := ""

    //Se a pasta antiga não existir, cria
    If ! ExistDir(cPastaOld)
        MakeDir(cPastaOld)
    EndIf

    //Se tiver pasta local
    If ! Empty(cPastaLocal)
        //Busca todos os TXT do Cloud
        aDir(cPasta + "*.txt", aArquivos)

        //Percorre os arquivos
        For nAtual := 1 To Len(aArquivos)
            cDataArqui := SubStr(aArquivos[nAtual], 7, 8)

            //Copia o arquivo para a pasta local, em seguida para a pasta de backup
            __CopyFile(cPasta + aArquivos[nAtual], cPastaLocal + aArquivos[nAtual])
            __CopyFile(cPasta + aArquivos[nAtual], cPastaOld   + aArquivos[nAtual])

            //Exclui o arquivo original
            FErase(cPasta + aArquivos[nAtual])
        Next
    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.

Deixe uma resposta