Criando tabelas, campos e índices “a quente” no Protheus

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.

Obs.: Pessoal, um aviso importante, essa função é de meados de 2015, quando ainda DBF/CTREE. Atualmente não é recomendado atualizar tabelas do dicionário via código fonte. Essa função ela foi descontinuada, então se forem utilizar, utilizem como forma de estudo apenas.

//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 := ""

    Alert("Cuidado, função descontinuada!")
     
    //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.

Verificando campos criados

Verificando campos 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.

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.

17 Responses

  1. Leonardo disse:

    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

  2. Everton disse:

    Bom dia não estou encontrando a função zTstTab poderia me enviar?

  3. Claudio Fernando Gaspar disse:

    Mas o que está lá é de outras tabelas?

  4. Douglas disse:

    Parabéns por compartilhar o seu conhecimento, showww
    Você teria um exemplo para criação somente para SX6 ?

  5. José Evaristo disse:

    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?

  6. renan freitas de souza disse:

    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 ?

  7. renan freitas de souza disse:

    Bacana deu certo! Sabe dizer se tem alguma função que substitui o X131UpdTABLE?

  8. Julio Rocha disse:

    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.

  9. ti1b4799b79103b disse:

    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?

Deixe uma resposta

Terminal de Informação