Testar se algum campo foi alterado em uma tela via AdvPL

Hoje vou mostrar como fazer a comparação de todos os campos em uma tabela estando em uma rotina de Alteração.

Por padrão em MVC, se você clica em confirmar, mas não houve alteração em nenhum campo, ele irá te mostrar uma mensagem, avisando que não teve alterações.

Porém, se você está em uma tela mais antiga, como fazer para validar essa informação? Como saber quais campos foram alterados?

Basicamente, nós precisamos fazer 4 coisas:

  1. Pegar todos os campos da tabela com a função DbStruct
  2. Fazer um laço de repetição, percorrendo todos esses campos
  3. Verificar se o conteúdo da tabela (por exemplo, SUATAB) é diferente do conteúdo em memória (M)
  4. Se o campo for diferente da tabela para a memória, ai você pode fazer suas tratativas

Então pegando a lógica acima, e aplicando, teríamos o seguinte cenário para uma tela clássica (que usa RegToMemory):

aCampos := SUATAB->(DbStruct())
    
//Percorre todos os campos
For nAtual := 1 To Len(aCampos)
   
     //Pega o nome do campo atual
     cCampo := aCampos[nAtual][1]
   
     //Somente se o campo não for Virtual
     If GetSX3Cache(cCampo, "X3_CONTEXT") != "V"
   
          //Se o campo salvo na tabela for diferente do da memória
          If &("SUATAB->"+cCampo) != &("M->"+cCampo)
               Alert("Teve alteração no campo " + cCampo)
          EndIf
     EndIf
Next

Update Janeiro de 2023:

Montei mais dois exemplos, um utilizando tela em MVC:

//Pegando o modelo de dados MVC e pegando os campos
oModelAtiv := FWModelActive()
oModelCad  := oModelAtiv:GetModel('SEU_MASTER_OU_DETAIL')
aCampos    := SUATAB->(DbStruct())
    
//Percorre todos os campos
For nAtual := 1 To Len(aCampos)
   
     //Pega o nome do campo atual
     cCampo := aCampos[nAtual][1]
   
     //Somente se o campo não for Virtual
     If GetSX3Cache(cCampo, "X3_CONTEXT") != "V"
   
          //Se o campo salvo na tabela for diferente do da memória
          If &("SUATAB->"+cCampo) != oModelCad:GetValue(cCampo)
               Alert("Teve alteração no campo " + cCampo)
          EndIf
     EndIf
Next

E esse utilizando uma grid antiga (com aCols e aHeader):

aCampos    := SUATAB->(DbStruct())
  
//Percorre todas as linhas da grid
For nLinha := 1 To Len(aCols)
  
    //Percorre todos os campos
    For nAtual := 1 To Len(aCampos)
       
         //Pega o nome do campo atual
         cCampo := aCampos[nAtual][1]
           
         //Busca no aHeader a posição
         nPosHead     := GDFieldPos(cCampo)
       
         //Somente se o campo não for Virtual
         If GetSX3Cache(cCampo, "X3_CONTEXT") != "V"
       
              //Se o campo salvo na tabela for diferente do da memória
              If &("SUATAB->"+cCampo) != aCols[nLinha][nPosHead]
                   Alert("Teve alteração no campo " + cCampo)
              EndIf
         EndIf
    Next
Next

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

15 Responses

  1. GERALDO disse:

    Bom dia mestre, jogando este codigo para orcamento de vendas que por sua vez ainda nao vou para MVC ele da type mismatch nao manjo nada de & Macrosubstituição pode ser algo ai ? carrega as dados certinho mas na comparacao se eu uso os nome reais ele compara 1 a um manualmente mas quando tempo usar o tal & ele nao reconhece …

  2. GERALDO disse:

    em MATA415 –> Ate sair MVC . Obrigado

    IF ALTERA == .T.
    aCampos1 := SCJ->(DbStruct())

    For nAtual := 1 To Len(aCampos1)

    if &(“SCJ->” + acampos1[nAtual][1]) &(“M->” + acampos1[nAtual][1])
    nValida++
    endif
    Next

    ENDIF

    if nValida == 1
    lRet := .F.
    msgalert(“Nada Foi Alterado”)
    Return (lRet)
    endif

    • Dan_Atilio disse:

      Boa tarde Geraldo, tudo bem?
      Faltou algum operador de comparação no seu if, por exemplo, != ou ==.
      if &(“SCJ->” + acampos1[nAtual][1]) {Faltou colocar o operador aqui dentro} &(“M->” + acampos1[nAtual][1])

      Note na linha acima, o que está entre chaves.
      Abraços.

  3. Luciano disse:

    Em que local eu coloco esse fonte? No ponto de entrada do MVC da tela?

  4. Fernando disse:

    como comparar se um campo de um acols foi alterado?

    • Você teria que:
      1. Percorrer todas as linhas do aCols
      2. Se a linha foi incluída ou excluída, ignora
      3. Se a linha do aCols for uma operação de alteração
      3.1 Posiciona na tabela do aCols, conforme a linha atual
      3.2 Percorre todas as colunas da linha
      3.3 Compara a coluna do aCols com a informação gravada na tabela

  5. Olá Daniel!

    No trecho abaixo, ao tentar verificar se o campo Z7_FASE foi alterado, só está retornando o valor da memória, embora o campo SZ7-Z7_FASE esteja preenchido…naõ entendi o motivo.
    Seria possível utilizar tb na inclusão, caso o usuário altere o valor digitado antes de salvar.?
    Abs

    //Se o campo salvo na tabela for diferente do da memória
    If &(“SZ7->”+”Z7_FASE”) != &(“M->”+”Z7_FASE”)
    Alert(“Teve alteração no campo “+&(“SZ7->”+”Z7_FASE”)+”/”+&(“M->”+”Z7_FASE”))
    EndIf

    • Boa tarde Altair, tudo bem?

      No caso, como você já tem o nome exato do campo, você pode fazer o teste diretamente sem macro, por exemplo:

      If SZ7->Z7_FASE != M->Z7_FASE
          Alert("....")
      EndIf
      

      Agora o motivo de não estar entrando no If, teria que debugar para encontrar, pode ser por exemplo desposicionamento na tabela, tente adicionar GetArea e RestArea nas funções pra ver.

      Da inclusão é um pouco mais complicado, pois se o usuário inputou um valor, não teria como você comparar com o que esta salvo, você teria que armazenar em um array ou temporária (FWTemporaryTable), pra ver se ele realmente alterou algo (se caso você quiser campo a campo). Se for apenas em um campo, basta adicionar uma validação no X3_VLDUSER e ai se o usuário preencher algo, você consegue tratar.

      Abraços.

  6. Robson Ribeiro disse:

    Tem como deixar um exemplo completo no caso do MVC?

    • Bom dia Robson, tudo joia?

      Sim, vamos pegar o exemplo e acima, e vamos fazer o seguinte:
      1. O nome do model será DA1DETAIL
      2. O nome da tabela será DA1
      3. Iremos adicionar User Function e Return
      4. Iremos adicionar GetArea e RestArea

      O resultado será:

      User Function zTeste()
      	Local aArea := FWGetArea()
      	//Pegando o modelo de dados MVC e pegando os campos
      	Local oModelAtiv := FWLoadModel()
      	Local oModelCad  := oModelAtiv:GetModel('DA1DETAIL')
      	Local aCampos    := DA1->(DbStruct())
      	   
      	//Percorre todos os campos
      	For nAtual := 1 To Len(aCampos)
      	  
      		 //Pega o nome do campo atual
      		 cCampo := aCampos[nAtual][1]
      	  
      		 //Somente se o campo não for Virtual
      		 If GetSX3Cache(cCampo, "X3_CONTEXT") != "V"
      	  
      			  //Se o campo salvo na tabela for diferente do da memória
      			  If &("DA1->"+cCampo) != oModelMVC:GetValue(cCampo)
      				   Alert("Teve alteração no campo " + cCampo)
      			  EndIf
      		 EndIf
      	Next
      	
      	FWRestArea(aArea)
      Return
      

      Abraços.

Deixe uma resposta para Dan_AtilioCancelar resposta

Terminal de Informação