Pragiva — Tecnologia na prática

Campo real ou virtual: o contexto decide se ele existe no banco

Campo real ou virtual no Protheus: X3_CONTEXT decide se ele vira coluna na tabela ou só existe na tela. Como montar um virtual que busca de outra tabela.

Protheus Referência técnica 5 min de leitura

Assista o vídeo

Resposta rápida

Onde fica
coluna X3_CONTEXT da SX3 — R para real, V para virtual.
Real
vira coluna na tabela física; o valor é gravado e o SQL enxerga.
Virtual
não vira coluna; o valor é montado na tela, em geral pelo inicializador (X3_RELACAO).
Armadilha
campo virtual não entra em SELECT, relatório por SQL nem integração que lê o banco.

Real quando o dado nasce ali; virtual quando o dado já existe em outra tabela e só precisa aparecer.

Consulta rápida5 seções

A pergunta que o contexto responde é uma só: esse campo vai existir na tabela do banco? Se sim, é real. Se ele só precisa aparecer na tela — mostrando uma informação que já mora em outro lugar —, é virtual.

O que cada contexto grava

Real (R)Virtual (V)
Coluna na tabela físicaSimNão
Valor gravado no registroSimNunca
Aparece em SELECTSimNão — a coluna não existe
De onde vem o valor na telaDo registro gravado (ou do inicializador, na inclusão)Da expressão do inicializador, a cada exibição
Uso típicoDado que nasce no cadastroDescrição, nome ou saldo buscado de outra tabela

Numa base Protheus 12 conferida para este artigo, os campos das tabelas , , e batem exatamente com essa tabela: todo campo com X3_CONTEXT = 'V' está ausente da tabela física, e todo campo real está presente.

Como o padrão monta um campo virtual

Os virtuais do dicionário padrão seguem um desenho repetido. Três exemplos reais:

CampoO que mostraX3_RELACAO
Descrição do grupo, da tabela genérica XJ da SX5IIF(!INCLUI,POSICIONE("SX5",1,XFILIAL("SX5")+"XJ"+SB1->B1_GRPTI,"X5_DESCRI"),"")
Nome do motorista do pedido, de outra tabelaIF(!INCLUI,Posicione("DHB",1,xFilial("DHB")+SC5->C5_CODMOT,"DHB_NOMMOT"),"")
Saldo a liberar do item, calculado por funçãoMa440SaLib()

Três leituras que valem para qualquer virtual seu:

  • Quem alimenta o campo é o inicializador padrão (X3_RELACAO). No virtual, ele é a fonte do dado. Quando e como ele roda está em Inicializador padrão e de browse.
  • Posicione(alias, ordem, chave, campo) é a busca típica: abre a tabela de origem pelo índice indicado, procura a chave e devolve o campo pedido.
  • O !INCLUI não é enfeite. Na inclusão o registro ainda não existe, e SC5->C5_CODMOT aponta para o que estiver posicionado na tabela — não para o pedido que está sendo digitado. Por isso o padrão só busca fora da inclusão e, na inclusão, devolve vazio.

Os três vêm com X3_VISUAL = 'V': o usuário vê, mas não digita. Para um virtual de consulta, é o certo — nada do que ele digitasse iria para a tabela.

Exemplo: o nome do motorista no pedido de venda

No exemplo da série, uma transportadora cadastra motoristas numa tabela customizada ZA1 e informa o motorista do pedido de venda num campo customizado real, C5_XMOTOR (C 6, com consulta padrão ZA1). O código fica gravado no pedido. O nome não precisa ser gravado de novo: ele já está em ZA1_NOME.

Então o nome entra como virtual, com o mesmo recurso do C5_NOMMOT do padrão (campo virtual com Posicione no inicializador). O tamanho segue o do ZA1_NOME:

ItemValor
CampoC5_XNOMMOT
Tipo / tamanhoC 40 (o mesmo de ZA1_NOME)
Contexto (X3_CONTEXT)Virtual
Visual (X3_VISUAL)Visualizar
Inicializador padrão (X3_RELACAO)IIF(!INCLUI,Posicione("ZA1",1,xFilial("ZA1")+SC5->C5_XMOTOR,"ZA1_NOME"),"")

O índice 1 da ZA1 é ZA1_FILIAL+ZA1_COD, por isso a chave é xFilial("ZA1") seguido do código. xFilial(cAlias, cFil) devolve a filial certa para aquela tabela: sem o segundo parâmetro, usa a filial corrente; em tabela compartilhada, como a ZA1, devolve brancos.

A documentação TOTVS sobre pesquisa por campo virtual no browse faz uma exigência para o Posicione do virtual: xFilial só com o primeiro parâmetro, e nada de FwXFilial. Fora disso, o campo continua mostrando o conteúdo, mas não entra na pesquisa do browse.

Isso cobre alteração e visualização. Na inclusão, o nome precisa aparecer quando o usuário digita o código — e aí quem preenche é um gatilho de C5_XMOTOR para C5_XNOMMOT, que posiciona na ZA1 e devolve ZA1->ZA1_NOME. O padrão usa esse par em outros cadastros: no de clientes, o é virtual, com Posicione no inicializador para o registro existente, e um gatilho do o preenche durante a digitação. O C5_NOMMOT tem só uma metade do par, o inicializador; não há gatilho para ele no dicionário padrão. A montagem do gatilho está em Gatilhos no Protheus.

Se você renomear o motorista na ZA1, todo pedido passa a mostrar o nome novo na próxima abertura. Com um campo real, cada pedido guardaria o nome da época — e alguém teria que escrever a rotina que atualiza os antigos.

Real ou virtual: como decidir

Use real quando:

  • o dado nasce naquele registro (número, data, quantidade, observação);
  • relatório, query, BI ou integração precisam ler o valor no banco;
  • o valor precisa ser o da época — o nome do motorista no dia da viagem, por exemplo, e não o nome atual.

Use virtual quando:

  • o dado já existe em outra tabela e só precisa aparecer ao lado do código;
  • o valor é calculado e mudaria sozinho (saldo, situação);
  • gravar a cópia criaria a obrigação de mantê-la sincronizada.

Conferindo no banco

Os campos virtuais de uma tabela, lado a lado com a coluna física (que não existe para eles). Os exemplos usam o sufixo 010; o sufixo muda conforme a empresa do seu ambiente. A consulta usa INFORMATION_SCHEMA.COLUMNS como no SQL Server; em outro banco, troque pela visão de colunas equivalente.

Confira no seu ambienteSQL
SELECT X3.X3_CAMPO, X3.X3_CONTEXT, X3.X3_RELACAO, C.COLUMN_NAME
  FROM SX3010 X3
  LEFT JOIN INFORMATION_SCHEMA.COLUMNS C
    ON C.TABLE_NAME = 'SC5010'
   AND C.COLUMN_NAME = RTRIM(X3.X3_CAMPO)
 WHERE X3.X3_ARQUIVO = 'SC5'
   AND X3.X3_CONTEXT = 'V'
   AND X3.D_E_L_E_T_ = ' '
 ORDER BY X3.X3_ORDEM

E a prova mais direta: um SELECT C5_NOMMOT FROM SC5010 falha com coluna inválida. Para levar a informação de um virtual para um relatório por SQL, faça na query o que o inicializador faz na tela — um JOIN com a tabela de origem.

Se você está decidindo o contexto de campos de uma customização grande, fale com a gente.

Referências

  1. TDNtdn.totvs.com

    TDN — SX3, Campos das tabelas
  2. TDNtdn.totvs.com

    TDN — Posicione com Campo Virtual

Continue por aqui