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:

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 aRot

3. 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 oModel

4. 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 oView

5. 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 lOk

Pegadinhas comuns 1:N

Veja também