Olá pessoal…
Hoje vou mostrar como criar tabelas, campos e índices no Protheus via código fonte.
O processo é simples, criei um fonte chamado zCriaTab, que é responsável por atualizar o Dicionário de Dados (SX2, SX3 e SIX), basta então, enviar as informações para ele (no caso fiz uma função teste, chamada zTstTab).
Basicamente então, você deve compilar o zCriaTab em seu ambiente, e criar outra rotina que irá enviar as informações necessárias para criação das tabelas.
Abaixo o fonte zCriaTab.
//Bibliotecas #Include "Protheus.ch" //Constantes usadas na criação dos campos #Define _X3_USADO "€€€€€€€€€€€€€€ " #Define _X3_USFILIAL "€€€€€€€€€€€€€€€" #Define _X3_RESERV "þA" #Define _X3_OBRIGA "€" #Define _X3_NAO_OBRIGA "" /*/{Protheus.doc} zCriaTab Função para criação das tabelas e/ou campos @author Atilio @since 06/08/2015 @version 1.0 @param aSX2, Array, Dados do Dicionário @param aSX3, Array, Dados dos Campos @param aSIX, Array, Dados da SIX @example u_zCriaTab() @obs Abaixo a estrutura dos arrays: SX2: [01] - Chave [02] - Descrição [03] - Modo [04] - Modo Un. [05] - Modo Emp. SX3: [nLinha][01] - Campo [nLinha][02] - Filial? [nLinha][03] - Tamanho [nLinha][04] - Decimais [nLinha][05] - Tipo [nLinha][06] - Título [nLinha][07] - Descrição [nLinha][08] - Máscara [nLinha][09] - Nível [nLinha][10] - Vld.User [nLinha][11] - Usado? [nLinha][12] - Ini.Padr. [nLinha][13] - Cons.F3 [nLinha][14] - Visual [nLinha][15] - Contexto [nLinha][16] - Browse [nLinha][17] - Obrigatório? [nLinha][18] - Lista Opções [nLinha][19] - Modo de Edição [nLinha][20] - Ini Browse [nLinha][21] - Pasta SIX: [nLinha][01] - Índice [nLinha][02] - Ordem [nLinha][03] - Chave [nLinha][04] - Descrição [nLinha][05] - Propriedade [nLinha][06] - NickName [nLinha][07] - Mostr.Pesq /*/ User Function zCriaTab(aSX2, aSX3, aSIX) Local aArea := GetArea() Local aAreaX2 := SX2->(GetArea()) Local aAreaX3 := SX3->(GetArea()) Local aAreaIX := SIX->(GetArea()) Local cTabAux := aSX2[1] Local lTabCriada := .F. Local lTemAltera := .F. Local cMsgAux := "" Local nAtual := 0 Local cOrdemAux := "" //Setando os índices SX2->(dbSetOrder(1)) // X2_CHAVE SX3->(dbSetOrder(2)) // X3_CAMPO SIX->(dbSetOrder(1)) // INDICE+ORDEM //Se não conseguir posicionar na tabela, irá criá-la SX2->(DbSetOrder(1)) If !SX2->(DbSeek(cTabAux)) RecLock("SX2", .T.) SX2->X2_CHAVE := cTabAux SX2->X2_PATH := "\data\" SX2->X2_ARQUIVO := cTabAux+SM0->M0_CODIGO+"0" SX2->X2_NOME := aSX2[2] SX2->X2_NOMESPA := aSX2[2] SX2->X2_NOMEENG := aSX2[2] SX2->X2_ROTINA := "" SX2->X2_MODO := aSX2[3] SX2->X2_MODOUN := aSX2[4] SX2->X2_MODOEMP := aSX2[5] SX2->X2_DELET := 0 SX2->X2_TTS := "" SX2->X2_UNICO := "" SX2->X2_PYME := "" SX2->X2_MODULO := 0 SX2->(MsUnlock()) lTabCriada := .T. Else lTabCriada := .T. EndIf //Se a tabela tiver sido criada If lTabCriada //Percorrendo os campos For nAtual := 1 To Len(aSX3) If !SX3->(DbSeek(aSX3[nAtual][01])) fProxSX3(cTabAux, @cOrdemAux) //Se for campo de filial, trata de forma diferente If aSX3[nAtual][02] RecLock("SX3", .T.) SX3->X3_ARQUIVO := cTabAux SX3->X3_ORDEM := cOrdemAux SX3->X3_CAMPO := aSX3[nAtual][01] SX3->X3_TIPO := aSX3[nAtual][05] SX3->X3_TAMANHO := aSX3[nAtual][03] SX3->X3_DECIMAL := aSX3[nAtual][04] SX3->X3_TITULO := aSX3[nAtual][06] SX3->X3_TITSPA := aSX3[nAtual][06] SX3->X3_TITENG := aSX3[nAtual][06] SX3->X3_DESCRIC := aSX3[nAtual][07] SX3->X3_DESCSPA := aSX3[nAtual][07] SX3->X3_DESCENG := aSX3[nAtual][07] SX3->X3_PICTURE := aSX3[nAtual][08] SX3->X3_USADO := _X3_USFILIAL SX3->X3_RESERV := "€€" SX3->X3_GRPSXG := "033" SX3->X3_PYME := "S" SX3->X3_IDXSRV := "N" SX3->X3_ORTOGRA := "N" SX3->X3_IDXFLD := "N" SX3->X3_BROWSE := "N" SX3->X3_NIVEL := aSX3[nAtual][09] SX3->(MsUnlock()) //Senão cria o campo Else RecLock("SX3", .T.) SX3->X3_ARQUIVO := cTabAux SX3->X3_ORDEM := cOrdemAux SX3->X3_CAMPO := aSX3[nAtual][01] SX3->X3_TIPO := aSX3[nAtual][05] SX3->X3_TAMANHO := aSX3[nAtual][03] SX3->X3_DECIMAL := aSX3[nAtual][04] SX3->X3_TITULO := aSX3[nAtual][06] SX3->X3_TITSPA := aSX3[nAtual][06] SX3->X3_TITENG := aSX3[nAtual][06] SX3->X3_DESCRIC := aSX3[nAtual][07] SX3->X3_DESCSPA := aSX3[nAtual][07] SX3->X3_DESCENG := aSX3[nAtual][07] SX3->X3_PICTURE := aSX3[nAtual][08] SX3->X3_VLDUSER := aSX3[nAtual][10] SX3->X3_VALID := "" SX3->X3_USADO := Iif(aSX3[nAtual][11], _X3_USADO, _X3_USFILIAL) SX3->X3_RELACAO := aSX3[nAtual][12] SX3->X3_F3 := aSX3[nAtual][13] SX3->X3_NIVEL := aSX3[nAtual][09] SX3->X3_RESERV := _X3_RESERV SX3->X3_CHECK := "" SX3->X3_TRIGGER := "" SX3->X3_PROPRI := "U" SX3->X3_VISUAL := aSX3[nAtual][14] SX3->X3_CONTEXT := aSX3[nAtual][15] SX3->X3_BROWSE := aSX3[nAtual][16] SX3->X3_OBRIGAT := Iif(aSX3[nAtual][17], _X3_OBRIGA, _X3_NAO_OBRIGA) SX3->X3_CBOX := aSX3[nAtual][18] SX3->X3_CBOXSPA := aSX3[nAtual][18] SX3->X3_CBOXENG := aSX3[nAtual][18] SX3->X3_PICTVAR := "" SX3->X3_WHEN := aSX3[nAtual][19] SX3->X3_INIBRW := aSX3[nAtual][20] SX3->X3_GRPSXG := "" SX3->X3_FOLDER := aSX3[nAtual][21] SX3->X3_PYME := "S" SX3->X3_CONDSQL := "" SX3->X3_IDXSRV := "N" SX3->X3_ORTOGRA := "N" SX3->X3_IDXFLD := "N" SX3->X3_TELA := "" SX3->(msUnlock()) EndIf lTemAltera := .T. EndIf Next //Percorrendo os índices For nAtual := 1 To Len(aSIX) //Se não conseguir posicionar, quer dizer que não existe o índice, logo será criado If ! SIX->(DbSeek(aSIX[nAtual][1] + aSIX[nAtual][2])) RecLock("SIX", .T.) SIX->INDICE := aSIX[nAtual][1] SIX->ORDEM := aSIX[nAtual][2] SIX->CHAVE := aSIX[nAtual][3] SIX->DESCRICAO := aSIX[nAtual][4] SIX->DESCSPA := aSIX[nAtual][4] SIX->DESCENG := aSIX[nAtual][4] SIX->PROPRI := aSIX[nAtual][5] SIX->F3 := "" SIX->NICKNAME := aSIX[nAtual][6] SIX->SHOWPESQ := aSIX[nAtual][7] SIX->(MsUnlock()) lTemAltera := .T. EndIf Next //Se tiver alterações em campo e/ou índices if lTemAltera //Bloqueia alterações no Dicionário __SetX31Mode(.F.) //Se a tabela tiver aberta nessa seção, fecha If Select(cTabAux) > 0 (cTabAux)->(DbCloseArea()) EndIf //Atualiza o Dicionário X31UpdTable(cTabAux) //Se houve Erro na Rotina If __GetX31Error() cMsgAux := "Houveram erros na atualização da tabela "+cTabAux+":"+Chr(13)+Chr(10) cMsgAux += __GetX31Trace() Aviso('Atenção', cMsgAux, {'OK'}, 03) EndIf //Abrindo a tabela para criar dados no sql DbSelectArea(cTabAux) //Desbloqueando alterações no dicionário __SetX31Mode(.T.) endif EndIf RestArea(aAreaIX) RestArea(aAreaX3) RestArea(aAreaX2) RestArea(aArea) Return /*---------------------------------------------------------------------* | Func: fProxSX3 | | Autor: Daniel Atilio | | Data: 06/08/2015 | | Desc: Função que pega a próxima sequencia da SX3 | *---------------------------------------------------------------------*/ Static Function fProxSX3(cTabela, cOrdem) Local aArea := GetArea() Local aAreaX3 := SX3->(GetArea()) Default cOrdem := "" //Se não vir ordem, irá percorrer a SX3 para encontrar a ordem atual If Empty(cOrdem) SX3->(DBSetOrder(1)) //TABELA //Se conseguir posicionar na tabela If SX3->(DBSeek(cTabela)) //Enquanto houver registros e for a mesma tabela While !SX3->(EoF()) .AND. SX3->X3_ARQUIVO == cTabela cOrdem := SX3->X3_ORDEM SX3->(DBSkip()) EndDo Else cOrdem := "00" EndIf cOrdem := Soma1(cOrdem) //Senão, irá somar 1, pois a tabela não tem nenhuma ordem Else cOrdem := Soma1(cOrdem) EndIf RestArea(aAreaX3) RestArea(aArea) Return /*/{Protheus.doc} zExistSIX Função que verifica se o indice já existe, setando a última sequencia disponível @author Atilio @since 31/08/2015 @version 1.0 @param cTabela, Caracter, Tabela buscada @param cNickName, Caracter, NickName do índice buscado @param cSequen, Caracter, Última sequencia disponível dos índices @return lExist, Retorna se o índice já existe ou não @example u_zExistSIX('SB1', 'CAMPO', @cOrdem) /*/ User Function zExistSIX(cTabela, cNickName, cSequen) Local aAreaSIX := SIX->(GetArea()) Local lExist := .F. Local cSequen := "1" SIX->(DbSetOrder(1)) //Indice + Ordem SIX->(DbGoTop()) //Se conseguir posicionar na tabela If SIX->(DbSeek(cTabela)) //Enquanto não for fim da tabela e for o mesmo índice While ! SIX->(EoF()) .And. SIX->INDICE == cTabela //Se tiver o mesmo apelido, já existe o índice If Alltrim(SIX->NICKNAME) == Alltrim(cNickName) lExist := .T. EndIf cSequen := SIX->ORDEM SIX->(DbSkip()) EndDo cSequen := Soma1(cSequen) EndIf RestArea(aAreaSIX) Return lExist
Depois se quiser, basta conferir na sua base de dados, se os campos estão criados.
Obs.: Ressalto que, para criação de novos campos em tabelas já existentes (como foi o caso da SB1), a tabela não pode estar sendo utilizada, senão, na SX3 (campo) ou SIX (índice), estará criada a informação, mas não no banco, portanto não rode se a tabela estiver sendo usada.
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Muito, mas muito bom mesmo. Obrigado por compartilhar 🙂
A gente já utilizava as funções do Forastieri mas, ficou bem simples a forma como você abordou e cedeu os fontes.
Obrigado!
Leonardo
http://www.userfunction.com.br
Boa noite Leonardo.
Rapaz, eu que agradeço muito.
Um grande abraço.
Bom dia não estou encontrando a função zTstTab poderia me enviar?
Boa noite Everton, tudo bem?
Esta no meu GitHub, o nome do prw é zTstDicio.prw, e o link direto é:
https://github.com/dan-atilio/AdvPL/blob/master/Projetos/Dicion%C3%A1rio%20de%20Dados/zTstDicio.prw
Um grande abraço.
Mas o que está lá é de outras tabelas?
Hã?
Não entendi a dúvida Claudio.
Fico no aguardo.
Parabéns por compartilhar o seu conhecimento, showww
Você teria um exemplo para criação somente para SX6 ?
Excelente, ajudou bastante, parabéns!
Eu tenho uma dúvida:
Quando cria uma rotina em MVC que lê esta tabela criada, os campos obrigatórios não aparecem como obrigatórios. Para correção eu entro no configurar em vou campo a campo alterando (não fazendo nenhuma alteração) e salvando. Já observei que após este processo manual a SX3 continua igual antes! Será que existe alguma outra tabela para estes tratamentos?
Bom dia José, obrigado pelo feedback.
Então, se o dicionário tiver no banco, pode ser que o X3_OBRIGAT seja diferente.
Para isso, entre via APSDU e compare o conteúdo de um campo criado pelo SIGACFG e de um campo criado pela rotina customizada.
Gostaria de saber, se consigo criar uma tabela do zero + campos que compõe essa tabela com esse codigo ? se sim, como ficaria um exemplo.
Exemplo, tenho uma tabela para criar que chama ZXX e ‘N’ campos para criar nessa tabela, como poderia aproveitar esse codigo ?
Boa noite Renan, tudo joia?
Então, no caso esse artigo é de 2015, apenas tome alguns cuidados, pois a rotina X31UpdTable vai ser descontinuada (na 2210 já é exibido um aviso ao utilizá-la).
Quanto ao exemplo, segue um link – https://github.com/dan-atilio/AdvPL/blob/master/Projetos/Dicion%C3%A1rio%20de%20Dados/zTstDicio.prw
Veja a função zTstTab.
Um grande abraço.
Bacana deu certo! Sabe dizer se tem alguma função que substitui o X131UpdTABLE?
Opa, por nada. Nós que agradecemos o comentário.
Infelizmente não sabemos dizer se há outra função que substituiu.
Abraços.
Danilo, boa tarde!
Espero que esteja bem.
Aparentemente essa funcionalidade parou de funcionar na versão 2310 do Protheus. Sabe dizer se isso é verdade? Estou tentando usar os mesmos fontes disponibilizados, mas não estou conseguindo criar a tabela conforme instruido.
Bom dia Julio, tudo joia?
Primeiramente, meu nome é Daniel, não Danilo rs…
Então essa função é bem antiga (meados de 2015), e ela usa um recurso que já estava vindo a ser descontinuado que é o X31UpdTable. Na 2310 não fizemos nenhum teste ainda, mas na 2210 ele ainda funciona, porém mostra um aviso dizendo que a função X31UpdTable vai ser descontinuada.
Um grande abraço.
Daniel, bom dia!
Eu rodei o fonte em questão, ele chegou a incluir os dados da tabela na SIX, SX2, SX3, mas não criou a tabela no banco.
Como proceder? Tem alguma forma de “forçar” a criação dessa tabela?
Bom dia, tudo joia?
Para forçar, você pode rodar um DbSelectArea no alias ou um CheckFile.
Segue exemplo do CheckFile: https://terminaldeinformacao.com/2023/10/26/criando-tabelas-com-a-funcao-checkfile-maratona-advpl-e-tl-079/
Um grande abraço.