Cadastro MVC pai-filho (1:N) com grid
Tutorial: cadastro MVC com tabela pai + tabela filha em grid (1:N). Modelo classico de Pedido + Itens, Nota + Produtos, etc.
Cadastros 1:N (pai-filho) sao a maioria dos cadastros reais: Pedido + Itens, Nota + Produtos, Familia + Membros. Vamos criar um cadastro fictício de "Projeto + Tarefas" pra ilustrar.
1. Tabelas necessarias
Cadastre na SX2 + SX3:
ZP1(Projetos):ZP1_FILIAL,ZP1_COD,ZP1_NOME,ZP1_INICIO,ZP1_FIMZP2(Tarefas):ZP2_FILIAL,ZP2_PROJ,ZP2_ITEM,ZP2_DESC,ZP2_HORAS
2. Estrutura MVC com grid
#INCLUDE "PROTHEUS.CH"
#INCLUDE "FWMVCDEF.CH"
User Function CadProj()
Local oBrowse := FWMBrowse():New()
oBrowse:SetAlias("ZP1")
oBrowse:SetDescription("Projetos e Tarefas")
oBrowse:Activate()
Return
Static Function MenuDef()
Local aRot := {}
ADD OPTION aRot TITLE "Visualizar" ACTION "VIEWDEF.CadProj" OPERATION 2 ACCESS 0
ADD OPTION aRot TITLE "Incluir" ACTION "VIEWDEF.CadProj" OPERATION 3 ACCESS 0
ADD OPTION aRot TITLE "Alterar" ACTION "VIEWDEF.CadProj" OPERATION 4 ACCESS 0
ADD OPTION aRot TITLE "Excluir" ACTION "VIEWDEF.CadProj" OPERATION 5 ACCESS 0
Return aRot3. ModelDef com pai + grid filha
Static Function ModelDef()
Local oModel := NIL
Local oStruct1 := FWFormStruct(1, "ZP1") // pai
Local oStruct2 := FWFormStruct(1, "ZP2") // filha
oModel := MPFormModel():New("CadProjMDL", , , {|o| Commit(o)})
// PAI
oModel:AddFields("ZP1MASTER", , oStruct1)
// GRID (1:N) — vinculada por ZP2_PROJ -> ZP1_COD
oModel:AddGrid("ZP2DETAIL", "ZP1MASTER", oStruct2)
oModel:SetRelation("ZP2DETAIL", { ;
{"ZP2_FILIAL", "xFilial('ZP2')"}, ;
{"ZP2_PROJ", "ZP1_COD"} ;
}, "ZP2_FILIAL+ZP2_PROJ+ZP2_ITEM")
oModel:SetDescription("Projeto com tarefas")
oModel:GetModel("ZP1MASTER"):SetDescription("Projeto")
oModel:GetModel("ZP2DETAIL"):SetDescription("Tarefas")
oModel:SetPrimaryKey({"ZP1_FILIAL", "ZP1_COD"})
Return oModel4. ViewDef com folder/abas
Static Function ViewDef()
Local oView := NIL
Local oModel := FWLoadModel("CadProj")
Local oStruct1 := FWFormStruct(2, "ZP1")
Local oStruct2 := FWFormStruct(2, "ZP2")
oView := FWFormView():New()
oView:SetModel(oModel)
oView:AddField("VIEW_ZP1", oStruct1, "ZP1MASTER")
oView:AddGrid("VIEW_ZP2", oStruct2, "ZP2DETAIL")
// Layout: pai em cima, grid embaixo
oView:CreateHorizontalBox("CIMA", 30)
oView:CreateHorizontalBox("BAIXO", 70)
oView:SetOwnerView("VIEW_ZP1", "CIMA")
oView:SetOwnerView("VIEW_ZP2", "BAIXO")
Return oView5. Commit customizado (opcional)
Static Function Commit(oModel)
Local lOk := .T.
Local nLinhas := 0
// Grava primeiro
lOk := FwFormCommit(oModel)
If lOk
// Validacao pos-gravacao: somar horas das tarefas
nLinhas := oModel:GetModel("ZP2DETAIL"):Length()
If nLinhas == 0
Help(,,"HELP",,"Projeto sem tarefas — adicione ao menos 1", 1, 0)
lOk := .F.
EndIf
EndIf
Return lOkPegadinhas comuns 1:N
- SetRelation: vincula chaves estrangeiras. Sem isso, grid nao acha as filhas
- Indice da filha: a tabela ZP2 precisa ter indice por FILIAL+PROJ+ITEM (ou similar) cadastrado na SIX
- Performance: grid com 1000+ linhas fica lento. Implemente paginacao se necessario
- Exclusao em cascata: ao excluir pai, MVC pergunta se exclui filhos. Custom logic no Commit pra controlar