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.