Pular para o conteúdo principal

Postagem em destaque

BlackTDN :: Gerenciamento de Erros e Avisos em Projetos AdvPL/TLPP: Um Desafio Frequente no Desenvolvimento TOTVS Protheus

_Créditos da imagem: Gerada com auxílio do ChatGPT_ --- **Gerenciamento de Erros e Avisos em Projetos AdvPL/TLPP: Um Desafio Frequente no Desenvolvimento TOTVS Protheus** Ao trabalhar com projetos desenvolvidos na linguagem AdvPL/TLPP, especialmente em ambientes como o TOTVS Microsiga Protheus, desenvolvedores frequentemente se deparam com uma série de problemas e avisos de compilação. Recentemente, em uma análise de código de um dos nossos projetos, foram identificados mais de 700 problemas durante o processo de compilação, distribuídos entre erros críticos e avisos que podem afetar o desempenho e a manutenção da aplicação. ### Principais Problemas Encontrados Entre os problemas destacados, podemos citar: 1. **Erros de Nomenclatura e Duplicação de Fontes**: - Um dos erros mais críticos que surgiram foi: "Not allowed more than one source with same name". Este erro é comum quando há múltiplos arquivos PRW com o mesmo nome em diferentes pastas do projeto, o que pode

BlackTDN :: TwBrowse/ListBox On-Demand (por: Robson Luiz)


Oi Naldo, tudo bem cara?

Vivemos em uma época onde devemos superar expectativas e sempre tentar fazer algo diferente e inovador. Quando é dito diferente e inovador a idéia é surpreender, e é claro sempre fazendo mais por menos, seja lá o que for, mas devemos mostrar que o consumo é menor e a praticidade é benéfica. Neste contexto apresento uma solução que não criei, a idéia veio de um bate-papo relativo a melhoria de um processo e otimização de tempo, e depois de tanto procurar achei o que precisava. Logo implementei o que eu havia negociado, ajustei, coloquei alguns recursos a mais e aproveitei para fazer um modelo de exemplo e deixa-lo para vocês que nos acompanham no BlackTDN.

Criar uma consulta ou uma lista de dados por demanda, ou se preferirem, ListBox On-Demand. A necessidade é simples, mostrar apenas alguns dados e conforme o usuário for teclando <Page_Down> ou teclando seta para baixo a rotina vai atribuindo mais dados ao vetor do Listbox. Então, com esta idéia imaginem uma query que retorne seis mil registros, geralmente fazemos a leitura de todos estes registros e depois apresentamos no Listbox, correto? Isso causa espera demonstrando que a rotina é lenta. Contudo, nem sempre o usuário quer consultar ou analisar todos os dados, portanto a idéia é apresentar 'N', poucos, registros por página, e no buffer deixar mais alguns 'N' registros, por exemplo, no total será lido apenas os quarenta primeiros registros de sei lá quantos retornam no resultado total da query, daí em diante a rotina deverá alimentar o vetor do ListBox conforme o usuário for teclando <Page-Down> ou seta para baixo. Há um porém, se caso nesta lista inicial o usuário souber a chave do registro que deseja buscar, a rotina também deve contemplar esta necessidade, OK! Abaixo os detalhes do passo-a-passo da rotina.

Atenciosamente,

Robson Luiz rleg30@gmail.com
 

#Include 'Protheus.ch'
#DEFINE NMAXPAGE 50

//--------------------------------------------------------------------------
// Rotina | LbxOnDemand  | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para listar os registro da tabela SX5, porém por demanda.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
User Function LbxOnDemand()
Local nOpc := 0
Local aSay := {}
Local aButton := {}

Private cCadastro := "ListBox On-Demand"
aAdd(aSay,"Este ListBox mostra os 50 primeiros registros, porém na tabela SX5 existem")
aAdd(aSay,"muito mais registros. Ao navegar o cursor para baixo ou teclar Page Down,")
aAdd(aSay,"a rotina irá buscar mais 50 registros quando o buffer de 50 registros for alcançado.")
aAdd(aSay,"")
aAdd(aSay,"Clique em OK para prosseguir.")
aAdd(aButton, { 1,.T.,{|| nOpc := 1, FechaBatch() }})
aAdd(aButton, { 2,.T.,{|| FechaBatch()              }})
FormBatch( cCadastro, aSay, aButton )
If nOpc==1
ODConfig("SX5")
Endif
Return

//--------------------------------------------------------------------------
// Rotina | ODConfig     | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para configurar qual tabela, campos e índices.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODConfig(cAliasRef)
Local lRet := .F.
Local aCampos := {}
Local aIndices := {}
Local cWhere := ""
Do Case
Case cAliasRef == "SX5"
aCampos := {"X5_TABELA","X5_CHAVE","X5_DESCRI"}
aIndices:= {{"X5_TABELA+X5_CHAVE"},{"Tabela+Chave"}}
EndCase

lRet := ODShow(cAliasRef,aCampos,aIndices,cWhere)
Return lRet

//--------------------------------------------------------------------------
// Rotina | ODShow       | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para apresentar os dados em tela.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODShow(cAliasRef,aCampos,aIndices,cWhere)
Local cCmbIndice := ""
Local cPesq := Space(50)
Local cTrbName := "TMP"+cAliasRef
Local cTitle := ""
Local cBLine := ""
Local cSep := ""
Local cCadAnt := ""

Local nX := 0
Local nRecno := 0

Local lRet := .F.

Local bRet:= {|| lRet := .T.,nRecno := IIf(Len(oLstBx:aArray)>=oLstBx:nAt,ATail(oLstBx:aArray[oLstBx:nAt]),0),oDlg:End()}

Local aDados:= {}
Local aHeaders:= {}
Local oDlg
Local oPesq
Local oLstBx
DEFAULT cWhere := ""
//-------------------------------
// Remove o campo filial da lista
//-------------------------------
For nX := 1 to Len(aCampos)
If "_FILIAL" $ aCampos[nX]
ADel(aCampos,nX)
ASize(aCampos,Len(aCampos)-1)
Exit
Endif
Next nX
//------------------------
// Monta header do listbox
//------------------------
SX3->(DbSetOrder(2))
For nX := 1 to Len(aCampos)
SX3->(DbSeek(aCampos[nX]))
#IFDEF SPANISH
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITSPA)))
#ELSE
#IFDEF ENGLISH
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITENG)))
#ELSE
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITULO)))
#ENDIF
#ENDIF
Next nX
//-----------------
// Nome da pesquisa
//-----------------
SX2->(DbSetOrder(1))
SX2->(DbSeek(cAliasRef))
#IFDEF SPANISH
cTitle := ALLTRIM(SX2->X2_NOMESPA)
#ELSE
#IFDEF ENGLISH
cTitle := ALLTRIM(SX2->X2_NOMEENG)
#ELSE
cTitle := ALLTRIM(SX2->X2_NOME)
#ENDIF
#ENDIF
DEFINE MSDIALOG oDlg TITLE "Consulta" + " " + cTitle FROM 268,260 TO 642,796 PIXEL
//------------------
// Texto de pesquisa
//------------------
@ 17,2 MSGET oPesq VAR cPesq SIZE 219,9 COLOR CLR_BLACK PIXEL OF oDlg
//------------------------------------------
// Interface para selecao de indice e filtro
//------------------------------------------
@ 3,228 BUTTON "Filtrar" SIZE 37,12 PIXEL OF oDlg ACTION ;
(ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq))
@ 5,2 COMBOBOX cCmbIndice ITEMS aIndices[2] SIZE 220,010 PIXEL OF oDlg ON CHANGE ;
(ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq))
//-------------------------
// Invocar o objeto ListBox
//-------------------------
oLstBx := TWBrowse():New(30,3,264,139,Nil,aHeaders,,oDlg,,,,,,,,,,,,,,.T.)
oLstBx:bLDblClick := bRet
//--------------------------
// Botoes de ação do usuário
//--------------------------
DEFINE SBUTTON FROM 172,02 TYPE 1  ENABLE OF oDlg Action(Eval(bRet))
DEFINE SBUTTON FROM 172,35 TYPE 2  ENABLE OF oDlg Action(oDlg:End()) 
DEFINE SBUTTON FROM 172,68 TYPE 15 ENABLE OF oDlg Action(ODVisual(@oLstBx,cAliasRef))
//------------------------
// Carga inicial dos dados
//------------------------
ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq)
ACTIVATE MSDIALOG oDlg CENTERED
If Select(cTrbName) > 0
(cTrbName)->(DbCloseArea())
Endif
If lRet
DbSelectArea(cAliasRef)
DbGoTo(nRecno)
Endif
Return lRet

//--------------------------------------------------------------------------
// Rotina | ODSetArray   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para passar os dados do vetor para o objeto TwBrowse.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODSetArray(oLstBx,aDados,cWhere,cTrbName,aCampos,cAlias,cCmbIndice,aIndices,oDlg,cPesq)
Local cQuery := ""
Local cCpos := ""
Local cSep := ""
Local cChave := ""
Local cConcat := "+"
Local cLenPsq := "" 
Local cFiltro := ""
Local nX := 0 
Local cPrefx := PrefixoCpo(cAlias)
Local cOrder := ""
Local bLine := Nil
Local bNextReg := {|a,b,c,d,e| ODPageDown(@a,b,c,d,e)}
Local nTotReg := 0
Local nLenChave := 0
Local nInd := 0   
Local lFiltra := .F.
Local aCposAdd := AClone(aCampos)
Default cPesq := "" 
//-----------------------------------
// Remove espacos do texto pesquisado
//-----------------------------------
cPesq := AllTrim(cPesq)
cLenPsq := AllTrim(Str(Len(cPesq)))
//----------------------------------------
// Verifica se deve ser feito algum filtro
//----------------------------------------
If !Empty(cPesq)
lFiltra := .T.
Endif
//-------------------------
// Define a ordem utilizada
//-------------------------
nInd := AScan(aIndices[2],cCmbIndice)
//--------------------------------
// Filtro de acordo com a pesquisa
//--------------------------------
If lFiltra
//----------------------------------------------------------------
// Define o simbolo de concatenacao de acordo com o banco de dados
//----------------------------------------------------------------
If Upper(TcGetDb()) $ "ORACLE,POSTGRES,DB2,INFORMIX"
cConcat := "||"
Endif
cChave   := Upper(aIndices[1][nInd])
cChave   := StrTran(cChave,cPrefX+"_FILIAL+","")
cChvOrig := cChave
cChave   := StrTran(cChave,cPrefX+"_",cAlias+"."+cPrefX+"_")
cChave   := StrTran(cChave,"DTOS","")
If cConcat <> "+"
cChave := StrTran(cChave,"+",cConcat)
Endif
//-------------------------------------------------------
// Verifica se a chave de busca nao eh maior que o indice
//-------------------------------------------------------
nLenChave := ODTamChave(cChvOrig)   
If nLenChave < Val(cLenPsq)
cLenPsq := AllTrim(Str(nLenChave))
cPesq := SubStr(cPesq,1,nLenChave)
Endif
//--------------------------------
// Concatena a expressao do filtro
//--------------------------------
If lFiltra
cFiltro += " AND SUBSTRING(" + cChave + ",1," + cLenPsq + ")= '"+cPesq+"' "
Endif
Endif
//--------------------------------------------
// Monta lista de campos para o objeto ListBox
//--------------------------------------------
SX3->(DbSetOrder(2))
cBLine := "{||{"
For nX := 1 To Len(aCampos)
cBLine += cSep + "oLstBx:aArray[oLstBx:nAt]["+AllTrim(Str(nX))+"]"
cSep := ","
Next nX
cBLine += "}}"
bLine := &(cBLine)
cSep := ""
//--------------------------
// Prepara e executa a query
//--------------------------
cQuery := ODQuery(NIL,cAlias,cFiltro,cTrbName,.T.)
cQuery := ChangeQuery( cQuery )
If Select(cTrbName) > 0
(cTrbName)->(DbCloseArea())
Endif
DbUseArea(.T., "TOPCONN", TCGenQry(,,cQuery), cTrbName,.T.,.T.)
(cTrbName)->(DbGoTop())
If (cTrbName)->(Eof())
MsgStop("Nenhum registro foi encontrado")
Else
//--------------------------
// Conta registros da tabela
//--------------------------
DbSelectArea(cTrbName)
DbGoTop()
While !Eof() .AND. nTotReg <= NMAXPAGE
nTotReg++
DbSkip()
End
DbGoTop()
aDados := ODPageDown(NIL,cTrbName,aCposAdd,NMAXPAGE,cAlias)
oLstBx:SetArray(aDados)
oLstBx:bLine := bLine
oLstBx:GoTop()
oLstBx:Refresh()
oDlg:Refresh()
If (nTotReg > NMAXPAGE)
oLstBx:bGoBottom := {||Eval(bNextReg,oLstBx,cTrbName,aCposAdd,NMAXPAGE,cAlias),oLstBx:NAT := EVAL( oLstBx:BLOGICLEN ) }
oLstBx:bSkip := {|NSKIP, NOLD, nMax| nMax:=EVAL( oLstBx:BLOGICLEN ),NOLD := oLstBx:NAT, oLstBx:NAT += NSKIP,;
oLstBx:NAT := MIN( MAX( oLstBx:NAT, 1 ), nMax ),Iif(oLstBx:nAt==nMax,;
Eval(bNextReg,oLstBx,cTrbName,aCposAdd,NMAXPAGE,cAlias),.F.),oLstBx:NAT - NOLD}
Endif
Endif
Return

//--------------------------------------------------------------------------
// Rotina | ODQuery      | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para efetuar a query.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODQuery(aVend,cAlias,cFiltro,cArquivo,lSoQuery)
Local aArea := GetArea()
Local cArqTmp := ""
Local cQuery := ""
Local cConcat := ""
DEFAULT cFiltro := ""
DEFAULT lSoQuery:= .F.
//----------------------------------------------------------------
// Define o simbolo de concatenacao de acordo com o banco de dados
//----------------------------------------------------------------
If Upper(TcGetDb()) $ "ORACLE,POSTGRES,DB2,INFORMIX"
cConcat := "||"
Else
cConcat := "+"
Endif
//-----------------------------------------
// Verificar qual tabela irá fazer a query.
//-----------------------------------------
If cArquivo == NIL
cArqTmp := "TRBSX5"
Else
cArqTmp := cArquivo
Endif
If Select(cArqTmp) > 0
(cArqTmp)->(DbCloseArea())
Endif
//----------------------------
// Efetuar e executar a query.
//----------------------------
If cAlias == "SX5"
SX5->(DbSetOrder(1))
cQuery := "SELECT DISTINCT X5_TABELA"+cConcat+"X5_CHAVE FROM " + RetSqlName("SX5") + " SX5 "
//---------------
// Clausula Where
//---------------
cQuery += "WHERE SX5.X5_FILIAL = '" + xFilial("SX5") + "'"
If !Empty(cFiltro)
cQuery += " " + cFiltro + " "
Endif
If TcSrvType() != "AS/400"
cQuery += " AND SX5.D_E_L_E_T_ = '' "
Else
cQuery += " AND SX5.@DELETED@ = '' "
Endif
cQuery += " ORDER BY X5_TABELA"+cConcat+"X5_CHAVE"
If !lSoQuery
cQuery := ChangeQuery(cQuery)
dbUseArea(.T.,"TOPCONN",TcGenQry(,,cQuery),cArqTmp,.T.,.T.)
(cArqTmp)->(DbGoTop())
Endif
Endif
RestArea(aArea)
Return cQuery

//--------------------------------------------------------------------------
// Rotina | ODPageDown   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para ler os dados na tabela conforme retorno da query. E
//        | fazer o controle de número de linhas lidas/visualizas pelo user.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODPageDown(oLstBx,cAlias,aCampos,nLimite,cAliasOri)
Local aLinha := {}
Local aDados := {}

Local nX := 0
Local nRegs := 0

Local cChaveInd := ""
Local cSep := ""
Local cChave := ""
(cAliasOri)->(DbSetOrder(1))
For nX := 1 To (cAlias)->(FCount())
If Type((cAlias)->(FieldName(nX))) == "C"
cChaveInd += cSep + (cAlias)->(FieldName(nX))
cSep := "+"
Endif
Next nX
While !(cAlias)->(Eof()) .And. nRegs <= nLimite
aLinha := {}
cChave := (cAlias)->&(cChaveInd)
(cAliasOri)->(DbSeek(xFilial(cAliasOri)+cChave))
For nX := 1 To Len(aCampos)
AAdd(aLinha,(cAliasOri)->&(aCampos[nX]))
Next nX
AAdd(aLinha,(cAliasOri)->(Recno()))
If oLstBx <> NIL
AAdd(oLstBx:aArray,aClone(aLinha))
Else
Aadd(aDados,aClone(aLinha))
Endif
nRegs++
(cAlias)->(DbSkip())
End
If oLstBx <> NIL
Return aClone(oLstBx:aArray)
Else
Return aDados
Endif
Return .F.

//--------------------------------------------------------------------------
// Rotina | ODVisual     | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para visualizar os dados na íntegra conforme o registro.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODVisual(oLstBx,cAlias)
Local aArea := GetArea()
Local nReg := 0
//---------------------------------------------------------------
// Tratamento para casos em que o filtro não exiba clientes no F3
//---------------------------------------------------------------
If Len(oLstBx:aArray) >= oLstBx:nAt
nReg := aTail(oLstBx:aArray[oLstBx:nAt])
SaveInter()
//-------------------------------------------------------
// Cria um aRotina basico para evitar quaisquer problemas
// com a rotina diferente deste padrao                  
//-------------------------------------------------------
aRotina := {{"Pesquisar","AxPesqui",0,1},{"Visualizar","AxVisual",0,2}}
DbSelectArea(cAlias)
DbGoTo(nReg)
AxVisual(cAlias,nReg,2)
RestInter()
Endif
RestArea(aArea)
Return

//--------------------------------------------------------------------------
// Rotina | ODTamChave   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para verificar se a chave de busca eh maior que o indice.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODTamChave(cChvOrig)
Local nTam := 0
Local nX := 0
Local aCpos := {}
cChvOrig := StrTran(cChvOrig,"DTOS")
cChvOrig := StrTran(cChvOrig,"(")
cChvOrig := StrTran(cChvOrig,")")
aCpos := StrToKArr(cChvOrig,"+")
For nX := 1 To Len(aCpos)
nTam += TamSX3(aCpos[nX])[1]
Next nX
Return nTam

Comentários

Postar um comentário

Postagens mais visitadas