Versões comparadas
Chave
- Esta linha foi adicionada.
- Esta linha foi removida.
- A formatação mudou.
Conceito
Nesse artigo é apresentado o novo recurso para interceptação do commit do modelo MVC, permitindo uma melhor separação das operações pós gravação de modelo ( integrações com o ERP por exemplo), além de permitir o reuso dessa operações em localizações de formulários MVC.
Sobrescrevendo o bloco de Commit.
Atualmente quando é preciso realizar outras operações além da gravação do modelo no Commit do MVC (contabilização, integração fiscal, financeira e etc.) são utilizados os seguintes passos:
- Criação do bloco de commit.
- Dentro da função do bloco é executada a função FWFormCommit, para persistir o modelo.
- Abre as tabelas do modelo e executa a leitura (novamente) do modelo, executando as operações de integração
| Bloco de código | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
//-------------------------------------------------------------------
/*/{Protheus.doc} ModelDef
Definição do modelo de Dados
@author alvaro.camillo
@since 05/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Static Function ModelDef()
Local oModel
Local oStr1 := FWFormStruct(1,'ZC3')
Local oStr2 := FWFormStruct(1,'ZC4')
//Bloco a ser executado no Commit
Local bCommit := {|oModel| MLOC003Com(oModel) }
oModel := MPFormModel():New('MLOC003Old', /*bPre*/, /*bPost*/, bCommit, /*bCancel*/)
oModel:SetDescription('Pedidos')
oModel:addFields('ZC3MASTER',,oStr1)
oModel:addGrid('ZC4DETAIL','ZC3MASTER',oStr2)
oModel:SetRelation('ZC4DETAIL', { { 'ZC4_FILIAL', 'xFilial("ZC4")' }, { 'ZC4_COD', 'ZC3_COD' } }, ZC4->(IndexKey(1)) )
Return oModel
//-------------------------------------------------------------------
/*/{Protheus.doc} MLOC003Com
Função de Commit
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Static Function MLOC003Com(oModel)
Local cPadrao := "005" // Lançamento padrão a ser configurado no CT5
Local nTotal := 0 // Variável totalizadora da contabilizacao
Local aFlagCTB := {} // Array com as informações para a gravação do flag de contabilização do registro
Local nHdlPrv := 0 // Handle (numero do arquivo de trabalho) utilizado na contabilizacao
Local cLote := LoteCont("FIN") // Lote Contábil do lançamento, cada módulo tem o seu e está configurado na tabela 09 do SX5
Local cArquivo := "" // Arquivo temporario usado para contabilizacao
Local lMostra := .T. // Verifica se mostra ou nao tela de contabilização
Local lAglutina := .F. // Verifica se aglutina lançamentos com as mesmas entidades contábeis
Begin Transaction
FWFormCommit( oModel )
// Função que verifica se o lançamento padrão foi configurado pelo cliente
If VerPadrao(cPadrao)
// Rotina que abre o capa do lote contábil ( Inicio da Contabilização)
nHdlPrv := HeadProva(cLote,FunName(),Substr(cUsername,1,6),@cArquivo)
EndIf
ZC4->(dbSetOrder(1))//ZC4_FILIAL+ZC4_COD+ZC4_ITEM
ZC0->(dbSetOrder(1))//ZC0_FILIAL+ZC0_COD+ZC0_LOJA
ZC1->(dbSetOrder(1))//ZC1_FILIAL+ZC1_COD
ZC0->(MsSeek(xFilial("ZC0") + ZC3->(ZC3_CLIENT + ZC3_LOJA) ))
If ZC4->(dbSeek( xFilial("ZC4") + ZC3->ZC3_COD ))
While ZC4->(!EOF()) .And. ZC4->(ZC4_FILIAL+ZC4_COD) == xFilial("ZC4") + ZC3->ZC3_COD
ZC1->(MsSeek(xFilial("ZC4") + ZC4->ZC4_PROD ))
If nHdlPrv > 0
aAdd(aFlagCTB,{"ZC4_LA","S","ZC4",ZC4->(Recno()),0,0,0})
// Função que interpreta todas as sequencias de lançamento configurada pelo usuário e cria as linhas de lançamento contábil
// Executada uma vez para cada registro que quer ser contabilizado
nTotal += DetProva(nHdlPrv,cPadrao,FunName(),cLote,,,,,,,,@aFlagCTB)
Endif
ZC4->(dbSkip())
EndDo
If nHdlPrv > 0 .And. ( nTotal > 0 )
// Função que fecha o lote contábil
RodaProva(nHdlPrv, nTotal)
// Função que apresenta a tela de contabilização, realiza aglutinação caso necessária e grava o documento contábil ( CT2 )
cA100Incl(cArquivo,nHdlPrv,3,cLote,lMostra,lAglutina)
Endif
EndIf
End Transaction
Return .T.
|
Porém essa implementação tem limitações como:
- Uso excessivo de bloco de código com gasto de memória e baixa performance.
- É preciso realizar a leitura novamente dos registros para as operações de integração.
- O controle de transação fica por conta do desenvolvedor.
- Em um fonte localizado não é possível estender o comportamento do commit, incluindo novas operações.
O padrão Observer
O Observer é um padrão de projeto de software que define uma dependência um-para-muitos entre objetos de modo que quando um objeto muda o estado, todos seus dependentes são notificados e atualizados automaticamente. Permite que objetos interessados sejam avisados da mudança de estado ou outros eventos ocorrendo num outro objeto.(https://pt.wikipedia.org/wiki/Observer)

O padrão Observer no MVC Protheus
Em um fomulário MVC esse padrão é aplicado utilizando os seguintes passos:
- Desenvolver uma classe que herde da classe FWObserver.
- Inscrever um objeto dessa classe no modelo.
Desenvolvendo uma classe Observer
Essa classe será responsável pela a operação que complementa a persistência do modelo e será chamada toda vez que o formulário onde ele foi inscrito realize o commit.
Essa classe deve herdar da classe FWObserver e implementar obrigatoriamente os seguintes métodos:
| Painel | ||
|---|---|---|
| ||
Sintaxe FWObserver():New()-> Objeto FWObserver
Descrição Construtor da classe
|
| Painel | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||
Sintaxe FWObserver():update(oObserver,cAction,aParam)
Descrição Método que é chamado pelo MVC quando ocorrer as ações do commit.
Parâmetros
|
| Bloco de código | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
#Include 'Protheus.ch'
#Include 'FWMVCDef.ch'
//-------------------------------------------------------------------
/*/{Protheus.doc} ML003PRORUS
Classe interna implementando o Observer do Commit para atualização de saldo no produto
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Class ML003PRORUS FROM FWObserver
Method New()
Method update()
End Class
Method update(oObserver,cAction,aParam) Class ML003PRORUS
Local oModel := Nil
Local cModelId := ""
Local cAlias := ""
Local lNewRecord:= .F.
If cAction =="AFTER"
oModel := aParam[1]
cModelId:= aParam[2]
cAlias:= aParam[3]
lNewRecord:= aParam[4]
If cAlias == "ZC4" .And. lNewRecord
ZL1->(dbSetOrder(1))//ZL1_FILIAL+ZL1_COD
If ZL1->(MsSeek(xFilial("ZL1") + ZC4->ZC4_PROD ))
RecLock("ZL1",.F.)
ZL1->ZL1_QTVEND += ZC4->ZC4_QUANT
MsUnLock()
EndIf
EndIf
EndIf
return
Method new() Class ML003PRORUS
Return
|
| Bloco de código | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
#Include 'Protheus.ch'
#Include 'FWMVCDef.ch'
//-------------------------------------------------------------------
/*/{Protheus.doc} ML003CTB
Classe interna implementando o Observer do Commit
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Class ML003CTB FROM FWObserver
DATA cPadrao // Lançamento padrão a ser configurado no CT5
DATA nTotal // Variável totalizadora da contabilizacao
DATA aFlagCTB // Array com as informações para a gravação do flag de contabilização do registro
DATA nHdlPrv // Handle (numero do arquivo de trabalho) utilizado na contabilizacao
DATA cLote
DATA cArquivo // Arquivo temporario usado para contabilizacao
Method new()
Method update()
Method openCTB()
Method closeCTB()
Method writeLineCTB()
Method destroy()
End Class
//-------------------------------------------------------------------
/*/{Protheus.doc} new
Método construtor da classe.
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method new() Class ML003CTB
self:cPadrao := "005"
self:nTotal := 0
self:aFlagCTB := {}
self:nHdlPrv := 0
self:cLote := ""
self:cArquivo := ""
Return
//-------------------------------------------------------------------
/*/{Protheus.doc} update
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method update(oObserver,cAction,aParam) Class ML003CTB
Local oModel := Nil
Local cModelId := ""
Local cAlias := ""
Local lNewRecord:= .F.
If cAction =="BEFORE_TTS"
oModel := aParam[1]
self:openCTB(oModel)
EndIf
If cAction =="AFTER"
oModel := aParam[1]
cModelId:= aParam[2]
cAlias:= aParam[3]
lNewRecord:= aParam[4]
self:writeLineCTB(oModel,cModelId,cAlias,lNewRecord)
EndIf
If cAction == "AFTER_TTS"
oModel := aParam[1]
self:closeCTB(oModel)
EndIf
return
//-------------------------------------------------------------------
/*/{Protheus.doc} openCTB
Bloco para ser executado antes da transação para abrir o header de contabilização
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method openCTB(oModel) Class ML003CTB
Local lRet := .T.
self:cPadrao := "005" // Lançamento padrão a ser configurado no CT5
self:nTotal := 0 // Variável totalizadora da contabilizacao
self:aFlagCTB := {} // Array com as informações para a gravação do flag de contabilização do registro
self:nHdlPrv := 0 // Handle (numero do arquivo de trabalho) utilizado na contabilizacao
self:cLote := LoteCont("FIN") // Lote Contábil do lançamento, cada módulo tem o seu e está configurado na tabela 09 do SX5
self:cArquivo := "" // Arquivo temporario usado para contabilizacao
// Função que verifica se o lançamento padrão foi configurado pelo cliente
If VerPadrao(self:cPadrao)
// Rotina que abre o capa do lote contábil ( Inicio da Contabilização)
self:nHdlPrv := HeadProva(self:cLote,FunName(),Substr(cUsername,1,6),@self:cArquivo)
EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} writeLineCTB
Bloco para ser executado depois da gravação.
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method writeLineCTB(oModel,cModelId,cAlias,lNewRecord) Class ML003CTB
Local lRet := .T.
Local aArea:= GetArea()
If cAlias == "ZC4"
ZC0->(dbSetOrder(1))//ZC0_FILIAL+ZC0_COD+ZC0_LOJA
ZC1->(dbSetOrder(1))//ZC1_FILIAL+ZC1_COD
ZC0->(MsSeek(xFilial("ZC0") + ZC3->(ZC3_CLIENT + ZC3_LOJA) ))
ZC1->(MsSeek(xFilial("ZC4") + ZC4->ZC4_PROD ))
If self:nHdlPrv > 0
aAdd(self:aFlagCTB,{"ZC4_LA","S","ZC4",ZC4->(Recno()),0,0,0})
// Função que interpreta todas as sequencias de lançamento configurada pelo usuário e cria as linhas de lançamento contábil
// Executada uma vez para cada registro que quer ser contabilizado
self:nTotal += DetProva(self:nHdlPrv,self:cPadrao,FunName(),self:cLote,,,,,,,,@self:aFlagCTB)
Endif
Endif
RestArea(aArea)
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} closeCTB
Bloco para ser executado depois da transação.
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method closeCTB(oModel) Class ML003CTB
Local lRet := .T.
Local lMostra := .T. // Verifica se mostra ou nao tela de contabilização
Local lAglutina := .F. // Verifica se aglutina lançamentos com as mesmas entidades contábeis
If self:nHdlPrv > 0 .And. ( self:nTotal > 0 )
// Função que fecha o lote contábil
RodaProva(self:nHdlPrv, self:nTotal)
// Função que apresenta a tela de contabilização, realiza aglutinação caso necessária e grava o documento contábil ( CT2 )
cA100Incl(self:cArquivo,self:nHdlPrv,3,self:cLote,lMostra,lAglutina)
Endif
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} destroy
Método responsável por limpar as váriaveis (principalmente arrays) utilizadas na classe para
evitar leak de memória.
@author alvaro.camillo
@since 06/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Method destroy() Class ML003CTB
aSize(self:aFlagCTB, 0)
Return
|
Inscrever um objeto no modelo.
Nessa etapa, no formulário MVC é preciso inscrever o objeto pelo método InstallObserver:
| Painel | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||
Sintaxe MPFormModel():InstallObserver(cEvent,oObserver)
Descrição Método de inscrição de observer
Parâmetros
|
Importante
| Aviso | ||
|---|---|---|
| ||
|
| Bloco de código | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
//-------------------------------------------------------------------
/*/{Protheus.doc} ModelDef
Definição do modelo de Dados
@author alvaro.camillo
@since 05/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Static Function ModelDef()
Local oModel
Local oStr1 := FWFormStruct(1,'ZC3')
Local oStr2 := FWFormStruct(1,'ZC4')
Local oObserver := ML003CTB():New()
oModel := MPFormModel():New('MLOC003', /*bPre*/, /*bPost*/, /*bCommit*/, /*bCancel*/)
oModel:SetDescription('Pedidos')
oModel:addFields('ZC3MASTER',,oStr1)
oModel:addGrid('ZC4DETAIL','ZC3MASTER',oStr2)
oModel:SetRelation('ZC4DETAIL', { { 'ZC4_FILIAL', 'xFilial("ZC4")' }, { 'ZC4_COD', 'ZC3_COD' } }, ZC4->(IndexKey(1)) )
//Inscrição do observer ML003CTB
oModel:InstallObserver("COMMIT",oObserver)
Return oModel
|
| Bloco de código | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
//-------------------------------------------------------------------
/*/{Protheus.doc} ModelDef
Definição do modelo de Dados
@author alvaro.camillo
@since 05/09/2016
@version 1.0
/*/
//-------------------------------------------------------------------
Static Function ModelDef()
Local oModel := FWLoadModel('MLOC003')
Local oStr2 := FWFormStruct(1,'ZL4')
Local oObsCTB := ML003CTBRUS():New()
Local oObsProduto := ML003PRORUS():New()
oModel:addGrid('ZL4DETAIL','ZC4DETAIL',oStr2)
oModel:SetRelation('ZL4DETAIL', { { 'ZL4_FILIAL', 'xFilial("ZL4")' }, { 'ZL4_COD', 'ZC3_COD' }, { 'ZL4_ITEM', 'ZC4_ITEM' } }, ZL4->(IndexKey(1)) )
oModel:getModel('ZL4DETAIL'):SetDescription('Rateio do Item Russo')
oModel:getModel('ZL4DETAIL'):SetOptional(.T.)
//Limpa os observer pois o observer da contabilização será herdado
oModel:ClearObserver("COMMIT")
//Instala o observer de contabilizao Russa
oModel:InstallObserver("COMMIT",oObsCTB)
//Instala o observer de atualização de saldo
oModel:InstallObserver("COMMIT",oObsProduto)
Return oModel
|
| Status do documento | Desenvolvimento |
|---|---|
| Data | |
| Versão | 1.0 |
| Autores | Alvaro Camillo Neto |
| Índice | ||||||
|
...