Como criar um cadastro em MVC de uma tabela fora do dicionário do Protheus

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:

Exemplo de dados na tabela

 

Agora vamos partir para a lógica da rotina em AdvPL, sendo que:

  1. Nós iremos montar uma tabela temporária com FWTemporaryTable com as mesmas colunas dessa tabela vista acima
  2. Após criar nossa tabela temporária, iremos inserir as informações nela, conforme uma query buscando os dados da tabela no SQL
  3. Iremos montar nosso ModelDef e ViewDef, criando a estrutura de campos (usamos esse artigo como exemplo)
  4. 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)
  5. 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:

Browse logo ao abrir a rotina

 

Veja que criamos 2 registros, alteramos 1 e excluímos 1, olhem como ficou o browse:

Browse após algumas manipulações

 

E agora veja ao executar a query no SQL como a tabela ficou após as operações:

Exemplo de como ficou a tabela

 

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.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

Deixe uma resposta

Terminal de Informação