Hoje iremos demonstrar um exemplo de uma tela em MVC usando 3 tabelas temporárias.
Esse exemplo foi gentilmente disponibilizado por Fabrício Antunes ( LinkedIn ). No caso, a lógica por trás da rotina é exibir a Conciliação x Contabilidade x Financeiro.
Esse fonte inclusive esta bem completo, ao abrir ele, será exibido uma pergunta que irá filtrar os dados.
Após confirmar, será aberto um browse com os dados já populados na temporária.
Ao clicar no botão Detalhes, será exibido uma tela com dados de 3 tabelas temporárias.
E se clicar no botão Relatório, será aberto um TReport listando os dados.
Abaixo o código fonte completo:
//Bibliotecas
#Include "TOTVS.CH"
#Include "FWMVCDef.CH"
/*/{Protheus.doc} User Function MCON00X
Funcao de conciliacao financeiro
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
User Function MCON00X()
Local aArea := FWGetArea()
Local aCposCab := {}
Local aCposGrd1 := {}
Local aCposGrd2 := {}
Local aPergs := {}
Local aTitulos
Local nX
Local cSpace := Space(17)
Local lAborta := .F.
Private cTableCab, cTableGr1, cTableGr2
Private oBrowse := Nil
Private aRotina := MenuDef()
Private aBrows, aGrd1, aGrd2 //Varias com estrutura de colunas para ser utilizado no browser, no fields e nos grids
Private cAlisCab := GetNextAlias()
Private cAlisGr1 := GetNextAlias()
Private cAlisGr2 := GetNextAlias()
Private cfilqry
Private aSelFil := {}
Private oGrd1
Private oGrd2
Private oCabec
// Perguntas de parametros para Funcao
aAdd(aPergs, {1, "Da Conta", ' ', "@!", ".T.", "CT1", ".T.", 80, .T.})
aAdd(aPergs, {1, "Registro de", sTod(' '), "", ".T.", "", ".T.", 80, .T.})
aAdd(aPergs, {1, "Registros ate", sTod(' '), "", ".T.", "", ".T.", 80, .T.})
aAdd(aPergs ,{3, "Seleciona Filiais",1,{"Sim","Nao"},50,"",.T.})
aAdd(aPergs ,{3, "Somente Divergentes",2,{"Sim","Nao"},50,"",.T.})
aAdd(aPergs ,{3, "Somente Div. Sld Final",2,{"Sim","Nao"},50,"",.T.})
aAdd(aPergs, {1, "Do Item", cSpace, "@!", ".T.", "CTD", ".T.", 80, .F.})
aAdd(aPergs, {1, "Ate o Item", 'ZZZZZZZZZZZZZZZZZ', "@!", ".T.", "CTD", ".T.", 80, .F.})
If ParamBox(aPergs, "Informe os parâmetros para definicao dos filtros da rotina")
///----------------------------------------------------------
//Cria tabela para browser que sera usada no filds do MVC
//----------------------------------------------------------
aAdd(aCposCab,{"ID","C",6,00})
aAdd(aCposCab,{"ITEM_TAB","C",10,00})
aAdd(aCposCab,{"DESC_ITEM","C",30,00})
aAdd(aCposCab,{"FIN_SLA","N",15,2})
aAdd(aCposCab,{"FIN_DEB","N",15,2})
aAdd(aCposCab,{"FIN_CRE","N",15,2})
aAdd(aCposCab,{"FIN_SLF","N",15,2})
aAdd(aCposCab,{"CTB_SLA","N",15,2})
aAdd(aCposCab,{"CTB_DEB","N",15,2})
aAdd(aCposCab,{"CTB_CRE","N",15,2})
aAdd(aCposCab,{"CTB_SLF","N",15,2})
aAdd(aCposCab,{"DEF_TAB","C",1,0})
aAdd(aCposCab,{"DIF_VAL","N",15,2})
//Array com nome dos campos para Browser
aTitulos := {'ID Rotina', "Codigo", "Nome", "Fin. Sl. Ant.","Fin. Debito","Fin. Credito","Fin. Sl. Fin.","Ctb. Sl. Ant." ,"Ctb. Debito","Ctb. Credito" ,"Ctb. Sl. Fin.", "Div.", "Diferença" }
//Funcao para gerar as colunas do Browser
aBrows := gerCpBrow(aCposCab,aTitulos)
If oCabec <> Nil
oCabec:Delete()
oCabec := Nil
Endif
oCabec := FWTemporaryTable():New(cAlisCab)
oCabec:SetFields(aCposCab)
oCabec:AddIndex("1", {"ID"})
oCabec:AddIndex("2", {"ITEM_TAB"})
oCabec:Create()
//Obtenho o nome "verdadeiro" da tabela no BD (criada como tempor ria)
cTableCab := oCabec:GetRealName()
//----------------------------------------------------------
//Cria tabela grid 1 para ser usado do MVC
//----------------------------------------------------------
If oGrd1 <> Nil
oGrd1:Delete()
oGrd1 := Nil
Endif
oGrd1 := FWTemporaryTable():New(cAlisGr1)
aAdd(aCposGrd1,{"ID" ,"C",06,0})
aAdd(aCposGrd1,{"ITEM" ,"C",03,0})
aAdd(aCposGrd1,{"DATS" ,"C",10,0})
aAdd(aCposGrd1,{"DOC" ,"C",09,0})
aAdd(aCposGrd1,{"HIST" ,"C",50,0})
aAdd(aCposGrd1,{"FIN_DEB" ,"N",18,2})
aAdd(aCposGrd1,{"FIN_CRED" ,"N",18,2})
aAdd(aCposGrd1,{"SALDO" ,"N",18,2})
aTitulos := {'ID Rotina', "Item","Data", "Documento", "Historico","Fin. Debito","Fin. Credito", "Saldo" }
aGrd1 := gerCpBrow(aCposGrd1,aTitulos)
oGrd1:SetFields(aCposGrd1)
oGrd1:AddIndex("1", {"ID"})
oGrd1:AddIndex("2", {"DATS"})
oGrd1:AddIndex("3", {"DOC"})
oGrd1:Create()
//Obtenho o nome "verdadeiro" da tabela no BD (criada como tempor ria)
cTableGr1 := oGrd1:GetRealName()
//----------------------------------------------------------
//Cria tabela grid 2 para ser usado do MVC
//----------------------------------------------------------
If oGrd2 <> Nil
oGrd2:Delete()
oGrd2 := Nil
Endif
oGrd2 := FWTemporaryTable():New(cAlisGr2)
aAdd(aCposGrd2,{"ID" ,"C",06,0})
aAdd(aCposGrd2,{"ITEM" ,"C",03,0})
aAdd(aCposGrd2,{"DATS" ,"C",10,0})
aAdd(aCposGrd2,{"LOTE" ,"C",09,0})
aAdd(aCposGrd2,{"HIST" ,"C",50,0})
aAdd(aCposGrd2,{"FIN_DEB" ,"N",18,2})
aAdd(aCposGrd2,{"FIN_CRED" ,"N",18,2})
aAdd(aCposGrd2,{"SALDO" ,"N",18,2})
aTitulos := {'ID Rotina', "Item","Data", "Lote", "Historico","Fin. Debito","Fin. Credito", "Saldo" }
aGrd2 := gerCpBrow(aCposGrd2,aTitulos)
oGrd2:SetFields(aCposGrd2)
oGrd2:AddIndex("1", {"ID"})
oGrd2:AddIndex("2", {"DATS"})
oGrd2:AddIndex("3", {"LOTE"})
oGrd2:Create()
//Obtenho o nome "verdadeiro" da tabela no BD (criada como tempor ria)
cTableGr2 := oGrd2:GetRealName()
//----------------------------------------------------------
//Preenchimento dos dados nas tabelas
//----------------------------------------------------------
If MV_PAR04 == 1 .And. Len( aSelFil ) <= 0
aSelFil := AdmGetFil()
If Len( aSelFil ) <= 0
lAborta := .T.
Else
cfilqry := "("
For nX := 1 to len(aSelFil)
cfilqry += "'"+aSelFil[nX]+"',"
Next
cfilqry := substr(cfilqry,1,len(cfilqry)-1) + ")"
EndIf
EndIf
If ! lAborta
MsgRun("Carregando dados de movimentação financeira...",,{||CursorWait(),GrvFin(MV_PAR01),CursorArrow()})
dbSelectArea(cAlisGr1)
dbSelectArea(cAlisGr2)
//----------------------------------------------------------
//Montagem do browser
//----------------------------------------------------------
oBrowse := FwMBrowse():New()
oBrowse:SetDescription("Concilicao Financeira")
oBrowse:SetAlias(cAlisCab)
oBrowse:SetWalkThru(.F.)
oBrowse:SetAmbiente(.T.)
oBrowse:SetTemporary(.T.)
oBrowse:SetFields(aBrows)
oBrowse:AddLegend( "DIF_VAL == 0", "GREEN", "Correto" )
oBrowse:AddLegend( "DIF_VAL <> 0", "RED", "Incorreto" )
oBrowse:Activate()
EndIf
//--------------------------------
//Exclui tabelas temporarias
//--------------------------------
If oCabec <> Nil
oCabec:Delete()
oCabec := Nil
Endif
If oGrd1 <> Nil
oGrd1:Delete()
oGrd1 := Nil
Endif
If oGrd2 <> Nil
oGrd2:Delete()
oGrd2 := Nil
Endif
EndIF
FWRestArea(aArea)
Return
/*/{Protheus.doc} MenuDef
Menu da rotina
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function MenuDef()
Local aRot := {}
ADD OPTION aRot TITLE 'Detalhes' ACTION 'VIEWDEF.MCON001'OPERATION 4 ACCESS 0
ADD OPTION aRot TITLE 'Relatorio' ACTION 'U_MCON01A()' OPERATION 4 ACCESS 0
Return(Aclone(aRot))
/*/{Protheus.doc} ModelDef
Modelo de dados MVC para edicao da tabela temporaria
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function ModelDef()
Local oModel := Nil
Local osCabec := FWFormModelStruct():New()
Local osGrd1 := FWFormModelStruct():New()
Local osGrd2 := FWFormModelStruct():New()
Local nX
Local bPre := {|oModel, cAction, cIDField, xValue| validPre(oModel, cAction, cIDField, xValue)}
Local bPos := {|oModel| fieldValidPos(oModel)}
Local bLoad := {|oModel, lCopy| loadField(oModel, lCopy)}
Local bLoaGr1 := {|oModel, lCopy| loadGrd(oModel, lCopy,"GR1")}
Local bLoaGr2 := {|oModel, lCopy| loadGrd(oModel, lCopy,"GR2")}
For nX := 1 to Len(aBrows)
aBrows[nX,6]=.F.
Next
osCabec:AddTable(cAlisCab, {"ID"}, "Concilicao Financeira")
For nX := 1 to Len(aGrd1)
aadd(aGrd1[nX],.F.)
Next
For nX := 1 to Len(aGrd2)
aadd(aGrd2[nX],.F.)
Next
/*----------------------------------------------------------------------
Estrutura do array para montagem dos campos usados na funcao MntStrut
1 - Descricao
2 - Nome do Campo
3 - Tipo do campo
4 - Tamanho do campo
5 - Decimal
6 - Se campo e editavel
------------------------------------------------------------------------*/
MntStrut(@osCabec,cAlisCab,aBrows)
MntStrut(@osGrd1,cAlisGr1,aGrd1)
MntStrut(@osGrd2,cAlisGr2,aGrd2)
osCabec:AddTable(cAlisCab,, "Concilicao Financeira" ,{|| oCabec:GetRealName()})
osGrd1:AddTable(cAlisGr1,, "Concilicao Financeira" ,{|| oGrd1:GetRealName()})
osGrd2:AddTable(cAlisGr2,, "Contabilidade" ,{|| oGrd2:GetRealName()})
oModel := FWFormModel():New( 'mdMCON001',,,{|oModel| commit()},{|oModel| cancel()})
oModel:AddFields( 'ID_M_FLD', , osCabec,bPre,bPos,bLoad)
oModel:AddGrid( 'ID_M_GRD1', 'ID_M_FLD', osGrd1, /*bLinePre*/, /*{|oModelZA2| ValLinha(oModelZA2)}*/, /*bPreVal*/,/*{|oModel| ValLinha(oModel)}*/, bLoaGr1/*bLoad1*/)
oModel:AddGrid( 'ID_M_GRD2', 'ID_M_FLD', osGrd2, /*bLinePre*/, /*{|oModelZA2| ValLinha(oModelZA2)}*/, /*bPreVal*/,/*{|oModel| ValLinha(oModel)}*/, bLoaGr2/* bLoad2*/)
oModel:SetRelation( 'ID_M_GRD1', {{'ID','ID'}}, (cAlisGr1)->(IndexKey(1)))
oModel:SetRelation( 'ID_M_GRD2', {{'ID','ID'}}, (cAlisGr2)->(IndexKey(1)))
oModel:GetModel( 'ID_M_GRD1' ):SetUniqueLine( { 'ITEM'} )
oModel:GetModel( 'ID_M_GRD2' ):SetUniqueLine( { 'ITEM'} )
oModel:SetPrimaryKey({ 'ID' })
oModel:AddCalc( 'TOTAL', 'ID_M_FLD', 'ID_M_GRD1', 'SALDO' , '_nVlrOcor', 'SUM' , ,,'Total Saldo',/*{ |oModel| AGL300H( oModel)} */ )
oModel:AddCalc( 'TOTAL2', 'ID_M_FLD', 'ID_M_GRD2', 'SALDO' , '_nVlrOco2', 'SUM' , ,,'Total Saldo',/*{ |oModel| AGL300H( oModel)} */ )
oModel:SetDescription( 'Conciliacao Financeiro' )
oModel:GetModel( 'ID_M_GRD1' ):SetDescription( 'Contabilidade' )
oModel:GetModel( 'ID_M_GRD1' ):SetDescription( 'Financeiro' )
Return oModel
/*/{Protheus.doc} fieldValidPos
Funcao de validacao pos carregamento dos dados
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function fieldValidPos(oModel)
Local lRet := .T.
oModel:GetModel():SetErrorMessage('mdMCON001', "ID" , 'mdMCON001' , 'ID' , "ITEM")
Return lRet
/*/{Protheus.doc} validPre
Funcao de validação dos dados de carregamento
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function validPre(oModel, cAction, cIDField, xValue)
Local lRet := .T.
oModel:GetModel():SetErrorMessage('mdMCON001', "ID" , 'mdMCON001' , 'ID' , "ITEM")
Return lRet
/*/{Protheus.doc} loadField
Funcao de carregamento dos dados para o Fields
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function loadField(oModel, lCopy)
Local aLoad := {}
Local nI as numeric
Local aLine as array
Local xValue as variant
aLine := {}
For nI := 1 to Len(aBrows)
If aBrows[nI][3] == "C"
xValue := (cAlisCab)->&(aBrows[nI,2])
Elseif aBrows[nI][3] == "D"
xValue := StoD((cAlisCab)->&(aBrows[nI,2]))
Elseif aBrows[nI][3] == "N"
xValue := (cAlisCab)->&(aBrows[nI,2])
Else
xValue := .F.
Endif
aAdd(aLine, xValue)
Next
aAdd(aLoad, aLine) //dados
aAdd(aLoad, 1) //recno
Return aLoad
/*/{Protheus.doc} Commit
Funcao de valicao do comit da tela
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function Commit()
Return .T.
/*/{Protheus.doc} Cancel
Funcao de valicao do cancelamento do tela
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function Cancel()
Return .T.
/*/{Protheus.doc} ViewDef
Visao de dados MVC para montagem da tela da tabela temporaria
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function ViewDef()
Local oModel := FWLoadModel("MCON001")
Local osCabec := FWFormViewStruct():New()
Local osGrd1 := FWFormViewStruct():New()
Local osGrd2 := FWFormViewStruct():New()
Local oView := Nil
Local nX
Local aDadCab := {}
Local aDadGr1 := {}
Local aDadGr2 := {}
/*----------------------------------------------------------------------
Estrutura do array para montagem dos campos usados na funcao MntView
1 - Nome do Campo
2 - Ordem
3 - Titulo do campo
4 - Tipo do campo
5 - Picture
6 - Se campo e editavel
------------------------------------------------------------------------*/
For nX := 1 to Len(aBrows)
IF aBrows[nX,3] = "C"
cPict := "@!"
ElseIF aBrows[nX,3] = "N"
cPict := "@E 9,999,999.99"
Else
cPict := ""
EnDIF
aADD(aDadCab,{aBrows[nX,2],StrZero(nX,2),aBrows[nX,1],aBrows[nX,3],cPict,.F.})
Next
For nX := 1 to Len(aGrd1)
IF aGrd1[nX,3] = "C"
cPict := "@!"
ElseIF aGrd1[nX,3] = "N"
cPict := "@E 9,999,999.99"
Else
cPict := ""
EnDIF
aADD(aDadGr1,{aGrd1[nX,2],StrZero(nX,2),aGrd1[nX,1],aGrd1[nX,3],cPict,.F.})
Next
For nX := 1 to Len(aGrd2)
IF aGrd2[nX,3] = "C"
cPict := "@!"
ElseIF aGrd2[nX,3] = "N"
cPict := "@E 9,999,999.99"
Else
cPict := ""
EnDIF
aADD(aDadGr2,{aGrd2[nX,2],StrZero(nX,2),aGrd2[nX,1],aGrd2[nX,3],cPict,.F.})
Next
MntView(@osCabec,aDadCab)
MntView(@osGrd1,aDadGr1)
MntView(@osGrd2,aDadGr2)
oView := FWFormView():New()
oView:SetModel(oModel)
oView:AddField("ID_V_FLD", osCabec, "ID_M_FLD")
oView:AddGrid("ID_V_GRD1", osGrd1, "ID_M_GRD1")
oView:AddGrid("ID_V_GRD2", osGrd2, "ID_M_GRD2")
oView:CreateHorizontalBox("SUPERIOR",30)
oView:CreateHorizontalBox("INFERIOR",70)
oView:CreateVerticalBox('ESQUERDA', 50 , 'INFERIOR')
oView:CreateVerticalBox("DIREITA", 50 , 'INFERIOR')
oView:SetOwnerView( 'ID_V_FLD' , 'SUPERIOR' )
oView:SetOwnerView( 'ID_V_GRD1' , 'ESQUERDA' )
oView:SetOwnerView( 'ID_V_GRD2' , 'DIREITA' )
//Colocando título do formulário
oView:EnableTitleView('ID_V_FLD', 'Conciliacao Financeiro' )
oView:EnableTitleView('ID_V_GRD1', 'Contabilidade' )
oView:EnableTitleView('ID_V_GRD2', 'Financeiro' )
oView:SetCloseOnOk({||.T.})
Return oView
/*/{Protheus.doc} gerCpBrow
Funcao para montar array com colunas para browse
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function gerCpBrow(aCampos,aTitulos)
Local nX
Local aBrows := {}
For nX := 1 to Len(aCampos)
aAdd(aBrows,{aTitulos[nX], aCampos[nX,1] ,aCampos[nX,2] ,aCampos[nX,3] ,aCampos[nX,4]})
Next
Return aBrows
/*/{Protheus.doc} MntStrut
Funcao para montar estrutura de dados para ModelDef
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function MntStrut(oObj,cAlias, aCampos)
Local nX
Default aCampos := {}
For nX := 1 to Len(aCampos)
oObj:AddField(;
aCampos[nX,1],; // [01] C Titulo do campo
aCampos[nX,1],; // [02] C ToolTip do campo
aCampos[nX,2],; // [03] C Id do Field
aCampos[nX,3],; // [04] C Tipo do campo
aCampos[nX,4],; // [05] N Tamanho do campo
aCampos[nX,5],; // [06] N Decimal do campo
Nil,; // [07] B Code-block de validação do campo
Nil,; // [08] B Code-block de validação When do campo
{},; // [09] A Lista de valores permitido do campo
.F.,; // [10] L Indica se o campo tem preenchimento obrigatório
FwBuildFeature( STRUCT_FEATURE_INIPAD, "Iif(!INCLUI,('"+cAlias+"')->"+aCampos[nX,2]+",'')" ),; // [11] B Code-block de inicializacao do campo
.T.,; // [12] L Indica se trata-se de um campo chave
aCampos[nX,6],; // [13] L Indica se o campo pode receber valor em uma operação de update.
.F.; // [14] L Indica se o campo é virtual
)
IF aCampos[nX,6]
oObj:SetProperty(aCampos[nX,2], MODEL_FIELD_WHEN, { || .T.})
oObj:SetProperty(aCampos[nX,2], MODEL_FIELD_NOUPD,.F.)
EndIF
Next
Return
/*/{Protheus.doc} MntView
Funcao para montar estrutura de dados para ViewDef
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function MntView(oObj,aCampos)
Local nX
For nX := 1 to Len(aCampos)
//Adicionando campos da estrutura
oObj:AddField(;
aCampos[nX,1],; // [01] C Nome do Campo
aCampos[nX,2],; // [02] C Ordem
aCampos[nX,3],; // [03] C Titulo do campo
aCampos[nX,3],; // [04] C Descricao do campo
Nil,; // [05] A Array com Help
aCampos[nX,4],; // [06] C Tipo do campo
aCampos[nX,5],; // [07] C Picture
Nil,; // [08] B Bloco de PictTre Var
Nil,; // [09] C Consulta F3
aCampos[nX,6],; // [10] L Indica se o campo é alteravel
Nil,; // [11] C Pasta do campo
Nil,; // [12] C Agrupamento do campo
Nil,; // [13] A Lista de valores permitido do campo (Combo)
Nil,; // [14] N Tamanho maximo da maior opção do combo
Nil,; // [15] C Inicializador de Browse
Nil,; // [16] L Indica se o campo é virtual
Nil,; // [17] C Picture Variavel
Nil; // [18] L Indica pulo de linha após o campo
)
Next
Return
/*/{Protheus.doc} GrvFin
Funcao que ira criar e popular a tabela temporaria
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static function GrvFin(cContab)
Local aArea := FWGetArea()
Local aTam := TamSX3("E1_CLIENTE")
Local aCampos := {}
Local _oFINR5501
Private cSE5KeyAnt := ""
aAdd(aCampos,{"CODIGO" ,"C",aTam[1],aTam[2]})
aAdd(aCampos,{"SALDOA" ,"N",18,2})
aAdd(aCampos,{"VALORD" ,"N",18,2})
aAdd(aCampos,{"VALORC" ,"N",18,2})
_oFINR5501 := FWTemporaryTable():New( "cNomeArq" )
_oFINR5501:SetFields(aCampos)
_oFINR5501:AddIndex("1", {"CODIGO"})
//------------------
//Criação da tabela temporaria
//------------------
_oFINR5501:Create()
//------------------
//Localiza e grava titulos a receber dentro dos parametros
//------------------
cQuery := "SELECT TOP 100 * FROM " + RetSqlName("SE2") + " E2 (NOLOCK) WHERE"
cQuery += " E2.D_E_L_E_T_ <> '*'"
dbUseArea(.T., "TOPCONN", TCGenQry(,,cQuery), 'TRBSE2', .F., .T.)
dbselectarea("TRBSE2")
While !TRBSE2->(Eof())
//------------------
//Le registros com data anterior a data inicial (para compor
//os saldos anteriores) ate a data final.
//------------------
If TRBSE2->E2_TIPO $ MVABATIM
dbSelectArea("TRBSE2")
TRBSE2->(dbSkip( ))
Loop
Endif
//------------------
//Grava debito no arquivo de trabalho
//------------------
_dEmissao := stod(TRBSE2->E2_EMIS1)
aMOVI := {10,20,30}
If( cNomeArq->(dbseek(TRBSE2->E2_FORNECE)))
Reclock( "cNomeArq", .F. )
cNomeArq->SALDOA += aMOVI[1]
cNomeArq->VALORD += aMOVI[2]
cNomeArq->VALORC += aMOVI[3]
cNomeArq->( MsUnlock() )
Else
Reclock( "cNomeArq", .T. )
cNomeArq->CODIGO := TRBSE2->E2_FORNECE
cNomeArq->SALDOA := aMOVI[1]
cNomeArq->VALORD := aMOVI[2]
cNomeArq->VALORC := aMOVI[3]
cNomeArq->( MsUnlock() )
Endif
dbSelectArea("TRBSE2")
TRBSE2->(dbSkip())
Enddo
dbselectarea("cNomeArq")
cNomeArq->(dbgotop())
cCodigo := ""
nSaldoAtu := 0
nTotDeb := 0
nTotCrd := 0
While cNomeArq->(!Eof())
cCodigo := cNomeArq->CODIGO
cLoja := ""
nSaldoAtu := 0
nTotDeb := 0
nTotCrd := 0
lNoSkip := .f.
nSaldoAtu += cNomeArq->SALDOA
nTotDeb += ABS(cNomeArq->VALORD)
nTotCrd += ABS(cNomeArq->VALORC)
cNomeArq->(dbSkip())
//cAlisCab := oGrd1:GetRealName()
If cNomeArq->(Eof()) .or. cNomeArq->CODIGO <> cCodigo
//------------------
//Grava debito no arquivo de trabalho
//------------------
dbSelectArea(cAlisCab)
(cAlisCab)->(dbSetOrder(2))
Reclock((cAlisCab),.t.)
Replace ID With cCodigo
Replace ITEM_TAB With cCodigo
IF SA1->(dbseek(xfilial("SA1")+cCodigo))
Replace DESC_ITEM With SA1->A1_NOME
ElseIf SA2->(dbseek(xfilial("SA2")+cCodigo))
Replace DESC_ITEM With SA2->A2_NOME
Endif
Replace FIN_SLA With nSaldoAtu
Replace FIN_DEB With nTotDeb
Replace FIN_CRE With nTotCrd
Replace FIN_SLF With FIN_SLA + FIN_DEB - FIN_CRE
MsUnlock()
////grid 1
dbSelectArea(cAlisGr1)
(cAlisGr1)->(dbSetOrder(2))
Reclock((cAlisGr1),.t.)
Replace ID With cCodigo
Replace ITEM With cCodigo
Replace FIN_DEB With nTotDeb
MsUnlock()
///grid 2
// dbSelectArea(cAlisGr2)
// (cAlisGr2)->(dbSetOrder(2))
// Reclock((cAlisGr2),.t.)
// Replace ID With cCodigo
// Replace ITEM With cCodigo
// Replace FIN_DEB With nTotDeb
// MsUnlock()
Endif
EndDo
FWRestArea(aArea)
Return
/*/{Protheus.doc} User Function MCON01A
Funcao para gerar um relatorio com os dados
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
User Function MCON01A()
Private oReport
Private oSecCabc
Private oSecFinan
Private oSecCont
oReport := ReportDef()
oReport:PrintDialog()
Return
/*/{Protheus.doc} ReportDef
Funcao que monta as definições do relatório TReport
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function ReportDef()
oReport := TReport():New("MCON001","Conciliacao Financeiro Contabil",,{|oReport| PrintReport(oReport)},"Conciliacao Financeiro Contabil")
oReport:SetLandscape(.T.) //Paisagem
oReport:lDisableOrientation := .T.
oReport:HideParamPage()//desabilita a impressão da pagina de parâmetros
oSecCabc := TRSection():New(oReport,"SINTETICO",)
TRCell():New( oSecCabc ,"ITEM_TAB" ,"" ,"Codigo" ,"@!"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"DESC_ITEM" ,"" ,"Nome" ,"@!"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"FIN_SLA" ,"" ,"Fin. Sl. Ant." ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"FIN_DEB" ,"" ,"Fin. Debito" ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"FIN_CRE" ,"" ,"Fin. Credito" ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"FIN_SLF" ,"" ,"Fin. Sl. Fin." ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"CTB_SLA" ,"" ,"Ctb. Sl. Ant." ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"CTB_DEB" ,"" ,"Ctb. Debito" ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"CTB_CRE" ,"" ,"Ctb. Credito" ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"CTB_SLF" ,"" ,"Ctb. Sl. Fin." ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"DEF_TAB" ,"" ,"Div." ,"@!"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New( oSecCabc ,"DIF_VAL" ,"" ,"Diferença" ,"@E 99,999,999.99"/*Picture*/ , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
oSecFinan := TRSection():New(oReport,"FINANCEIRO","TRB")
TRCell():New(oSecFinan ,"DATS" ,"" ,"Data" ,X3Picture("E1_EMISSAO"), /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecFinan ,"DOC" ,"" ,"Documento" ,X3Picture("E1_MUM") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecFinan ,"HIST" ,"" ,"Historico" ,X3Picture("E1_HIST") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecFinan ,"FIN_DEB" ,"" ,"Fin. Debit." ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecFinan ,"FIN_CRED" ,"" ,"Fin. Cred." ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecFinan ,"SALDO" ,"" ,"Saldo" ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
oSecFinan:SetLinesBefore(2)
oSecCont := TRSection():New(oReport,"CONTABIL","TRB")
TRCell():New(oSecCont ,"DATS" ,"" ,"Data" ,X3Picture("E1_EMISSAO"), /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecCont ,"LOTE" ,"" ,"Lote" ,X3Picture("E1_MUM") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecCont ,"HIST" ,"" ,"Historico" ,X3Picture("E1_HIST") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecCont ,"FIN_DEB" ,"" ,"Fin. Debit." ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecCont ,"FIN_CRED" ,"" ,"Fin. Cred." ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSecCont ,"SALDO" ,"" ,"Saldo" ,X3Picture("E1_VALOR") , /*nSize*/,/*lPixel*/,/*bBlock*/,"LEFT"/*cAlign*/,/*lLineBreak*/,"LEFT"/*cHeaderAlign*/,/*lCellBreak*/,/*nColSpace*/,.T./*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
oSecCont:SetLinesBefore(2)
Return(oReport)
/*/{Protheus.doc} PrintReport
Função que faz a impressão do relatório TReport
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function PrintReport(oReport)
oSecCabc:Init()
oSecCabc:Cell("ITEM_TAB"):SetValue((cAlisCab)->ITEM_TAB)
oSecCabc:Cell("DESC_ITEM"):SetValue((cAlisCab)->DESC_ITEM)
oSecCabc:Cell("FIN_SLA"):SetValue((cAlisCab)->FIN_SLA)
oSecCabc:Cell("FIN_DEB"):SetValue((cAlisCab)->FIN_DEB)
oSecCabc:Cell("FIN_CRE"):SetValue((cAlisCab)->FIN_CRE)
oSecCabc:Cell("FIN_SLF"):SetValue((cAlisCab)->FIN_SLF)
oSecCabc:Cell("CTB_SLA"):SetValue((cAlisCab)->CTB_SLA)
oSecCabc:Cell("CTB_DEB"):SetValue((cAlisCab)->CTB_DEB)
oSecCabc:Cell("CTB_CRE"):SetValue((cAlisCab)->CTB_CRE)
oSecCabc:Cell("CTB_SLF"):SetValue((cAlisCab)->CTB_SLF)
oSecCabc:Cell("DEF_TAB"):SetValue((cAlisCab)->DEF_TAB)
oSecCabc:Cell("DIF_VAL"):SetValue((cAlisCab)->DIF_VAL)
oSecCabc:PrintLine()
oSecCabc:Finish()
oReport:Say(oReport:Row()+20, 10, " --------------------DADOS FINANCEIROS--------------------")
oSecFinan:Init()
(cAlisGr1)->(dbSetOrder(1))
(cAlisGr1)->(dbGoTop())
IF (cAlisGr1)->(dbSeek((cAlisCab)->ID))
While !(cAlisGr1)->(Eof()) .AND. (cAlisGr1)->ID = (cAlisCab)->ID
If oReport:Cancel()
Exit
EndIf
oSecFinan:Cell("DATS"):SetValue((cAlisGr1)->DATS)
oSecFinan:Cell("DOC"):SetValue((cAlisGr1)->DOC)
oSecFinan:Cell("HIST"):SetValue((cAlisGr1)->HIST)
oSecFinan:Cell("FIN_DEB"):SetValue((cAlisGr1)->FIN_DEB)
oSecFinan:Cell("FIN_CRED"):SetValue((cAlisGr1)->FIN_CRED)
oSecFinan:Cell("SALDO"):SetValue((cAlisGr1)->SALDO)
oSecFinan:PrintLine()
(cAlisGr1)->(dbSkip())
EndDo
EndIF
oSecFinan:Finish()
oReport:Say(oReport:Row()+20, 10, " --------------------DADOS CONTABILIDADE--------------------")
oSecCont:Init()
(cAlisGr2)->(dbSetOrder(1))
(cAlisGr2)->(dbGoTop())
IF (cAlisGr2)->(dbSeek((cAlisCab)->ID))
While !(cAlisGr2)->(Eof()) .AND. (cAlisGr2)->ID = (cAlisCab)->ID
If oReport:Cancel()
Exit
EndIf
oSecCont:Cell("DATS"):SetValue((cAlisGr2)->DATS)
oSecCont:Cell("LOTE"):SetValue((cAlisGr2)->LOTE)
oSecCont:Cell("HIST"):SetValue((cAlisGr2)->HIST)
oSecCont:Cell("FIN_DEB"):SetValue((cAlisGr2)->FIN_DEB)
oSecCont:Cell("FIN_CRED"):SetValue((cAlisGr2)->FIN_CRED)
oSecCont:Cell("SALDO"):SetValue((cAlisGr2)->SALDO)
oSecCont:PrintLine()
(cAlisGr2)->(dbSkip())
EndDo
EndIf
oSecCont:Finish()
oReport:StartPage()
oReport:EndPage()
Return
/*/{Protheus.doc} loadGrd
Função responsável pela carga dos modelos em MVC
@type Function
@author Fabricio Antunes
@since 22/09/2021
/*/
Static Function loadGrd(oSub,lCopy,cIdSub)
Local cAliasTab := ""
Local nI := 0
Local nRec := 1
Local aFldSub := {}
Local aRet := {}
Local aAux := {}
aFldSub := oSub:GetStruct():GetFields()
If ( cIdSub == "GR1" )
cAliasTab := oGrd1:GetAlias()
ElseIf ( cIdSub == "GR2" )
cAliasTab := oGrd2:GetAlias()
EndIf
(cAliasTab)->(dbSetOrder(1))
(cAliasTab)->(dbGoTop())
IF (cAliasTab)->(dbSeek((cAlisCab)->ID))
While !(cAliasTab)->(Eof()) .AND. (cAliasTab)->ID = (cAlisCab)->ID
For nI := 1 to Len(aFldSub)
If ( (cAliasTab)->(FieldPos(aFldSub[nI,3])) > 0 )
aAdd(aAux,(cAliasTab)->&(aFldSub[nI,3]))
Else
aAdd(aAux,GTPCastType(,aFldSub[nI,4]))
EndIf
Next nI
aAdd(aRet,{nRec,aClone(aAux)})
aAux := {}
nRec++
(cAliasTab)->(DbSkip())
EndDo
EndIF
Return(aRet)
Bom pessoal, por hoje é só.
Abraços e até a próxima.




Ola bom dia, poderia complementar o exmplo pois VIEWDEF.MCON001 no menu não abre a tela de detalhes, aterando o mesmo para VIEWDEF.MCON00X tenho o seguinte erro
ERROR: argumento #0 , parâmetro oObj erro, previsto O->U
Boa tarde Paulo, tudo bem?
Opa, como esse exemplo foi gentilmente disponibilizado pelo Fabrício Antunes, caso ele consiga complementar, nós atualizamos aqui.
Complementando, dê uma olhada nesse artigo também, para ver se não é uma das possibilidades ai: https://terminaldeinformacao.com/2021/01/06/o-que-pode-ser-quando-botoes-nao-funcionam-em-mvc/
Um grande abraço.
Bom dia, obrigado pelo exemplo, funcionou perfeitamente aqui! Tive somente uma dúvida com relação ao porque do uso do “GTPCastType” na Static Function loadGrd. Não encontrei documentação sobre isso. Trata-se de uma função reservada da TOTVS? Obrigado!
Boa tarde Pedro.
Essa função ela faz a conversão de tipo de dado, então nesse exemplo, se ele não encontrar o campo, irá formatar o valor nulo conforme o tipo do campo (se o campo for caractere ou memo, volta espaço vazio, se for numérico, volta 0, se for data volta a database e se for lógico volta .f.).
Bom dia pessoal!
Surgiu a necessidade aqui de montar uma tela nesse estilo, porém com a grid da direita (osGrd2) sendo do tipo FwMarkBrowse, e sendo filha da grid da esquerda (essa pode ser FwFormStruct). Como poderia fazer essa construção? Estou com dificuldade em algumas coisas, por exemplo na hora de setar o owner do oMarkBrowse.
Obrigado.
Bom dia Pedro, tudo joia?
Talvez a forma mais fácil seja você criar um campo no configurador nessa sua tabela que ficará no grid da direita, que seja do tipo Lógico (checkbox) e ver como o sistema se comporta, se ele faz nativamente. Que daí você não precisa se preocupar em montar uma FWMarkBrowse.
Se mesmo assim não der certo, o que você pode fazer, é nessa área da direita sua, você utilizar um AddOtherObject, que ele vai construir um Painel, e ai você usa essa painel como owner para instanciar seus objetos.
Caso você queira, no curso de MVC nosso ( https://terminaldeinformacao.com/2022/04/22/curso-mvc-em-advpl/ ), nós abordamos os assuntos de box verticais e adição de outros objetos com AddOtherObject nas aulas 14 e 32.
Um grande abraço.
Primeiro obrigado pelo exemplo.
Para trazer os dados do GRID achava que somente populando a tabela eles seriam trazidos, mas vi que vc está usando a static function loadGrd.
Se precisa retornar o array do ‘bload’, afinal pra que popular a tabela temporária?
Tenho um exemplo com 2 grids que todos eles são preenchidos pelo ‘bload’ e não uso FWTemporaryTable. Eu ainda não percebi a vantagem em usá-lo.
Bom dia Gabriel, tudo joia?
Opa, nós é que agradecemos o comentário. Respondendo sua dúvida, sim, somente ao preencher a temporária e usar os relacionamentos, atualmente funciona normalmente (sem precisar passar o bLoad).
Mas no caso, como esse é um exemplo de uns 2 anos atrás (Setembro de 2021), se você quiser (ou tiver dúvidas), logo no começo do artigo, tem o contato do grande Fabricio Antunes, que foi quem gentilmente dispôs um pouco do seu tempo e nos mandou esse exemplo para publicarmos.
Se você quiser também, nos envie um prw de exemplo (que você citou que utiliza) que atualizamos aqui no artigo.
Ficamos no aguardo.
Um grande abraço.
Boa Tarde Daniel tudo bem, Voce tem algum exemplo modelo1 com cabeçalho e grid usando tabela fora do padrão do protheus.
Desde já agradeceço a atenção.
Bom dia Wanderson, tudo joia graças a Deus e você?
Então, de Modelo 1 (que usa apenas uma tabela), nós temos um exemplo que vai ao ar agora no dia 03/01. Se você for assinante premium, e quiser receber o exemplo antecipadamente, nos mande um email ( terminaldeinformacao.com/contato ) que lhe enviamos o prw de exemplo de antemão.
Agora de Modelo 3 (que tem duas tabelas, uma para cabeçalho e uma para grid), vamos ficar devendo momentaneamente.
Um grande abraço.
Bom dia estou usando o modelo acima , porem os campos estão bloqueados para edição alguém conseguiu implementar com a opção alterar?
Bom dia Paulo, tudo joia?
Tem dois exemplos de Modelo X (uma pai com duas grids), sendo o primeiro na aula 9 do curso de MVC ( https://terminaldeinformacao.com/2022/04/22/curso-mvc-em-advpl/ ).
O segundo, se você quiser, entra em Autumn Code Maker ( https://autumncodemaker.com/ ), ai lá tem a opção para você gerar o molde do código fonte.
Um grande abraço.