No artigo de hoje, vamos demonstrar em como criar um cadastro em MVC, de uma tabela do banco de dados que não esteja no dicionário (SX2, SX3 e SIX).
Recentemente, um aluno me perguntou se havia algum exemplo desse tipo de cadastro de tabela usando MVC. Como não tínhamos, decidi montar esse artigo com um exemplo de manipulação de uma tabela fora do dicionário.
No caso, quase sempre manipulamos tabelas do Protheus, mas pode ser que no mesmo banco de dados, existam outras tabelas que sejam necessário manipulação e podemos fazer isso direto no Protheus.
Nesse cenário, temos uma tabela no SQL Server, chamada de TABELA_JUBARTE, sendo que o script de criação dela foi o seguinte:
CREATE TABLE TABELA_JUBARTE ( CODIGO VARCHAR(6), NOME VARCHAR(100), APELIDO VARCHAR(30), ALTURA FLOAT );
Dentro dela, temos alguns registros, abaixo um print:
Agora vamos partir para a lógica da rotina em AdvPL, sendo que:
- Nós iremos montar uma tabela temporária com FWTemporaryTable com as mesmas colunas dessa tabela vista acima
- Após criar nossa tabela temporária, iremos inserir as informações nela, conforme uma query buscando os dados da tabela no SQL
- Iremos montar nosso ModelDef e ViewDef, criando a estrutura de campos (usamos esse artigo como exemplo)
- Na parte de gravação, iremos usar o recurso de FWModelEvent, para que possamos executar um AfterTTS (conforme explicado nesse artigo – Como utilizar a FWModelEvent no lugar do Commit em uma rotina MVC)
- Nesse AfterTTS, conforme o tipo da operação, iremos executar uma instrução no SQL atualizando a tabela (inserção = INSERT, atualização = UPDATE, exclusão = DELETE) com os campos inseridos no formulário
Obs.: Para executar as intruções como UPDATE e INSERT, usamos a função zExecQry, disponível para download nesse link – https://terminaldeinformacao.com/2021/04/21/como-fazer-um-update-via-advpl/
Abaixo um print de como fica a tela do browse ao acessar a rotina:
Veja que criamos 2 registros, alteramos 1 e excluímos 1, olhem como ficou o browse:
E agora veja ao executar a query no SQL como a tabela ficou após as operações:
Abaixo o código fonte principal da rotina em MVC:
//Bibliotecas
#Include "Totvs.ch"
#Include "FWMVCDef.ch"
//Variveis Estaticas
Static cTitulo := ""
/*/{Protheus.doc} User Function zCadTmp
Cadastro de temporária
@author Atilio
@since 27/07/2023
@version 1.0
/*/
User Function zCadTmp()
Local aArea := FWGetArea()
Local oBrowse
Local aCampos := {}
Local aColunas := {}
Local aPesquisa := {}
Local cQryUpd := ""
Local cTRealName := ""
Private aRotina := {}
Private cAliasTmp := GetNextAlias()
Private oTempTable
//Definicao do menu
aRotina := MenuDef()
//Campos da temporária
aAdd(aCampos, {"TMP_CODIGO", "C", 006, 0})
aAdd(aCampos, {"TMP_NOME", "C", 100, 0})
aAdd(aCampos, {"TMP_APELID", "C", 030, 0})
aAdd(aCampos, {"TMP_ALTURA", "N", 004, 2})
//Cria a temporária
oTempTable := FWTemporaryTable():New(cAliasTmp)
oTempTable:SetFields(aCampos)
oTempTable:AddIndex("1", {"TMP_CODIGO"} )
oTempTable:Create()
cTRealName := oTempTable:GetRealName()
cTitulo := "Tabela Temporária (TABELA_JUBARTE) - " + cTRealName
//Agora vamos inserir os dados na temporária conforme os que existem no banco de dados
cQryUpd := ""
cQryUpd += " INSERT INTO " + cTRealName + CRLF
cQryUpd += " (TMP_CODIGO, TMP_NOME, TMP_APELID, TMP_ALTURA) " + CRLF
cQryUpd += " SELECT CODIGO, NOME, APELIDO, ALTURA FROM TABELA_JUBARTE"
u_zExecQry(cQryUpd, .T.)
//Definindo as colunas que serão usadas no browse
aAdd(aColunas, {"Codigo", "TMP_CODIGO", "C", 006, 0, ""})
aAdd(aColunas, {"Nome", "TMP_NOME", "C", 100, 0, ""})
aAdd(aColunas, {"Apelido", "TMP_APELID", "C", 030, 0, ""})
aAdd(aColunas, {"Altura", "TMP_ALTURA", "N", 004, 2, "@E 9.99"})
//Adiciona os indices para pesquisar
/*
[n,1] Título da pesquisa
[n,2,n,1] LookUp
[n,2,n,2] Tipo de dados
[n,2,n,3] Tamanho
[n,2,n,4] Decimal
[n,2,n,5] Título do campo
[n,2,n,6] Máscara
[n,2,n,7] Nome Físico do campo - Opcional - é ajustado no programa
[n,3] Ordem da pesquisa
[n,4] Exibe na pesquisa
*/
aAdd(aPesquisa, {"Codigo", {{"", "C", 6, 0, "Codigo", "@!", "TMP_CODIGO"}} } )
//Criando o browse da temporária
oBrowse := FWMBrowse():New()
oBrowse:SetAlias(cAliasTmp)
oBrowse:SetTemporary(.T.)
oBrowse:SetFields(aColunas)
oBrowse:DisableDetails()
oBrowse:SetDescription(cTitulo)
oBrowse:SetSeek(.T., aPesquisa)
oBrowse:Activate()
oTempTable:Delete()
FWRestArea(aArea)
Return Nil
/*/{Protheus.doc} MenuDef
Menu de opcoes na funcao zCadTmp
@author Atilio
@since 27/07/2023
@version 1.0
/*/
Static Function MenuDef()
Local aRotina := {}
//Adicionando opcoes do menu
ADD OPTION aRotina TITLE "Visualizar" ACTION "VIEWDEF.zCadTmp" OPERATION 1 ACCESS 0
ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.zCadTmp" OPERATION 3 ACCESS 0
ADD OPTION aRotina TITLE "Alterar" ACTION "VIEWDEF.zCadTmp" OPERATION 4 ACCESS 0
ADD OPTION aRotina TITLE "Excluir" ACTION "VIEWDEF.zCadTmp" OPERATION 5 ACCESS 0
Return aRotina
/*/{Protheus.doc} ModelDef
Modelo de dados na funcao zCadTmp
@author Atilio
@since 27/07/2023
@version 1.0
/*/
Static Function ModelDef()
Local oModel := Nil
Local oStTMP := FWFormModelStruct():New()
//Na estrutura, define os campos e a temporária
oStTMP:AddTable(cAliasTmp, {'TMP_CODIGO', 'TMP_NOME', 'TMP_APELID', 'TMP_ALTURA'}, "Temporaria")
//Adiciona os campos da estrutura
oStTmp:AddField(;
"Codigo",; // [01] C Titulo do campo
"Codigo",; // [02] C ToolTip do campo
"TMP_CODIGO",; // [03] C Id do Field
"C",; // [04] C Tipo do campo
06,; // [05] N Tamanho do campo
0,; // [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
.T.,; // [10] L Indica se o campo tem preenchimento obrigatório
FwBuildFeature( STRUCT_FEATURE_INIPAD, "Iif(!INCLUI,"+cAliasTmp+"->TMP_CODIGO,'')" ),; // [11] B Code-block de inicializacao do campo
.T.,; // [12] L Indica se trata-se de um campo chave
.F.,; // [13] L Indica se o campo pode receber valor em uma operação de update.
.F.) // [14] L Indica se o campo é virtual
oStTmp:AddField(;
"Descricao",; // [01] C Titulo do campo
"Descricao",; // [02] C ToolTip do campo
"TMP_NOME",; // [03] C Id do Field
"C",; // [04] C Tipo do campo
100,; // [05] N Tamanho do campo
0,; // [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
.T.,; // [10] L Indica se o campo tem preenchimento obrigatório
FwBuildFeature( STRUCT_FEATURE_INIPAD, "Iif(!INCLUI,"+cAliasTmp+"->TMP_NOME,'')" ),; // [11] B Code-block de inicializacao do campo
.F.,; // [12] L Indica se trata-se de um campo chave
.F.,; // [13] L Indica se o campo pode receber valor em uma operação de update.
.F.) // [14] L Indica se o campo é virtual
oStTmp:AddField(;
"Apelido",; // [01] C Titulo do campo
"Apelido",; // [02] C ToolTip do campo
"TMP_APELID",; // [03] C Id do Field
"C",; // [04] C Tipo do campo
30,; // [05] N Tamanho do campo
0,; // [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
.T.,; // [10] L Indica se o campo tem preenchimento obrigatório
FwBuildFeature( STRUCT_FEATURE_INIPAD, "Iif(!INCLUI,"+cAliasTmp+"->TMP_APELID,'')" ),; // [11] B Code-block de inicializacao do campo
.F.,; // [12] L Indica se trata-se de um campo chave
.F.,; // [13] L Indica se o campo pode receber valor em uma operação de update.
.F.) // [14] L Indica se o campo é virtual
oStTmp:AddField(;
"Altura",; // [01] C Titulo do campo
"Altura",; // [02] C ToolTip do campo
"TMP_ALTURA",; // [03] C Id do Field
"N",; // [04] C Tipo do campo
04,; // [05] N Tamanho do campo
02,; // [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,"+cAliasTmp+"->TMP_ALTURA,'')" ),; // [11] B Code-block de inicializacao do campo
.F.,; // [12] L Indica se trata-se de um campo chave
.F.,; // [13] L Indica se o campo pode receber valor em uma operação de update.
.F.) // [14] L Indica se o campo é virtual
//Instanciando o modelo
oModel := MPFormModel():New("zCadTmpM",/*bPre*/, /*bPos*/,/*bCommit*/,/*bCancel*/)
oModel:AddFields("FORMTMP",/*cOwner*/,oStTMP)
oModel:SetPrimaryKey({'TMP_CODIGO'})
oModel:SetDescription("Modelo de Dados do Cadastro "+cTitulo)
oModel:GetModel("FORMTMP"):SetDescription("Formulário do Cadastro "+cTitulo)
//Instala um evento no modelo de dados que irá ficar "observando" as alterações do formulário
oModel:InstallEvent("VLD_TMPTAB", , zClassTempTab():New(oModel))
Return oModel
/*/{Protheus.doc} ViewDef
Visualizacao de dados na funcao zCadTmp
@author Atilio
@since 27/07/2023
@version 1.0
/*/
Static Function ViewDef()
Local oModel := FWLoadModel("zCadTmp")
Local oStTMP := FWFormViewStruct():New()
Local oView := Nil
//Adicionando campos da estrutura
oStTmp:AddField(;
"TMP_CODIGO",; // [01] C Nome do Campo
"01",; // [02] C Ordem
"Codigo",; // [03] C Titulo do campo
"Codigo",; // [04] C Descricao do campo
Nil,; // [05] A Array com Help
"C",; // [06] C Tipo do campo
"",; // [07] C Picture
Nil,; // [08] B Bloco de PictTre Var
Nil,; // [09] C Consulta F3
Iif(INCLUI, .T., .F.),; // [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
oStTmp:AddField(;
"TMP_NOME",; // [01] C Nome do Campo
"02",; // [02] C Ordem
"Nome",; // [03] C Titulo do campo
"Nome",; // [04] C Descricao do campo
Nil,; // [05] A Array com Help
"C",; // [06] C Tipo do campo
"",; // [07] C Picture
Nil,; // [08] B Bloco de PictTre Var
Nil,; // [09] C Consulta F3
.T.,; // [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
oStTmp:AddField(;
"TMP_APELID",; // [01] C Nome do Campo
"03",; // [02] C Ordem
"Apelido",; // [03] C Titulo do campo
"Apelido",; // [04] C Descricao do campo
Nil,; // [05] A Array com Help
"C",; // [06] C Tipo do campo
"",; // [07] C Picture
Nil,; // [08] B Bloco de PictTre Var
Nil,; // [09] C Consulta F3
.T.,; // [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
oStTmp:AddField(;
"TMP_ALTURA",; // [01] C Nome do Campo
"04",; // [02] C Ordem
"Altura",; // [03] C Titulo do campo
"Altura",; // [04] C Descricao do campo
Nil,; // [05] A Array com Help
"N",; // [06] C Tipo do campo
"@E 9.99",; // [07] C Picture
Nil,; // [08] B Bloco de PictTre Var
Nil,; // [09] C Consulta F3
.T.,; // [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
//Criando a view que será o retorno da função e setando o modelo da rotina
oView := FWFormView():New()
oView:SetModel(oModel)
oView:AddField("VIEW_TMP", oStTMP, "FORMTMP")
oView:CreateHorizontalBox("TELA",100)
oView:EnableTitleView('VIEW_TMP', 'Dados - '+cTitulo )
oView:SetCloseOnOk({||.T.})
oView:SetOwnerView("VIEW_TMP","TELA")
Return oView
E abaixo a classe usada para interceptar e usar o AfterTTS:
//Bibliotecas
#Include "TOTVS.ch"
/*/{Protheus.doc} zClassTempTab
Declara a Classe vinda da FWModelEvent e os métodos que serão utilizados
@author Atilio
@since 27/07/2023
@version version
@see https://tdn.totvs.com/pages/releaseview.action?pageId=269552294
/*/
Class zClassTempTab From FWModelEvent
Method New() CONSTRUCTOR
Method BeforeTTS()
Method InTTS()
Method AfterTTS()
EndClass
/*/{Protheus.doc} New
Método para "instanciar" um observador
@author Atilio
@since 27/07/2023
@version version
@param oModel, Objeto, Objeto instanciado do Modelo de Dados
/*/
Method New(oModel) CLASS zClassTempTab
Return
/*/{Protheus.doc} BeforeTTS
Método acionado antes de fazer as gravações da transação
@author Atilio
@since 27/07/2023
@version version
@param oModel, Objeto, Objeto instanciado do Modelo de Dados
/*/
Method BeforeTTS(oModel) Class zClassTempTab
//Aqui você pode fazer as operações antes de gravar
Return
/*/{Protheus.doc} InTTS
Método acionado durante as gravações da transação
@author Atilio
@since 27/07/2023
@version version
@param oModel, Objeto, Objeto instanciado do Modelo de Dados
/*/
Method InTTS(oModel) Class zClassTempTab
//Aqui você pode fazer as durante a gravação (como alterar campos)
Return
/*/{Protheus.doc} AfterTTS
Método acionado após as gravações da transação
@author Atilio
@since 27/07/2023
@version version
@param oModel, Objeto, Objeto instanciado do Modelo de Dados
/*/
Method AfterTTS(oModel) Class zClassTempTab
//Aqui você pode fazer as operações após gravar
Local nOperacao := oModel:GetOperation()
Local oModelTmp := oModel:GetModel("FORMTMP")
Local cQryUpd := ""
//Se for inclusão
If nOperacao == 3
cQryUpd += " INSERT INTO TABELA_JUBARTE " + CRLF
cQryUpd += " (CODIGO, NOME, APELIDO, ALTURA) " + CRLF
cQryUpd += " VALUES " + CRLF
cQryUpd += " ('" + oModelTmp:GetValue("TMP_CODIGO") + "', '" + oModelTmp:GetValue("TMP_NOME") + "', '" + oModelTmp:GetValue("TMP_APELID") + "', " + cValToChar(oModelTmp:GetValue("TMP_ALTURA")) + ") " + CRLF
//Se for alteração
ElseIf nOperacao == 4
cQryUpd += " UPDATE TABELA_JUBARTE " + CRLF
cQryUpd += " SET " + CRLF
cQryUpd += " NOME = '" + oModelTmp:GetValue("TMP_NOME") + "', " + CRLF
cQryUpd += " APELIDO = '" + oModelTmp:GetValue("TMP_APELID") + "', " + CRLF
cQryUpd += " ALTURA = " + cValToChar(oModelTmp:GetValue("TMP_ALTURA")) + CRLF
cQryUpd += " WHERE CODIGO = '" + oModelTmp:GetValue("TMP_CODIGO") + "' " + CRLF
//se for exclusão
ElseIf nOperacao == 5
cQryUpd += " DELETE FROM TABELA_JUBARTE " + CRLF
cQryUpd += " WHERE CODIGO = '" + oModelTmp:GetValue("TMP_CODIGO") + "' " + CRLF
EndIf
//Se houver query, executa atualizando a tabela direto no SQL
If ! Empty(cQryUpd)
u_zExecQry(cQryUpd, .T.)
EndIf
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.




eu consigo manipular o dicionario atraves de uma tabela temporaria?
Bom dia Diogo, tudo joia?
Você diz, que tem uma tabela temporária, vamos chamar ela de TMP, e ai você quer jogar essas informações para alguma tabela padrão, tipo SA1 (Clientes), SB1 (Produtos), SA2 (Fornecedores), entre outras?
Se sim, o que você pode fazer, é segundo esse exemplo que montamos acima, no AfterTTS você acionaria um ExecAuto para essa tabela em especícia, ou se for algum campo customizado, você acionar um RecLock.
Tenha um ótimo e abençoado fim de semana.
Um grande abraço.