Criando Browse com campos editáveis (FWBrowse)


Amigos me perguntam se é possível criar um browse editável, sem a necessidade de criar uma tela para abrir e alterar o conteúdo de um campo. Sim, é possível fazer isso, e para exempleficar irei usar o componente FWBrowse (https://tdn.totvs.com/display/public/PROT/FwBrowse) para isso. A vantagem de usar este componente, é você ter disponível um botão de impressão e o filtro para pesquisa. O filtro será criado a partir deste trecho localizado no fonte abaixo:

If SX3->X3_CONTEXT <> "V" // Apenas campos REAIS
    aAdd(aFields, { ;
        SX3->X3_CAMPO  , ;
        X3Titulo()     , ;
        SX3->X3_TIPO   , ;
        SX3->X3_TAMANHO, ;
        SX3->X3_DECIMAL, ;
        PesqPict(SX3->X3_ARQUIVO, SX3->X3_CAMPO, ), ;
        AllTrim(X3CBox()), ;
        SX3->X3_F3, ;
        {|| .T. }, ;
        Nil ;
        })
    //Montar filtro de pesquisa de determinados campos
    If Alltrim(SX3->X3_CAMPO)$"C6_NUM|C6_NUMORC|C6_PRODUTO|C6_TES|C6_PEDCLI"
        Aadd(aFiltro, {SX3->X3_CAMPO, X3Titulo(), SX3->X3_TIPO, SX3->X3_TAMANHO, 5, PesqPict(SX3->X3_ARQUIVO, SX3->X3_CAMPO, )})
        Aadd(aSeek  , {X3Titulo(), {{"",SX3->X3_TIPO, SX3->X3_TAMANHO, SX3->X3_TAMANHO, 5, SX3->X3_CAMPO}} } )
    Endif
    
EndIf
  O código é bem simples, após criar toda a estrutura do browse, você precisará habilitar a coluna que ficará editável com a seguinte sintaxe:
oBrowse:acolumns[10]:ledit:= lEditar //Habilita a coluna 10 como editável
oBrowse:acolumns[10]:cReadVar:= 'aDados[oBrowse:nat,10]' //Cria variavel de memoria
  E usando a propriedade bValidEdit, você criar uma validação após sair do campo.
//Validação do campo editável
Static Function MFAT024E(aCampos,cProduto,cCNPJ)
    Local lRet		:= .T.
    Local cItemPC 	:= iif(!Empty(aDados[oBrowse:nAt,10]),aDados[oBrowse:nAt,10],Space(TamSx3("C6_ITEMPC")[1]))  // ItemPC
    oBrowse:setArray(aDados)	// Forço o Browse a ler os novos valores informados.
    oBrowse:Refresh()			// Refresh do Grid
Return ( lRet )
  No exemplo acima, se após perder o foco, preecher o campo com espaços, poderia se qualquer tipo de validação. Código completo:
#INCLUDE "TOTVS.CH"
#INCLUDE "FWMVCDEF.CH"
#INCLUDE "TOPCONN.CH"

User Function MFAT024()
    Local oDlg, oPanel, btnEnviar
    Local nCont
    Local cPerg		:= FunName()
    Local aSize		:= MsAdvSize(.F.)
    Local nMSEsq   	:= aSize[7]		//Margem Superior Esquerda
    Local nMIEsq   	:= 0			//Margem Inferior Esquerda
    Local nMIDir 	:= aSize[6]		//Margem Superior Direita
    Local nMSDir  	:= aSize[5]  	//Margem Inferior Direita
    Local nWidth	:= 0
    Local bConfirma	:= {|| }
    Local bAct001
    Local bAct002
    Private cProduto:= Space(TamSx3("C6_PRODUTO")[1])
    Private cCnpj	:= Space(TamSx3("A1_CGC")[1])
    Private aCampos	:= {}, aColunas := {}, aBrowse	:= {}, aFields := {}, aSeek := {}, aFiltro := {}
    Private aDados	:= {}
    Private lEditar	:= .T.
    Private lItemPC := .T.
    Private oItemPC := Nil
    Private oBrowse	:= FWBrowse():New()
    Private oProduto, oCnpj, btnConfirma
    
    AjustaSX1(cPerg)
    Pergunte(cPerg,.F.)
    cCnpj := MV_PAR01
    
    /* Definição de campos que serão exibidos na visualização. */
    aCampos:= {"C6_NUM", "C6_NUMORC", "C6_ITEM", "C6_PRODUTO", "C6_DESCRI", "C6_LOCAL", "C6_QTDVEN", "C6_TES", "C6_PRCVEN","C6_ITEMPC","C6_NUMPCOM","C6_UM","C6_PEDCLI","C6_ZZDTREC","C6_ZZPRIPI"}
    
    Define MsDialog oDlg Title "Analise de KT" From nMSEsq,nMIEsq To nMIDir,nMSDir Of oMainWnd Pixel STYLE nOR( WS_VISIBLE, WS_POPUP )
    oDlg:lMaximized := .T.

    SetKey(VK_F5, SuaFuncao() ) //Criando tecla de atalho 
    
    /* Parte superior */
    For nCont := 1 To Len(aCampos)
        dbSelectArea("SX3")
        SX3->(dbSetOrder(2))
        SX3->(dbSeek(aCampos[nCont]))
        
        aAdd( aColunas , MontaColunas("{|| aDados[oBrowse:nAt,"+cValToChar(nCont)+"] }",aDados) )
        
        If SX3->X3_CONTEXT <> "V" // Apenas campos REAIS
            aAdd(aFields, { ;
                SX3->X3_CAMPO  , ;
                X3Titulo()     , ;
                SX3->X3_TIPO   , ;
                SX3->X3_TAMANHO, ;
                SX3->X3_DECIMAL, ;
                PesqPict(SX3->X3_ARQUIVO, SX3->X3_CAMPO, ), ;
                AllTrim(X3CBox()), ;
                SX3->X3_F3, ;
                {|| .T. }, ;
                Nil ;
                })
            //Montar filtro de pesquisa de determinados campos
            If Alltrim(SX3->X3_CAMPO)$"C6_NUM|C6_NUMORC|C6_PRODUTO|C6_TES|C6_PEDCLI"
                Aadd(aFiltro, {SX3->X3_CAMPO, X3Titulo(), SX3->X3_TIPO, SX3->X3_TAMANHO, 5, PesqPict(SX3->X3_ARQUIVO, SX3->X3_CAMPO, )})
                Aadd(aSeek  , {X3Titulo(), {{"",SX3->X3_TIPO, SX3->X3_TAMANHO, SX3->X3_TAMANHO, 5, SX3->X3_CAMPO}} } )
            Endif
            
        EndIf
        
    Next nCont
    
    /*Browse*/
    oBrowse:SetOwner(oDlg)
    oBrowse:SetDescription("Analise de KT")
    oBrowse:SetDataArray()
    oBrowse:SetColumns(aColunas)
    oBrowse:SetArray(aDados)
    oBrowse:lHeaderClick:=.F.
    oBrowse:SetLocate() // Habilita a Localização de registros
    oBrowse:SetSeek(nil,aSeek)
    oBrowse:SetFieldFilter(aFiltro)
    oBrowse:SetEditCell( .T. ) 				// indica que o grid é editavel
    oBrowse:acolumns[10]:ledit	 := lEditar //Habilita a coluna 10 como editável
    oBrowse:acolumns[10]:cReadVar:= 'aDados[oBrowse:nat,10]' //Cria variavel de memoria
    oBrowse:bValidEdit  := {|| MFAT024E(aCampos,cProduto,cCNPJ) } // valida e move o valor para o array
    oBrowse:Activate()
    
    /*Parte inferior*/
    oPanel	 := tPanel():New(0, 0, "", oDlg,,,,,16777215,100,26)
    oProduto := tGet():New( 003, 005,{|u| if(PCount()>0,cProduto:=u,cProduto)},oPanel,080,010,PesqPict("SC6","C6_PRODUTO"),{ || },,,,,,.T.,,, {|| .T. } ,,,{||  },.F.,/*lPassword*/,"SB1","cProduto",,,,.T.,,,"Produto: ",1,,,"Produto")
    oCnpj	 := TGet():New( 003, 095,{|u| if(PCount()>0,cCNPJ:=u,cCNPJ)}	  ,oPanel,080,010,PesqPict("SA1","A1_CGC")	  ,{ || },0,,,.F.,,.T.,,.F.,,.F.,.F.,,.F.,.F.,"SA1CNP","cCNPJ",,,,.T.,,,"CNPJ Cliente: ",1,,,"CNPJ")
    btnEnviar:= TButton():New( 010, 185, "Procurar",oPanel,Nil, 060, 012,,,.F.,.T.,.F.,,.F.,,,.F. )
    btnEnviar:bAction := {|| MsgRun("Montando estrutura...","Processando",{|| MFAT024C(aCampos,cProduto,cCNPJ) }), SalvaSX1(cPerg,cCNPJ) }

    oItemPC:= TCheckBox():New(012, 255,"Confirmar alteração após informar Num.Ped.Cliente",{|u| If(PCount()>0,lItemPC:=u,lItemPC)},oPanel,200, 008,,{||  },,{|| },CLR_RED,CLR_WHITE,,.T.,"",,{|| } )
    
    nWidth	   := (nMSDir/2)
    btnConfirma:= TButton():New( 005, nWidth-060, "Confirmar (F5)",oPanel,Nil, 060, 017,,,.F.,.T.,.F.,,.F.,,,.F. )
    btnConfirma:bAction := bConfirma
    btnConfirma:Disable()
    
    oBrowse:oBrowse:Align := CONTROL_ALIGN_ALLCLIENT
    oPanel:Align:= CONTROL_ALIGN_BOTTOM
    
    Activate MsDialog oDlg CENTERED
    
    SetKey(VK_F5,{||})
Return
 
//Validação do campo editável
Static Function MFAT024E(aCampos,cProduto,cCNPJ)
    Local lRet		:= .T.
    Local cItemPC 	:= iif(!Empty(aDados[oBrowse:nAt,10]),aDados[oBrowse:nAt,10],Space(TamSx3("C6_ITEMPC")[1]))  // ItemPC
    oBrowse:setArray(aDados)	// Forço o Browse a ler os novos valores informados.
    oBrowse:Refresh()			// Refresh do Grid
Return ( lRet )
 
//função responsável em popular o browse
Static Function MFAT024C(aCampos,cProduto,cCNPJ)
    Local cPedCli	:= ""
    Local cQuery	:= ""
    Local nCont		:= 0
    
    If Len(aCampos) == 0
        Aviso("Atenção","Não há campos configuradors para exibição de grade!"+CRLF+CRLF+;
            "Entre em contato com o Administrador.",{"Fechar"},1)
        Return
    Endif
    
    If Empty(cCNPJ)
        Alert("Para iniciar a pesquisa é necessário informar o CNPJ!")
        oCNPJ:SetFocus()
        Return
    Endif
    
    Begin Sequence
        
        cQuery:= "SELECT "
        
        For nCont:= 1 To Len(aCampos)
            If nCont > 1
                cQuery += ", "
            EndIf
            cQuery += aCampos[nCont]
        Next
        
        cQuery += " FROM " + RetSqlName("SC6") + " SC6 " + CRLF
        cQuery += " WHERE D_E_L_E_T_= '' " + CRLF
        cQuery += " AND C6_FILIAL 	= '" + xFilial("SC6") + "'" + CRLF
        if !Empty(cProduto)
            cQuery += " AND C6_PRODUTO	= '" + Alltrim(cProduto) + "'" + CRLF
        Endif
        cQuery += " AND C6_CLI + C6_LOJA IN (SELECT A1_COD+A1_LOJA AS 'CLIENTE' FROM " + RetSqlName("SA1") + " SA1  WHERE SA1.D_E_L_E_T_='' AND A1_FILIAL = '" + xFilial("SA1") + "' AND A1_CGC = '"+cCNPJ+"')" + CRLF
        cQuery += " AND C6_QTDVEN > C6_QTDENT" + CRLF
        cQuery += " AND C6_LOCAL = 'KT'" + CRLF
        cQuery += " AND C6_NUMPCOM=''" + CRLF
        cQuery += " ORDER BY C6_NUM, C6_ITEM, C6_PRODUTO"
        
        If ( SELECT("TRB") ) > 0
            dbSelectArea("TRB")
            TRB->(dbCloseArea())
        EndIf
        
        TcQuery cQuery Alias "TRB" New
        
        TRB->(DBGoTop())
        aDados := {}
        While TRB->(!Eof())
            cPedCli := Posicione("SC5",1,xFilial("SC5")+TRB->C6_NUM,"C5_ZZPVCL")
            AAdd(aDados, { TRB->C6_NUM, TRB->C6_NUMORC, TRB->C6_ITEM, TRB->C6_PRODUTO, TRB->C6_DESCRI, TRB->C6_LOCAL, TRB->C6_QTDVEN, TRB->C6_TES, TRB->C6_PRCVEN, TRB->C6_ITEMPC, TRB->C6_NUMPCOM, TRB->C6_UM, cPedCli, STOD(TRB->C6_ZZDTREC), TRB->C6_ZZPRIPI })
            TRB->(DBSkip())
        EndDo
        
        //Popula o array do browse		
        oBrowse:SetArray(aDados)
        btnConfirma:Enable()
        oBrowse:Refresh()
        oBrowse:SetFocus()
        
        TRB->(DBCloseArea())
        
    End Sequence
    
Return
 
//função criada para montar as colunas que serão apresentadas no browse
Static Function MontaColunas(cDados,aBrowse)
    Local oCol
    
    oCol := FWBrwColumn():New()														//Cria objeto
    oCol:SetTitle( AllTrim(X3Titulo()) )											//Define titulo
    oCol:SetData( &(cDados))														//Define valor
    oCol:SetType( SX3->X3_TIPO )													//Define tipo
    oCol:SetPicture( SX3->X3_PICTURE ) 												//Define picture
    oCol:SetAlign( If(SX3->X3_TIPO == "N",CONTROL_ALIGN_RIGHT,CONTROL_ALIGN_LEFT) )	//Define alinhamento
    oCol:SetSize( SX3->X3_TAMANHO + SX3->X3_DECIMAL )								//Define tamanho
    oCol:SetEdit( .F. )																//Indica se é editavel
    
Return oCol