Olá pessoal…
Na aula de hoje irei mostrar como fazer um cadastro do tipo Modelo 3 em MVC, que basicamente é um cadastro que possui 2 tabelas, uma cabeçalho e outra itens (similar ao Pedido de Venda).
Abaixo o código fonte completo usado na aula:
//Bibliotecas #Include 'Protheus.ch' #Include 'FWMVCDef.ch' //Variáveis Estáticas Static cTitulo := "Composição de CDs" /*/{Protheus.doc} zModel3 Função para cadastro de Composição de CDs (Exemplo de Modelo 3 - ZZ2 x ZZ3) @author Atilio @since 03/09/2016 @version 1.0 @return Nil, Função não tem retorno @example u_zModel3() /*/ User Function zModel3() Local aArea := GetArea() Local oBrowse //Instânciando FWMBrowse - Somente com dicionário de dados oBrowse := FWMBrowse():New() //Setando a tabela de cadastro de CDs oBrowse:SetAlias("ZZ2") //Setando a descrição da rotina oBrowse:SetDescription(cTitulo) //Ativa a Browse oBrowse:Activate() RestArea(aArea) Return Nil /*---------------------------------------------------------------------* | Func: MenuDef | | Autor: Daniel Atilio | | Data: 03/09/2016 | | Desc: Criação do menu MVC | *---------------------------------------------------------------------*/ Static Function MenuDef() Local aRot := {} //Adicionando opções ADD OPTION aRot TITLE 'Visualizar' ACTION 'VIEWDEF.zModel3' OPERATION MODEL_OPERATION_VIEW ACCESS 0 //OPERATION 1 ADD OPTION aRot TITLE 'Incluir' ACTION 'VIEWDEF.zModel3' OPERATION MODEL_OPERATION_INSERT ACCESS 0 //OPERATION 3 ADD OPTION aRot TITLE 'Alterar' ACTION 'VIEWDEF.zModel3' OPERATION MODEL_OPERATION_UPDATE ACCESS 0 //OPERATION 4 ADD OPTION aRot TITLE 'Excluir' ACTION 'VIEWDEF.zModel3' OPERATION MODEL_OPERATION_DELETE ACCESS 0 //OPERATION 5 Return aRot /*---------------------------------------------------------------------* | Func: ModelDef | | Autor: Daniel Atilio | | Data: 03/09/2016 | | Desc: Criação do modelo de dados MVC | *---------------------------------------------------------------------*/ Static Function ModelDef() Local oModel := Nil Local oStPai := FWFormStruct(1, 'ZZ2') Local oStFilho := FWFormStruct(1, 'ZZ3') Local aZZ3Rel := {} //Definições dos campos oStPai:SetProperty('ZZ2_CODCD', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, '.F.')) //Modo de Edição oStPai:SetProperty('ZZ2_CODCD', MODEL_FIELD_INIT, FwBuildFeature(STRUCT_FEATURE_INIPAD, 'GetSXENum("ZZ2", "ZZ2_CODCD")')) //Ini Padrão oStPai:SetProperty('ZZ2_CODART', MODEL_FIELD_VALID, FwBuildFeature(STRUCT_FEATURE_VALID, 'ExistCpo("ZZ1", M->ZZ2_CODART)')) //Validação de Campo oStFilho:SetProperty('ZZ3_CODCD', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, '.F.')) //Modo de Edição oStFilho:SetProperty('ZZ3_CODCD', MODEL_FIELD_OBRIGAT, .F. ) //Campo Obrigatório oStFilho:SetProperty('ZZ3_CODART', MODEL_FIELD_OBRIGAT, .F. ) //Campo Obrigatório oStFilho:SetProperty('ZZ3_CODMUS', MODEL_FIELD_INIT, FwBuildFeature(STRUCT_FEATURE_INIPAD, 'u_zIniMus()')) //Ini Padrão //Criando o modelo e os relacionamentos oModel := MPFormModel():New('zModel3M') oModel:AddFields('ZZ2MASTER',/*cOwner*/,oStPai) oModel:AddGrid('ZZ3DETAIL','ZZ2MASTER',oStFilho,/*bLinePre*/, /*bLinePost*/,/*bPre - Grid Inteiro*/,/*bPos - Grid Inteiro*/,/*bLoad - Carga do modelo manualmente*/) //cOwner é para quem pertence //Fazendo o relacionamento entre o Pai e Filho aAdd(aZZ3Rel, {'ZZ3_FILIAL','ZZ2_FILIAL'} ) aAdd(aZZ3Rel, {'ZZ3_CODCD', 'ZZ2_CODCD'}) aAdd(aZZ3Rel, {'ZZ3_CODART','ZZ2_CODART'}) oModel:SetRelation('ZZ3DETAIL', aZZ3Rel, ZZ3->(IndexKey(1))) //IndexKey -> quero a ordenação e depois filtrado oModel:GetModel('ZZ3DETAIL'):SetUniqueLine({"ZZ3_DESC"}) //Não repetir informações ou combinações {"CAMPO1","CAMPO2","CAMPOX"} oModel:SetPrimaryKey({}) //Setando as descrições oModel:SetDescription("Grupo de Produtos - Mod. 3") oModel:GetModel('ZZ2MASTER'):SetDescription('Cadastro') oModel:GetModel('ZZ3DETAIL'):SetDescription('CDs') Return oModel /*---------------------------------------------------------------------* | Func: ViewDef | | Autor: Daniel Atilio | | Data: 03/09/2016 | | Desc: Criação da visão MVC | *---------------------------------------------------------------------*/ Static Function ViewDef() Local oView := Nil Local oModel := FWLoadModel('zModel3') Local oStPai := FWFormStruct(2, 'ZZ2') Local oStFilho := FWFormStruct(2, 'ZZ3') //Criando a View oView := FWFormView():New() oView:SetModel(oModel) //Adicionando os campos do cabeçalho e o grid dos filhos oView:AddField('VIEW_ZZ2',oStPai,'ZZ2MASTER') oView:AddGrid('VIEW_ZZ3',oStFilho,'ZZ3DETAIL') //Setando o dimensionamento de tamanho oView:CreateHorizontalBox('CABEC',30) oView:CreateHorizontalBox('GRID',70) //Amarrando a view com as box oView:SetOwnerView('VIEW_ZZ2','CABEC') oView:SetOwnerView('VIEW_ZZ3','GRID') //Habilitando título oView:EnableTitleView('VIEW_ZZ2','Cabeçalho - Cadastro') oView:EnableTitleView('VIEW_ZZ3','Grid - CDs') //Força o fechamento da janela na confirmação oView:SetCloseOnOk({||.T.}) //Remove os campos de Código do Artista e CD oStFilho:RemoveField('ZZ3_CODART') oStFilho:RemoveField('ZZ3_CODCD') Return oView /*/{Protheus.doc} zIniMus Função que inicia o código sequencial da grid @type function @author Atilio @since 03/09/2016 @version 1.0 /*/ User Function zIniMus() Local aArea := GetArea() Local cCod := StrTran(Space(TamSX3('ZZ3_CODMUS')[1]), ' ', '0') Local oModelPad := FWModelActive() Local oModelGrid := oModelPad:GetModel('ZZ3DETAIL') Local nOperacao := oModelPad:nOperation Local nLinAtu := oModelGrid:nLine Local nPosCod := aScan(oModelGrid:aHeader, {|x| AllTrim(x[2]) == AllTrim("ZZ3_CODMUS")}) //Se for a primeira linha If nLinAtu < 1 cCod := Soma1(cCod) //Senão, pega o valor da última linha Else cCod := oModelGrid:aCols[nLinAtu][nPosCod] cCod := Soma1(cCod) EndIf RestArea(aArea) Return cCod
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Parabéns!
Muito Obrigado Artur.
Um grande abraço.
Olá Dan_Atilio, ótima aula.
No caso do ZZ3_CODMUS porque ao invés de usar essa função para incrementar o proximo numero, porque não usar o AddIncrementField ?
Eu costumo usar dessa forma que é mais fácil.
Abçs.
Boa tarde Josuel, tudo bem?
Rapaz, não conheço esse recurso, que interessante, tem algum exemplo ai?
Abraços.
Boa tarde,
Vi sua video aula e fiz o codigo, mas acontece que ele não está abrindo duas telas igual ao seu
Boa noite Leonardo, tudo bem?
Tem o print da tela ou o fonte para eu dar uma verificada?
Olá Dan_Atilio!
Parabéns pelo vídeo, são muito bons!
Estou com um erro, espero que possa me ajudar
Quando eu compilo aparece esse erro abaixo nas Static function, MenuDef(), ModelDef() e ViewDef().
warning W0010 Static Function MENUDEF never called
Se puder me ajudar eu agradeço!
Boa tarde Wagner, tudo bem?
Na verdade isso não é um erro, é apenas um Warning, é uma tratativa nova dos repositórios mais novos do Protheus.
Quando se usa uma função estática e não tem chamada dela, ele dá esse Warning mesmo, mas não se preocupe, não impacta em nada na rotina.
Abraços.
Dan_Atilio muito obrigado mais uma vez pela ajuda!
E mais uma vez parabéns pelos vídeos!
Opa, eu que agradeço Wagner.
Abraços.
Boa Tarde!
Dan_Atilio, estou com um problema.
Não está aparecendo a Grid, somente o cabecalho!
Se puder ajudar eu agradeço.
Peguei o código que você postou e criei as tabelas conforme você descreveu
Desde já, Obrigado!
Já resolvi o problema acima com a ajuda de um amigo.
Só que a grid aparece cinza e quando dou seta para baixo da erro!
Bom dia Wagner, tudo bem?
Se possível, mande o fonte e a imagem de erro por e-Mail para eu dar uma olhada.
Abraços.
Boa noite Dan_Atilio!
Desculpa não ter respondido antes, estava ocupado!
Muito obrigado pela atenção!
Acho que o problema está na minha versão do protheus 12, deve ter algum erro.
Testei no protheus 11 e funcionou tudo certinho.
Mais uma vez muito obrigado pela atenção!
Bom dia Wagner.
Ah entendi. Que estranho hein.
Qualquer coisa, entre em contato.
Abraços.
Bom dia Dan_Atilio!
Aqui no meu cabeçalho tenho um campo, valor total, que deve ter a soma dos valores do campo, valor, da minha grid.
Com a função addcalc não consegui.
Existe alguma função que faça isso por mim ou tenho que criar algum método para implementar essa funcionalidade?
Bom dia Wagner, tudo bem?
Me mande por favor o fonte por email.
Abraços.
Excelente!
Muito obrigado Gustavo.
Um grande abraço.
Josuel Realmente funciona com o oView:AddIncrementField(‘FORM3’ , ‘ZL0_ITEM’ ).
Eu também só uso esse recurso agora rs.
Boa tarde.
Adaptei sua rotina para a minha 2 tabelas e só aparece o Browse, sem os menus.
Bom dia Artur, tudo bem?
Verifique esses pontos:
– O nome do prw, deve ter no máximo 7 caracteres, por exemplo, arquivo.prw
– O nome da user function, deve ter no máximo 7 caracteres, e ser igual ao nome do prw, por exemplo, User Function Arquivo
– A função não deve ser executada no Fórmulas, se for, deve ser usado as funções de SetFunName para alterar o nome
Abraços.
Boa tarde,
Parabéns pelo vídeo, segui o exemplo criei tabela SZF – cabeçalho e SZG – itens, deu tudo certo exceto 1 único campo que não grava a filial, o campo ZG_FILIAL fica em branco. Alguma dica ou sugestão?
Boa noite Alan.
Verifique no SetRelation, tente usar a função FWxFilial.
Um grande abraço.
Boa tarde!
Desde já quero agradecer pelo material disponível para aprendizado, queria tirar uma duvida, ao rolar na próxima linha do grid da o seguinte erro
THREAD ERROR ([9160], valmir.rodrigues, GERA24) 21/12/2018 15:03:07
array out of bounds ( 1 of 0 ) on U_ZINIMUS(ZMODEL3.PRW) 21/12/2018 14:40:31 line : 160
[TOTVS build: 7.00.131227A-20180920 NG]
Called from U_ZINIMUS(ZMODEL3.PRW) 21/12/2018 14:40:31 line : 160
Called from {|a,b,c| FWInitCpo(a,b,c),xRet:=(u_zIniMus()),FWCloseCpo(a,b,c,.T.),FwSetVarMem(a,b,xRet),xRet }(PROTHEUSFUNCTIONMVC.PRX) 23/10/2018 18:06:52 line : 160
Called from INITVALUE(FWFORMGRIDMODEL.PRX) 23/10/2018 18:06:50
Boa tarde Valmir.
Essa forma é antiga de manipular o array, o vídeo é antigo, e para solucionar isso, você pode usar duas coisas, ou a SetUseOldGrid ou AddIncrementField. Segue abaixo ambos exemplos:
– https://terminaldeinformacao.com/knowledgebase/metodo-setuseoldgrid/
– https://terminaldeinformacao.com/knowledgebase/metodo-addincrementfield/
Desculpe a demora, estou de férias, e retorno apenas no fim de janeiro.
Um grande abraço.
Pois é, no meu também não grava a filial na tabela filho, apenas a do cabeçalho… Então, quando vamos visualizar o cadastro, ele vem com o grid em branco, olhando no banco, o campo filial esta vazio, não foi gravado… não sei também o que pode ser ;/
Bom dia Bruno.
No setRelation, deixe o campo da sua grid, e a relação dele com FWxFilial, por exemplo:
Abraços.
Primeiramente, parabéns por esse inciativa que certamente ajuda muito a comunidade ADVPL.
Gostaria do seu apoio, criei o modelo 3, conforme sua aula, revisei n vezes e ao acessar a rotina NÃO APARECE os menus de trabalho, incluir, visualizar etc…. nada só impressão em browser que é padrão do Protheus. seja o código:
Static Function Menu()
Local aRot := {}
//Adicionando opções
ADD OPTION aRot TITLE ‘Visualizar’ ACTION ‘VIEWDEF.qprogra’ OPERATION MODEL_OPERATION_VIEW ACCESS 0 //OPERATION 1
ADD OPTION aRot TITLE ‘Incluir’ ACTION ‘VIEWDEF.qprogra’ OPERATION MODEL_OPERATION_INSERT ACCESS 0 //OPERATION 2
ADD OPTION aRot TITLE ‘Alterar’ ACTION ‘VIEWDEF.qprogra’ OPERATION MODEL_OPERATION_UPDATE ACCESS 0 //OPERATION 3
ADD OPTION aRot TITLE ‘Excluir’ ACTION ‘VIEWDEF.qprogra’ OPERATION MODEL_OPERATION_DELETE ACCESS 0 //OPERATION 4
Return aRot
Pode dar um apoio Daniel.
Bom dia Paulo, obrigado pelo feedback.
Então, você está executando direto pelo menu ou fórmulas? Se for pelo fórmulas tente no Menu.
E como disse nas aulas anteriores, a User Function tem que ter no máximo 7 caracteres, e o nome do prw tem que ser o mesmo nome da User Function. Por exemplo, zBeluga.prw e User Function zBeluga().
Abraços.
Perfeito Atilio, resolveu.
Tinha nome de menu com mais de 7, “Visualizar”.
Perdoe a insistência, deu um erro que eu acho que ta ligado ao sistema e não ao fonte/programa. Pode ver abaixo:
THREAD ERROR ([8044], paulo, LAPTOP_PAULO) 07/08/2019 11:01:36
argumento #0 , parâmetro oObj erro, previsto O->U on FWFORMVIEW:SETMODEL(FWFORMVIEW.PRW) 12/07/2019 17:33:43 line : 715
Estou com o ambiente todo atualizado.
Muito grato amigo, fico lhe devendo 2 com essa.
Grande abraço, Paulo Queiroz.
Boa tarde Paulo.
Vixe, esse erro parece meio vago, precisaria ver o fonte mesmo.
Entre no grupo do Discord, lá nós podemos te ajudar.
Abraços.
Muito bom seus vídeos! Por favor faça mais vídeos!
Boa tarde Fabrício.
Obrigado pelo feedback, fique de olho que futuramente virão sim.
Grande abraço.
Olá Dan, parabéns pelo conteúdo.
Me orientando pelo seu material desenvolvi uma tela em que é digitado o Código do produto e já chamo uma função para preencher o resto dos campos e pular a linha, até ai tudo bem.
Mas quando pula a linha o Foco sai do código do produto e na nova linha avança como se eu não tivesse pulado.
Após preencher os campos eu estou fazendo seguinte
oModel:AddLine()
oView:Refresh()
Estou tentando com SetFocus como vi no stackoverflow (https://pt.stackoverflow.com/questions/150088/como-setar-o-foco-em-um-componente-do-mvc), mas sem sucesso.
Tem ideia do que pode ser feito?
Bom dia Leandro, tudo bem?
O trecho que passou parece certo, talvez o que você pode fazer, é na sua função de validar o código do produto, após adicionar a linha, você retornar um .F. para ele não prosseguir para o próximo campo.
Qualquer coisa, me mande o fonte no Discord para analisarmos a situação.
Abraços.
Maravilha,ajudou muito!!
Obrigado pelo comentário jovem.
Olá Dan, parabéns pelo conteúdo.
Eu tenho o mesmo problema que “PAULO JOSE ALCANTARA”. Não consigo exibir as opções definidas no MenuDef ().
O código é o mesmo que você publicou. Você me pode ajudar?
Preciso de uma configuração extra em outro lugar? Obrigado
Bom dia Bruno, obrigado pelo feedback.
Então, o nome da user function tem que ser igual o nome do prw.
O ideal é que tenha 7 caracteres.
Porém pode ser algo do MenuDef, se possível nos mande o código fonte para avaliarmos.
Boa Tarde, qual seria o motivo mais obvio para ao clicar em incluir o programa nao fazer nada ?
Bom dia, segue abaixo as possibilidades.
1. Nome da User Function difere do .prw
2. Nome do MPFormModel é igual o da User Function
3. Você estar executando no Mini Fórmulas / Lanç. Padrão sem estar usando SetFunName
Gostei do seu video e estou tentando adaptar a minha necessidade, mas estou com um problema. Nesse modelo 3 que estou refazendo ele só vai alterar um campo do grid o que devo alterar no setProperty para deixar que os campos em questão fiquem apenas para visualização?
Boa tarde José, obrigado pelo comentário.
No caso, você precisa manipular o FWFormStruct, na posição que indica se o campo será alterável sim (.T.) ou não (.F.), dentro do seu ViewDef.
Ai você teria duas opções, ou debugar e ver como está a variável e encontrar a posição, ou criar a estrutura, por exemplo:
[/code]
Bom dia. Grato pela resposta isso ajuda muito. Se vc puder fazer mais uma gentileza de me responder mais uma pergunta eu agradeço. Levando-se em conta que estou usando uma tabela do banco de dados. Exemplo SC5, a onde esta os campo do SX5 bastaria substituir pelos campo que quero do SC5?
Bom dia José.
Sim, só tome como atenção que a SC5 é cheia de gatilhos e validações internas.
Então o campo que você quer alterar, se for campo customizado, daria certo, se for campo padrão, ai pode dar algum problema em validações internas.
Boa tarde, primeiramente obrigado pelo material !
Estou tento um problema que ao clicar em qualquer botão seja incluir, alterar, visualizar ou excluir nada acontece.
Também não está permetindo fechar a aba, aparece um alert escrito problema, sem texto
Boa tarde Aramis, obrigado pelo feedback.
Das opções dos menus, dê uma olhada nesse artigo: https://terminaldeinformacao.com/2021/01/06/o-que-pode-ser-quando-botoes-nao-funcionam-em-mvc/
Perfeitamente isso mesmo, muito obrigado !
Estou enfrentando outro problema agora, está dando o seguinte erro:
erro no parâmetroFWFormField: O modeloZZEMASTERnão contem referência com o campoZZD_CODPRO on FWFORMFIELD:PREPAREFIELDS(FWFORMFIELD.PRW) 13/10/2020 16:57:59 line : 368
Pelo o que eu entendi ele está pedindo um relacionamento da tabela pai com o codpro da filho, mas realmente não tem esse relacionamento !
O que será que faço pra solucionar?
Opa por nada.
Quanto a esse erro, se você estiver montando uma tela com cabeçalho e grid (modelo 3), é obrigatório ter um relacionamento entre as duas tabelas.
Por exemplo, imagine a tela de pedidos de venda, temos o cabeçalho (SC5) e os itens do pedido (SC6), e o relacionamento entre ambas é o número do pedido (C5_NUM e C6_NUM).
Entendi, o relacionamento existe com outros campos zzd_cod e zze_cod, mas ai informa este erro
Qual erro Aramis?
Se for o caso, crie uma conta gratuita no Autumn, e gere o código fonte por lá, segue um vídeo de exemplo: https://youtu.be/lG_qzgzhk1o
oModel := MPFormModel():New(‘OCOA’)
oModel:AddFields(‘ZZEMASTER’,/*cOwner*/,oStPai)
oModel:AddGrid(‘ZZDDETAIL’,’ZZEMASTER’,oStFilho,/*bLinePre*/,/*bLinePost*/,/*bPre – Grid Inteiro*/,/*bPos – Grid Inteiro*/,/*bLoad – Carga do modelo manualmente*/)
//Fazendo Relacionamento entre pai e filho
aAdd(aZZDRel, {‘ZZD_FILIAL’ , ‘xFilial(“ZZE”)’})
aAdd(aZZDRel, {‘ZZD_COD’ , ‘ZZE_COD’})
oModel:SetRelation(‘ZZDDETAIL’,aZZDRel,ZZD->(IndexKey(1)))
oModel:SetPrimaryKey({})
Saudações Dan, como tornar oStPai apenas visualização? tentei o médoto noupdate(.T.) como fazemos no grid, mas não deu certo.
Bom dia Marco, tudo joia?
Você consegue fazer de duas formas, uma deixando apenas para visualização direto no model, e a outra é percorrendo campo a campo (caso você precise liberar em caso de inclusão por exemplo), abaixo os dois exemplos:
Bom dia
Mais uma vez venho lhe fazer uma pergunta sobre o modelo3 em MVC. Tenho duas tabelas que a unica ligação entre elas é a data, campo DATA INICIAL. Uma tabela é o pai e a outra é o filho.Na tabela filho, existe um campo de bloqueio que se estiver SIM não deverá mostrar esse registro no grid(arquivo filho), mesmo que o campo DATA INICIAL estiver igual ao da tabela pai. Existe algum jeito de se fazer isso?
Bom dia José, tudo joia?’
Certo, o que daria para você fazer é filtrar os dados na grid (seja dentro do modeldef, ou acionando em um bPre por exemplo). Com isso você intercepta a grid, através do GetModel, ai você usa o método SetLoadFilter passando o filtro que deseja executar.
Abaixo um exemplo:
Grande abraço.
Bom dia
Agradeço sua resposta. Usei o metodo que vc passou e funcionou. Grato pela ajuda.
Pra mim ocorre de nao gravar a filial correta. O campo está aberto para digitacao e na inclusão, ele grava a filial corrente forçadamente, mesmo que eu digite outra coisa. Na alteracao funciona corretamente. Como corrijo isso? A tabela é exclusiva e a filial nao faz parte do relacionamento.
Bom dia Diogo, tudo bem?
O campo “_FILIAL” ele é tratado internamente, independente do que você informe em tela. Então existem 3 formas (mais simples) de você sanar esse problema:
1 – Utilizar outro nome de campo: Por exemplo, igual na tabela RCC, onde teria um campo _FIL e nele faria suas validações e gravações
2 – Instalar um evento para complementar o Commit: Por exemplo, usaria o InstallEvent e após o commit você buscaria o que foi digitado no Model via GetValue, e daria RecLock na sua tabela
3 – Criar um ponto de entrada com o nome do seu MPFormModel: Similar ao 2, só que dessa forma, você cria uma user function apartada com o nome do seu MPFormModel e faz similar ao item 2, buscando o valor via GetValue e dando RecLock
Um grande abraço.