Novo exemplo de Modelo 2 em MVC (2020)

Hoje trago um exemplo mais atual de como usar a Modelo 2 em MVC.

Usando Autumn Code Maker


Caso você queira gerar o código de maneira rápida, nós temos essa opção no Autumn Code Maker, onde você pode gerar o código prw em poucos minutos.

Se tiver interesse, acesse o artigo Como criar uma tela Modelo 2 em MVC em poucos passos.

Ou se preferir, na aba Artigo Original, tem o conteúdo original de 2020 desse post.

Artigo Original

Há muito tempo atrás, eu fiz um vídeo e demonstrei no YouTube como fazer uma Modelo 2 em MVC (link aqui – https://terminaldeinformacao.com/2017/01/30/vd-advpl-025/ ).

Para quem não sabe, Modelo 2 é um tipo de cadastro, onde tem um cabeçalho e uma grid, mas ambos salvam as informações na mesma tabela (similar ao pedido de compras – SC7).

Porém, como o exemplo é bem antigo, eu montei um exemplo mais novo, que inclusive está no escopo do curso de MVC em AdvPL aqui para os assinantes, e decidi compartilhar abaixo com vocês.

//Bibliotecas
#Include 'Protheus.ch'
#Include 'FWMVCDef.ch'
  
//Variáveis Estáticas
Static cTitulo := "Abastecimentos"
  
/*/{Protheus.doc} ASATF04
Função para cadastros de Abastecimentos dos veículos
@author Atilio
@since 13/09/2020
@version 1.0
    @return Nil, Função não tem retorno
    @example
    u_ASATF04()
    @obs Os campos chave usado entre cabeçalho e grid são: ZAF_IDSEQ (Código Sequencial), ZAF_NOTFIS (Nota Fiscal), ZAF_VALOR (Valor)
/*/
 
User Function ASATF04()
    Local aArea   := GetArea()
    Local oBrowse
      
    //Cria um browse para a ZAF
    oBrowse := FWMBrowse():New()
    oBrowse:SetAlias("ZAF")
    oBrowse:SetDescription(cTitulo)
    oBrowse:Activate()
      
    RestArea(aArea)
Return Nil
 
Static Function MenuDef()
    Local aRot := {}
      
    //Adicionando opções
    ADD OPTION aRot TITLE 'Visualizar' ACTION 'VIEWDEF.ASATF04' OPERATION MODEL_OPERATION_VIEW   ACCESS 0 //OPERATION 1
    ADD OPTION aRot TITLE 'Incluir'    ACTION 'VIEWDEF.ASATF04' OPERATION MODEL_OPERATION_INSERT ACCESS 0 //OPERATION 3
    ADD OPTION aRot TITLE 'Alterar'    ACTION 'VIEWDEF.ASATF04' OPERATION MODEL_OPERATION_UPDATE ACCESS 0 //OPERATION 4
    ADD OPTION aRot TITLE 'Excluir'    ACTION 'VIEWDEF.ASATF04' OPERATION MODEL_OPERATION_DELETE ACCESS 0 //OPERATION 5
Return aRot
 
Static Function ModelDef()
    //Na montagem da estrutura do Modelo de dados, o cabeçalho filtrará e exibirá somente 3 campos, já a grid irá carregar a estrutura inteira conforme função fModStruct
    Local oModel      := NIL
    Local oStruCab     := FWFormStruct(1, 'ZAF', {|cCampo| AllTRim(cCampo) $ "ZAF_IDSEQ;ZAF_NOTFIS;ZAF_VALOR;"})
    Local oStruGrid := fModStruct()
 
    //Monta o modelo de dados, e na Pós Validação, informa a função fValidGrid
    oModel := MPFormModel():New('ASATF04M', /*bPreValidacao*/, {|oModel| fValidGrid(oModel)}, /*bCommit*/, /*bCancel*/ )
 
    //Agora, define no modelo de dados, que terá um Cabeçalho e uma Grid apontando para estruturas acima
    oModel:AddFields('MdFieldZAF', NIL, oStruCab)
    oModel:AddGrid('MdGridZAF', 'MdFieldZAF', oStruGrid, , )
 
    //Monta o relacionamento entre Grid e Cabeçalho, as expressões da Esquerda representam o campo da Grid e da direita do Cabeçalho
    oModel:SetRelation('MdGridZAF', {;
            {'ZAF_FILIAL', 'xFilial("ZAF")'},;
            {"ZAF_IDSEQ",  "ZAF_IDSEQ"},;
            {"ZAF_NOTFIS", "ZAF_NOTFIS"},;
            {"ZAF_VALOR",  "ZAF_VALOR"};
        }, ZAF->(IndexKey(1)))
     
    //Definindo outras informações do Modelo e da Grid
    oModel:GetModel("MdGridZAF"):SetMaxLine(9999)
    oModel:SetDescription("Atualização Controle de Combustível")
    oModel:SetPrimaryKey({"ZAF_FILIAL", "ZAF_IDSEQ", "ZAF_NOTFIS"})
 
Return oModel
 
Static Function ViewDef()
    //Na montagem da estrutura da visualização de dados, vamos chamar o modelo criado anteriormente, no cabeçalho vamos mostrar somente 3 campos, e na grid vamos carregar conforme a função fViewStruct
    Local oView        := NIL
    Local oModel    := FWLoadModel('ASATF04')
    Local oStruCab  := FWFormStruct(2, "ZAF", {|cCampo| AllTRim(cCampo) $ "ZAF_IDSEQ;ZAF_NOTFIS;ZAF_VALOR;"})
    Local oStruGRID := fViewStruct()
 
    //Define que no cabeçalho não terá separação de abas (SXA)
    oStruCab:SetNoFolder()
 
    //Cria o View
    oView:= FWFormView():New() 
    oView:SetModel(oModel)              
 
    //Cria uma área de Field vinculando a estrutura do cabeçalho com MDFieldZAF, e uma Grid vinculando com MdGridZAF
    oView:AddField('VIEW_ZAF', oStruCab, 'MdFieldZAF')
    oView:AddGrid ('GRID_ZAF', oStruGRID, 'MdGridZAF' )
 
    //O cabeçalho (MAIN) terá 25% de tamanho, e o restante de 75% irá para a GRID
    oView:CreateHorizontalBox("MAIN", 25)
    oView:CreateHorizontalBox("GRID", 75)
 
    //Vincula o MAIN com a VIEW_ZAF e a GRID com a GRID_ZAF
    oView:SetOwnerView('VIEW_ZAF', 'MAIN')
    oView:SetOwnerView('GRID_ZAF', 'GRID')
    oView:EnableControlBar(.T.)
 
    //Define o campo incremental da grid como o ZAF_ITEM
    oView:AddIncrementField('GRID_ZAF', 'ZAF_ITEM')
Return oView
 
//Função chamada para montar o modelo de dados da Grid
Static Function fModStruct()
    Local oStruct
    oStruct := FWFormStruct(1, 'ZAF')
Return oStruct
 
//Função chamada para montar a visualização de dados da Grid
Static Function fViewStruct()
    Local cCampoCom := "ZAF_IDSEQ;ZAF_NOTFIS;ZAF_VALOR;"
    Local oStruct
 
    //Irá filtrar, e trazer todos os campos, menos os que tiverem na variável cCampoCom
    oStruct := FWFormStruct(2, "ZAF", {|cCampo| !(Alltrim(cCampo) $ cCampoCom)})
Return oStruct
 
//Função que faz a validação da grid
Static Function fValidGrid(oModel)
    Local lRet     := .T.
    Local nDeletados := 0
    Local nLinAtual :=0
    Local oModelGRID := oModel:GetModel('MdGridZAF')
    Local oModelMain := oModel:GetModel('MdFieldZAF')
    Local nValorMain := oModelMain:GetValue("ZAF_VALOR")
    Local nValorGrid := 0
    Local cPictVlr   := PesqPict('ZAF', 'ZAF_VALOR')
 
    //Percorrendo todos os itens da grid
    For nLinAtual := 1 To oModelGRID:Length() 
        //Posiciona na linha
        oModelGRID:GoLine(nLinAtual) 
         
        //Se a linha for excluida, incrementa a variável de deletados, senão irá incrementar o valor digitado em um campo na grid
        If oModelGRID:IsDeleted()
            nDeletados++
        Else
            nValorGrid += NoRound(oModelGRID:GetValue("ZAF_TCOMB"), 4)
        EndIf
    Next nLinAtual
 
    //Se o tamanho da Grid for igual ao número de itens deletados, acusa uma falha
    If oModelGRID:Length()==nDeletados
        lRet :=.F.
        Help( , , 'Dados Inválidos' , , 'A grid precisa ter pelo menos 1 linha sem ser excluida!', 1, 0, , , , , , {"Inclua uma linha válida!"})
    EndIf
 
    If lRet
        //Se o valor digitado no cabeçalho (valor da NF), não bater com o valor de todos os abastecimentos digitados (valor dos itens da Grid), irá mostrar uma mensagem alertando, porém irá permitir salvar (do contrário, seria necessário alterar lRet para falso)
        If nValorMain != nValorGrid
            //lRet := .F.
            MsgAlert("O valor do cabeçalho (" + Alltrim(Transform(nValorMain, cPictVlr)) + ") tem que ser igual o valor dos itens (" + Alltrim(Transform(nValorGrid, cPictVlr)) + ")!", "Atenção")
        EndIf
    EndIf
 
Return lRet

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.

38 Responses

  1. LEANDRO MARQUES DE QUEIROZ PEREIRA disse:

    Atilio, boa tarde!

    Utilizei o modelo acima, mas não entendi por que mas os menus de inclusão alteração nao foram criados.

  2. Gabriel disse:

    O que esse campo ZAF_TCOMB faz e o nome dele?

  3. LEANDRO MARQUES DE QUEIROZ PEREIRA disse:

    Boa tarde! Quando vou gravar o registro está gerando esse erro:

    variable is not array – Type [U] on EXFORMCOMMIT(PROTHEUSFUNCTIONMVC.PRX) 06/07/2021 19:07:04 line : 2328

  4. Paulo disse:

    Boa tarde! Quando vou gravar o registro está gerando esse erro:

    variable is not array – Type [U] on EXFORMCOMMIT(PROTHEUSFUNCTIONMVC.PRX) 06/07/2021 19:07:04 line : 2328

  5. Ulisses Souza disse:

    Eu estava com o mesmo erro e era o Relation

  6. Ulisses Souza disse:

    estava com o mesmo erro e era o relation que estava incorreto

  7. Thulio Oliveira disse:

    Atílio, boa tarde!

    Estou com um problema nesta tela. Quando eu cadastro somente um item no grid, ele não grava o campo data.

    Quando coloco 3 itens por exemplo no grid ele grava corretamente os 2 últimos registros do grid. O primeiro continua sem gravar o campo data por exemplo.

    Tem alguma ideia do que pode estar acontecendo ?

  8. Phablo Iago disse:

    No exemplo citado, como eu deixaria o browse listando apenas os campos do cabeçalho sem duplicar?

  9. Paulo disse:

    Poderia me informar qual foi a alteração realizada?

    • Basicamente 2 grandes mudanças que podem impactar na manutenção:
      1. No antigo era feito manualmente o RecLock no Commit, nesse novo já é tratado isso automaticamente.
      2. No antigo era usado manualmente o AddField nas structs, nesse novo já usa o FWFormStruct com filtro.

      Além disso, tem uma ou outra melhoria já que o primeiro fonte é de 2017 e esse segundo é de 2020.

  10. Luis Branco disse:

    Como foi consertado esse Relation ??

  11. Junio Almeida disse:

    Não estou conseguindo alterar e visualizar os registros, ao tentar efetuar estas operações recebo o erro:

    erro no parâmetroFWFormGridModel: A linha 0 é inválida !!! on FWFORMGRIDMODEL:GETVALUE(FWFORMGRIDMODEL.PRX) 08/06/2022 16:53:24 line : 2655

    Alguma ideia do que possa ser?

  12. Jose de Aguiar Ferreira Real Neto disse:

    Boa tarde.
    Estou com o mesmo erro do Paulo variable is not array – Type [U] on EXFORMCOMMIT(PROTHEUSFUNCTIONMVC.PRX) 04/02/2022 19:14:01 line : 2328. O que vc me aconselha ?

  13. Gerson Luis Fachini disse:

    Olá Daniel
    Usei o seu modelo de programa para fazer um cadastro modelo 2. Até consegui fazer os dados aparecerem na tela, mas nada funciona, alteração, inclusão e exclusão. tenho 4 campos na tabela filial, data, produto e valor. O objetivo é em uma determinada data ter um valor para cada produto.

  14. Gerson Luis Fachini disse:

    Verifiquei o artigo indicado e todos os pontos estão corretos. No meu caso os registros aparecem na tela de Alteração, consigo alterar e ao salvar exibe a mensagem que foi alterado com sucesso, mas não altera. Se Vc olhar o seu exemplo ele só altera a descrição da tabela SX5 e não os itens que a compõem. Por exemplo a Tabela 03 é Grupos de Materiais e nela tem vários itens, se Vc alterar de Grupo de Materiais para Grupo de Materiaisxxxx funciona, mas se alterar o item 01 desta tabela não altera.

    • Bom dia Gerson, tudo joia?

      No caso, você esta citando o exemplo de 2017 que fiz com a SX5? Ele é muito antigo, de 6 anos atrás (tanto que no vídeo do YouTube ainda esta sendo usado o Protheus 11).

      Tente se basear no exemplo de 2020, ou utilize o Autumn Code Maker para gerar um código que você possa reaproveitar.

      Um grande abraço.

  15. Henrique Sciascio disse:

    Bom dia Daniel, tudo bem?
    Tive o mesmo problema que o pessoal ai de cima (variable is not array – Type [U] on EXFORMCOMMIT(PROTHEUSFUNCTIONMVC.PRX) 04/02/2022 19:14:01 line : 2328) e não estava conseguindo resolver com nada, tive a ideia de tirar o SetRelation do fonte e funcionou (esta gravando corretamente com 1 ou mais linhas), aposto que essa pratica não é muito recomendada rsrs, você saberia me dizer que problemas pode ocasionar o fonte ficar sem o SetRelation?

    Alias, ótimo trabalho com os posts sobre MVC, sou novato no MVC e esta me ajudando muito!

    • Bom dia Henrique, estou bem graças a Deus e você?

      Primeiramente obrigado pelo feedback sobre nosso trabalho, é muita bondade e generosidade sua.

      Quanto a utilização do SetRelation, em teoria não era pra dar problemas. Não consegui reproduzir esse erro relatado no EXFORMCOMMIT.

      Você consegue fazer um teste, gerando um fonte molde no Autumn e depois nos dando o feedback se o problema persiste? Segue um link com o passo a passo de como fazer: https://terminaldeinformacao.com/2022/09/29/como-criar-uma-tela-modelo-2-em-mvc-em-poucos-passos/

      Fico no aguardo.

      Um grande abraço.

      • Henrique Sciascio disse:

        Bom dia Daniel,

        Estou bem tb obrigado por perguntar.

        Para fins de documentação vou escrever minha trajetória com o erro, primeiro estava dando o erro EXFORMCOMMIT na hora da inclusão, depois de eu ter tirado o SetRelation funcionou a inclusão porem na alteração o grid vinha vazio em qualquer registro que eu entrava então segui sua dica e fui fazer o teste com o Autumn, gerei um código que estava bem parecido com o meu só que o SetRelation veio diferente.
        SetRelation antigo (Esse não funcionava):
        oModel:SetRelation(“MdGridZ94”, {{“Z94_FILIAL”, “FWxFilial(‘Z94’)”},{“Z94_CODPRE”, “Z94_CODPRE”}{“Z94_LOJPRE”, “Z94_LOJPRE”}}, Z94->(IndexKey(3)) ) //Index Z94_FILIAL+Z94_CODPRE+Z94_LOJPRE

        SetRelation novo (Esse funciona):
        aAdd(aRelation, {“Z94_FILIAL”, “FWxFilial(‘Z94’)”} )
        aAdd(aRelation, {“Z94_CODPRE”, “Z94_CODPRE”})
        aAdd(aRelation, {“Z94_LOJPRE”, “Z94_LOJPRE”})
        aAdd(aRelation, {“Z94_FILORI”, “Z94_FILORI”})
        oModel:SetRelation(“MdGridZ94”, aRelation, Z94->(IndexKey(4))) //Index Z94_FILIAL+Z94_CODPRE+Z94_LOJPRE+Z94_FILORI

        Os campos que estão no cabeçalho são ->Z94_FILIAL,Z94_CODPRE,Z94_LOJPRE,Z94_FILORI,Z94_BLOQUE

        Minha analise concluiu que estavam faltando campos no primeiro SetRelation, porem já teve um SetRelation que eu fiz com apenas 1 campo do cabeçalho e funciona perfeitamente, então não sei muito bem o que pode ter dado errado.

        Caso queira entrar em contato no meu email para mais analise sobre isso só me contatar (h********o@gmail.com).

        Novamente, obrigado pela pronta resposta e pelo apoio!

      • Henrique Sciascio disse:

        Bom dia,
        Estou bem também obrigado por perguntar

        Primeiramente houve um problema quando tirei o SetRelation, quando tentei alterar as linhas do grid vinham vazias pra qualquer registro que eu entrava, resolvi fazer o código no Autumn pra ver se resolvia e resolveu mesmo, só não entendi direito o que eu estava fazendo errado.
        No SetRelation antigo tinha colocado os campos Z94_FILIAL,Z94_CODPRE,Z94_LOJPRE com o indice 3 da tabela (Z94_FILIAL+Z94_CODPRE+Z94_LOJPRE), e o codigo do Autumn me deu esse SetRelation que funcionou:
        aRelation := {}

        aAdd(aRelation, {“Z94_FILIAL”, “FWxFilial(‘Z94’)”} )
        aAdd(aRelation, {“Z94_CODPRE”, “Z94_CODPRE”})
        aAdd(aRelation, {“Z94_LOJPRE”, “Z94_LOJPRE”})
        aAdd(aRelation, {“Z94_FILORI”, “Z94_FILORI”})
        oModel:SetRelation(“MdGridZ94”, aRelation, Z94->(IndexKey(4)))

        Sendo o indice 4: Z94_FILIAL+Z94_CODPRE+Z94_LOJPRE+Z94_FILORI

        Talvez o erro da Variavel vir sem formatação correta talvez tenha sido algum erro meu na criação do Array para colocar no SetRelation ou o fato de eu não ter colocado todos os campos do cabeçalho (mesmo eu conferindo isso diversas vezes e não ter funcionado com meu codigo antigo).

        Agradeço o apoio e obrigado pela pronta resposta!

        • Bom dia Henrique, tudo joia?

          Li aqui os dois comentários, e realmente bem estranho o problema estar na estrutura do aRelation.

          Pode ser que seja algo da LIB ou internamente na estruturação do ModelDef.

          Nós que agradecemos o comentário e a análise, e também pelo feedback da utilização do Autumn Code Maker.

          Um grande abraço.

Deixe uma resposta

Terminal de Informação