Olá pessoal…
Hoje vou mostrar um exemplo que fiz, de função para cadastro de dados em uma tabela genérica – SX5.
O intuito da rotina, é liberar o cadastro de informações nas tabelas genéricas para um usuário comum, que se fosse uma pequena AxCadastro de manipulação dos dados.
Para utilizar a rotina, basta passar por parâmetro qual é a tabela genérica e o título da rotina.
Ao executar, é mostrado uma grid com os registros da tabela genérica.
Ao clicar em Incluir, Alterar, etc é mostrado a tela, porém sem o campo de Tabela, apenas a Chave e os dados, e a Chave liberada apenas na Inclusão.
Fonte Novo (2024)
Foi atualizado o fonte, dando uma modernizada nos comandos.
Abaixo segue o código:
//Bibliotecas
#Include "Totvs.ch"
#Include "FWMVCDef.ch"
//Variveis Estaticas
Static cTitulo := "Tabelas Genéricas"
Static cAliasMVC := "SX5"
/*/{Protheus.doc} User Function zCadSX5
Cadastro de Tabela Genérica
@author Atilio
@since 15/02/2024
@version 1.0
@type function
@example
u_zCadSX5("01", "Séries de NF")
/*/
User Function zCadSX5(cTabela, cTitTela)
Local aArea := FWGetArea()
Local oBrowse
Local aInfoCab := {}
Local aColunas := {}
Private aRotina := {}
Private cTabGen := ""
Default cTabela := ""
Default cTitTela := ""
//Somente se tiver tabela
If ! Empty(cTabela)
//Atualiza a tabela em uso e o título
cTabGen := cTabela
cTitulo := cTitTela
//Se o título tiver vazio, busca do cadastro do cabeçalho da tabela
If Empty(cTitulo)
aInfoCab := FWGetSX5("00", cTabGen)
cTitulo := Alltrim(Capital(aInfoCab[1][4]))
EndIf
//Definicao do menu
aRotina := MenuDef()
//Adiciona as colunas que vão ser apresentadas no browse
aAdd(aColunas, { 'Código', 'X5_CHAVE', 'C', TamSX3('X5_CHAVE')[1], 0, ''})
aAdd(aColunas, { 'Descrição', 'X5_DESCRI', 'C', TamSX3('X5_DESCRI')[1], 0, ''})
//Instanciando o browse
oBrowse := FWMBrowse():New()
oBrowse:SetAlias(cAliasMVC)
oBrowse:SetOnlyFields({"X5_FILIAL"})
oBrowse:SetFields(aColunas)
oBrowse:SetDescription(cTitulo)
oBrowse:DisableDetails()
//Filtrando conforme a tabela que veio
oBrowse:SetFilterDefault("SX5->X5_TABELA == '" + cTabGen + "'")
//Ativa a Browse
oBrowse:Activate()
EndIf
FWRestArea(aArea)
Return Nil
/*/{Protheus.doc} MenuDef
Menu de opcoes na funcao zCadSX5
@author Atilio
@since 15/02/2024
@version 1.0
@type function
/*/
Static Function MenuDef()
Local aRotina := {}
//Adicionando opcoes do menu
ADD OPTION aRotina TITLE "Visualizar" ACTION "VIEWDEF.zCadSX5" OPERATION 1 ACCESS 0
ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.zCadSX5" OPERATION 3 ACCESS 0
ADD OPTION aRotina TITLE "Alterar" ACTION "VIEWDEF.zCadSX5" OPERATION 4 ACCESS 0
//ADD OPTION aRotina TITLE "Excluir" ACTION "VIEWDEF.zCadSX5" OPERATION 5 ACCESS 0
ADD OPTION aRotina TITLE "Copiar" ACTION "VIEWDEF.zCadSX5" OPERATION 9 ACCESS 0
Return aRotina
/*/{Protheus.doc} ModelDef
Modelo de dados na funcao zCadSX5
@author Atilio
@since 15/02/2024
@version 1.0
@type function
/*/
Static Function ModelDef()
Local oStruct := FWFormStruct(1, cAliasMVC)
Local oModel
Local bPre := Nil
Local bPos := {|| u_zSX5Vld()}
Local bCancel := Nil
//Editando características do dicionário
oStruct:SetProperty('X5_TABELA', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, '.F.')) //Modo de Edição
oStruct:SetProperty('X5_TABELA', MODEL_FIELD_INIT, FwBuildFeature(STRUCT_FEATURE_INIPAD, 'cTabGen')) //Ini Padrão
oStruct:SetProperty('X5_CHAVE', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, 'Iif(INCLUI, .T., .F.)')) //Modo de Edição
oStruct:SetProperty('X5_CHAVE', MODEL_FIELD_VALID, FwBuildFeature(STRUCT_FEATURE_VALID, 'u_zSX5Vld()')) //Validação de Campo
oStruct:SetProperty('X5_CHAVE', MODEL_FIELD_OBRIGAT, .T. ) //Campo Obrigatório
oStruct:SetProperty('X5_DESCRI', MODEL_FIELD_OBRIGAT, .T. ) //Campo Obrigatório
//Cria o modelo de dados para cadastro
oModel := MPFormModel():New("zCadSX5M", bPre, bPos, /*bCommit*/, bCancel)
oModel:AddFields("SX5MASTER", /*cOwner*/, oStruct)
oModel:SetPrimaryKey({'X5_FILIAL', 'X5_TABELA', 'X5_CHAVE'})
oModel:SetDescription("Modelo de dados - " + cTitulo)
oModel:GetModel("SX5MASTER"):SetDescription( "Dados de - " + cTitulo)
oModel:SetPrimaryKey({})
Return oModel
/*/{Protheus.doc} ViewDef
Visualizacao de dados na funcao zCadSX5
@author Atilio
@since 15/02/2024
@version 1.0
@type function
/*/
Static Function ViewDef()
Local cCamposPrin := "X5_CHAVE|X5_DESCRI|"
Local oModel := FWLoadModel("zCadSX5")
Local oStructPrin := FWFormStruct(2, cAliasMVC, {|cCampo| AllTrim(cCampo) $ cCamposPrin})
Local oStructOutr := FWFormStruct(2, cAliasMVC, {|cCampo| ! AllTrim(cCampo) $ cCamposPrin})
Local oView
//Retira as abas padrões
oStructPrin:SetNoFolder()
oStructOutr:SetNoFolder()
//Altera o título dos campos principais
oStructPrin:SetProperty('X5_CHAVE', MVC_VIEW_TITULO, 'Código')
oStructPrin:SetProperty('X5_DESCRI', MVC_VIEW_TITULO, 'Descrição')
//Altera o título dos outros campos
oStructOutr:SetProperty('X5_TABELA', MVC_VIEW_TITULO, 'Código Interno Tabela')
oStructOutr:SetProperty('X5_DESCSPA', MVC_VIEW_TITULO, 'Descrição Espanhol')
oStructOutr:SetProperty('X5_DESCENG', MVC_VIEW_TITULO, 'Descrição Inglês')
//Cria a visualizacao do cadastro
oView := FWFormView():New()
oView:SetModel(oModel)
oView:AddField("VIEW_PRIN", oStructPrin, "SX5MASTER")
oView:AddField("VIEW_OUTR", oStructOutr, "SX5MASTER")
//Cria o controle de Abas
oView:CreateFolder('ABAS')
oView:AddSheet('ABAS', 'ABA_PRIN', 'Cadastro')
oView:AddSheet('ABAS', 'ABA_OUTR', 'Outros Campos')
//Cria os Box que serão vinculados as abas
oView:CreateHorizontalBox('BOX_PRIN' ,100, /*owner*/, /*lUsePixel*/, 'ABAS', 'ABA_PRIN')
oView:CreateHorizontalBox('BOX_OUTR' ,100, /*owner*/, /*lUsePixel*/, 'ABAS', 'ABA_OUTR')
//Amarra as Abas aos Views de Struct criados
oView:SetOwnerView('VIEW_PRIN', 'BOX_PRIN')
oView:SetOwnerView('VIEW_OUTR', 'BOX_OUTR')
Return oView
/*/{Protheus.doc} zSX5Vld
Função que valida a digitação do campo Chave, para verificar se já existe
@type function
@author Atilio
@since 15/02/2024
@version 1.0
/*/
User Function zSX5Vld()
Local aArea := GetArea()
Local lRet := .T.
Local cX5Chave := FWFldGet("X5_CHAVE")
Local oModel := FWModelActive()
Local nOper := oModel:GetOperation()
//Se for operação de inclusão (ou a cópia)
If nOper == 3
DbSelectArea('SX5')
SX5->(DbSetOrder(1)) // X5_FILIAL+X5_TABELA+X5_CHAVE
SX5->(DbGoTop())
//Se conseguir posicionar, já existe
If SX5->(DbSeek(FWxFilial('SX5') + cTabGen + cX5Chave))
ExibeHelp("Help", "Código já existe!", "Informe um código diferente.")
lRet := .F.
EndIf
EndIf
RestArea(aArea)
Return lRet
Fonte Original (2016)
Abaixo o exemplo do código fonte desenvolvido lá em 2016, que na época funcionava bem no Protheus 11.
//Bibliotecas
#Include 'Protheus.ch'
#Include 'FWMVCDef.ch'
//Variáveis Estáticas
Static cTitulo := ""
/*/{Protheus.doc} zCadSX5
Cadastro de tabelas SX5
@author Atilio
@since 05/08/2016
@version 1.0
@param cTabela, character, Código da tabela genérica
@param cTitRot, character, Título da Rotina
@example
u_zCadSX5("01", "Séries de NF")
/*/
User Function zCadSX5(cTabela, cTitRot)
Local aArea := GetArea()
Local oBrowse
Local cFunBkp := FunName()
Default cTitRot := ""
Private cTabX := cTabela
//Senão tiver chave, finaliza
If Empty(cTabela)
Return
EndIf
DbSelectArea('SX5')
SX5->(DbSetOrder(1)) // X5_FILIAL+X5_TABELA+X5_CHAVE
SX5->(DbGoTop())
//Se vier título por parâmetro
If !Empty(cTitRot)
cTitulo := cTitRot
EndIf
//Se ainda tiver em branco, pega o da própria tabela
If Empty(cTitulo)
//Se conseguir posicionar
If SX5->(DbSeek(FWxFilial("SX5") + "00" + cTabela))
cTitulo := SX5->X5_DESCRI
Else
MsgAlert("Tabela não encontrada!", "Atenção")
Return
EndIf
EndIf
//Instânciando FWMBrowse - Somente com dicionário de dados
SetFunName("zCadSX5")
oBrowse := FWMBrowse():New()
//Setando a tabela de cadastro de Autor/Interprete
oBrowse:SetAlias("SX5")
//Setando a descrição da rotina
oBrowse:SetDescription(cTitulo)
//Filtrando
oBrowse:SetFilterDefault("SX5->X5_TABELA = '"+cTabela+"'")
//Ativa a Browse
oBrowse:Activate()
SetFunName(cFunBkp)
RestArea(aArea)
Return
/*---------------------------------------------------------------------*
| Func: MenuDef |
| Autor: Daniel Atilio |
| Data: 05/08/2016 |
| Desc: Criação do menu MVC |
*---------------------------------------------------------------------*/
Static Function MenuDef()
Local aRot := {}
//Adicionando opções
ADD OPTION aRot TITLE 'Visualizar' ACTION 'VIEWDEF.zCadSX5' OPERATION MODEL_OPERATION_VIEW ACCESS 0 //OPERATION 1
ADD OPTION aRot TITLE 'Incluir' ACTION 'VIEWDEF.zCadSX5' OPERATION MODEL_OPERATION_INSERT ACCESS 0 //OPERATION 3
ADD OPTION aRot TITLE 'Alterar' ACTION 'VIEWDEF.zCadSX5' OPERATION MODEL_OPERATION_UPDATE ACCESS 0 //OPERATION 4
ADD OPTION aRot TITLE 'Excluir' ACTION 'VIEWDEF.zCadSX5' OPERATION MODEL_OPERATION_DELETE ACCESS 0 //OPERATION 5
Return aRot
/*---------------------------------------------------------------------*
| Func: ModelDef |
| Autor: Daniel Atilio |
| Data: 05/08/2016 |
| Desc: Criação do modelo de dados MVC |
*---------------------------------------------------------------------*/
Static Function ModelDef()
//Criação do objeto do modelo de dados
Local oModel := Nil
//Criação da estrutura de dados utilizada na interface
Local oStSX5 := FWFormStruct(1, "SX5")
//Editando características do dicionário
oStSX5:SetProperty('X5_TABELA', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, '.F.')) //Modo de Edição
oStSX5:SetProperty('X5_TABELA', MODEL_FIELD_INIT, FwBuildFeature(STRUCT_FEATURE_INIPAD, 'cTabX')) //Ini Padrão
oStSX5:SetProperty('X5_CHAVE', MODEL_FIELD_WHEN, FwBuildFeature(STRUCT_FEATURE_WHEN, 'Iif(INCLUI, .T., .F.)')) //Modo de Edição
oStSX5:SetProperty('X5_CHAVE', MODEL_FIELD_VALID, FwBuildFeature(STRUCT_FEATURE_VALID, 'u_zSX5Chv()')) //Validação de Campo
oStSX5:SetProperty('X5_CHAVE', MODEL_FIELD_OBRIGAT, .T. ) //Campo Obrigatório
oStSX5:SetProperty('X5_DESCRI', MODEL_FIELD_OBRIGAT, .T. ) //Campo Obrigatório
//Instanciando o modelo, não é recomendado colocar nome da user function (por causa do u_), respeitando 10 caracteres
oModel := MPFormModel():New("zCadSX5M",/*bPre*/,/*bPos*/,/*bCommit*/,/*bCancel*/)
//Atribuindo formulários para o modelo
oModel:AddFields("FORMSX5",/*cOwner*/,oStSX5)
//Setando a chave primária da rotina
oModel:SetPrimaryKey({'X5_FILIAL', 'X5_TABELA', 'X5_CHAVE'})
//Adicionando descrição ao modelo
oModel:SetDescription("Modelo de Dados do Cadastro "+cTitulo)
//Setando a descrição do formulário
oModel:GetModel("FORMSX5"):SetDescription("Formulário do Cadastro "+cTitulo)
Return oModel
/*---------------------------------------------------------------------*
| Func: ViewDef |
| Autor: Daniel Atilio |
| Data: 05/08/2016 |
| Desc: Criação da visão MVC |
*---------------------------------------------------------------------*/
Static Function ViewDef()
//Criação do objeto do modelo de dados da Interface do Cadastro de Autor/Interprete
Local oModel := FWLoadModel("zCadSX5")
//Criação da estrutura de dados utilizada na interface do cadastro de Autor
Local oStSX5 := FWFormStruct(2, "SX5") //pode se usar um terceiro parâmetro para filtrar os campos exibidos { |cCampo| cCampo $ 'SX5_NOME|SX5_DTAFAL|'}
//Criando oView como nulo
Local oView := Nil
//Criando a view que será o retorno da função e setando o modelo da rotina
oView := FWFormView():New()
oView:SetModel(oModel)
//Atribuindo formulários para interface
oView:AddField("VIEW_SX5", oStSX5, "FORMSX5")
//Criando um container com nome tela com 100%
oView:CreateHorizontalBox("TELA",100)
//Colocando título do formulário
oView:EnableTitleView('VIEW_SX5', 'Dados - '+cTitulo )
//Força o fechamento da janela na confirmação
oView:SetCloseOnOk({||.T.})
//O formulário da interface será colocado dentro do container
oView:SetOwnerView("VIEW_SX5","TELA")
//Retira o campo de tabela da visualização
oStSX5:RemoveField("X5_TABELA")
Return oView
/*/{Protheus.doc} zSX5Chv
Função que valida a digitação do campo Chave, para verificar se já existe
@type function
@author Atilio
@since 05/08/2016
@version 1.0
/*/
User Function zSX5Chv()
Local aArea := GetArea()
Local lRet := .T.
Local cX5Chave := M->X5_CHAVE
DbSelectArea('SX5')
SX5->(DbSetOrder(1)) // X5_FILIAL+X5_TABELA+X5_CHAVE
SX5->(DbGoTop())
//Se conseguir posicionar, já existe
If SX5->(DbSeek(FWxFilial('SX5') + cTabX + cX5Chave))
MsgAlert("Já existe chave com esse código (<b>"+cX5Chave+"</b>)!", "Atenção")
lRet := .F.
EndIf
RestArea(aArea)
Return lRet
Bom pessoal, por hoje é só.
Abraços e até a próxima.


Onde devo chamar as funções estáticas?
Bom dia Maycon, tudo bem?
Não entendi a dúvida. Você quer acionar as funções estáticas isoladamente?
Pois como é um fonte em MVC, você não precisa fazer isso, basta criar uma função, por exemplo:
User Function zTst() u_zCadSX5("01", "Séries NF") ReturnAi no menu do sistema, você coloca essa função zTst.
Ao tentar excluir aparece esse erro “CPA_CODNAT”: invalid identifier
e a query
SELECT 1 REG FROM SX5300 DOM WHERE X5_FILIAL = ‘ ‘ AND X5_TABELA = ‘Z4’ AND X5_CHAVE = ‘329 ‘ AND EXISTS (SELECT 1 FROM CPA300 CDOM WHERE CPA_FILIAL = ‘ ‘ AND ‘LX’ = ‘Z4’ AND CPA_CODNAT = ‘329 ‘ AND CDOM.D_E_L_E_T_ = ‘ ‘ ) on FWESX9EXEC(FWEVALSX9.PRW) 27/06/2024 17:13:41 line : 513
o que pode ser?
Bom dia Vinicius, tudo joia?
Então, até deixei o Excluir comentado do fonte por causa disso.
Por padrão, o MVC vai validar se tem algum Relation na SX9 para poder prosseguir com a exclusão.
Como é a SX5, ai ele pode dar problemas na montagem da query que faz essa validação.
Então para exclusão de informações, o indicado é usar o SIGACFG.
Tenha uma ótima e abençoada terça feira.
Um grande abraço.