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.
Atilio, boa tarde!
Utilizei o modelo acima, mas não entendi por que mas os menus de inclusão alteração nao foram criados.
Boa tarde Leandro, tudo bem?
Dê uma olhada nesse artigo – https://terminaldeinformacao.com/2021/01/06/o-que-pode-ser-quando-botoes-nao-funcionam-em-mvc/
Abraços jovem.
O que esse campo ZAF_TCOMB faz e o nome dele?
É um campo que armazena o valor total dos abastecimentos/combustíveis. O nome é “Total Comb.”.
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
Boa tarde Leandro, tudo bem?
Você inseriu algo no bloco bCommit?
Se sim, qual seria o trecho?
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
Bom dia Paulo.
Nossa que estranho, o bCommit foi sobreposto?
Caso não, nos envie o fonte que você esta usando por e-Mail, junto com a SX2, SX3 e SIX exportada para podermos realizar testes.
Eu estava com o mesmo erro e era o Relation
Opa, obrigado pelo comentário e adendo Ulisses.
Grande abraço.
estava com o mesmo erro e era o relation que estava incorreto
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 ?
Bom dia Thulio, tudo joia?
Rapaz, que cenário estranho, confesso que nunca vi algo nesse sentido.
Se quiser, nos mande o dicionário (sx2, sx3 e six) dessa sua tabela para simularmos.
Grande abraço.
cara estou com esse mesmo problema, o que poderia ser ?
Bom dia Renan, tudo joia?
Tente gerar o fonte usando o Autumn Code Maker ( https://autumncodemaker.com ), e nos dê um feedback se funciona.
Segue vídeo de exemplo: https://youtu.be/HR-FbiPRanI
Ficamos no aguardo.
Um grande abraço.
No exemplo citado, como eu deixaria o browse listando apenas os campos do cabeçalho sem duplicar?
Você consegue fazer de duas formas:
1 – Fazer alguma tratativa após a gravação dos registros, para gravar uma flag sempre no primeiro registro ou último, e depois aplicar um filtro conforme essa flag
2 – Fazer uma query, buscando os últimos registros, e depois filtrar conforme eles (exemplo em https://terminaldeinformacao.com/2018/01/30/como-deixar-apenas-uma-linha-por-pedido-browse-pedido-de-compras/ )
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.
Como foi consertado esse Relation ??
Boa tarde Luis.
Geralmente o SetRelation não tem muito segredo, tendo sempre que se atentar aos campos relacionados, na esquerda sempre os da tabela filho e na direita sempre da pai.
Caso tenha dúvidas também, no Autumn Code Maker, tem uma opção para gerar Modelo2, crie uma conta lá gratuitamente – https://autumncodemaker.com
Abraços.
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?
Geralmente, quando ele acusa a linha 0, é pelo motivo de não ter conseguido carregar a grid.
Isso pode ser no SetRelation, que tem alguma divergência.
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 ?
Boa tarde.
Tente utilizar o Autumn para gerar o código, ele já vai gerar formatado e funcional.
Segue um exemplo com vídeo: https://terminaldeinformacao.com/2022/09/29/como-criar-uma-tela-modelo-2-em-mvc-em-poucos-passos/
Boa tarde
Agradeço sua ajuda. Só tenho mais uma pergunta é necessario ter algum campo no item do modelo2 para inumerar a linha?
Bom dia José.
Necessário não é, mas é indicado.
Pois dessa forma, ficaria mais fácil de você fazer o controle dos dados.
Bom dia dr.
Mais uma vez agradeço sua resposta. Ajudou em muito.
Opa, eu que agradeço o comentário.
Abraços.
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.
Boa tarde Gerson, tudo joia?
Dê uma olhada nesse artigo – https://terminaldeinformacao.com/2021/01/06/o-que-pode-ser-quando-botoes-nao-funcionam-em-mvc/
Nele nós explicamos 5 coisas que podem impactar para que os botões não funcionem em MVC.
Um grande abraço.
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.
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.
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!
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.