Novo exemplo de Modelo 2 em MVC (2020)

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

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 Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

11 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

Deixe uma resposta