33` module.xml ` -- um módulo Forms2XML 100% sintético (sem dado de cliente,
44committável; nomeado ` module.xml ` como o resto de ` fixtures/corpus/ ` ,
55não ` *_fmb.xml ` , pra não cair na regra do ` .gitignore ` que barra exports
6- reais de cliente) desenhado para acionar deliberadamente quase todo
7- branch relevante de ` apexlayout.py ` / ` apexlang.py ` num único módulo, e
8- servir de exemplo de referência tanto do lado Forms quanto do lado APEX.
6+ reais de cliente) desenhado para dois fins ao mesmo tempo:
7+
8+ 1 . ** Bancada do FormsLang** -- acionar deliberadamente quase todo branch
9+ relevante de ` apexlayout.py ` / ` apexlang.py ` (layout geométrico, export
10+ APEXlang) e todas as faixas de veredito de ` rules.py ` (AUTO / DROP /
11+ ASSISTED / MANUAL / UNKNOWN) num único módulo.
12+ 2 . ** Demo ao vivo** -- converter no Forms Builder 14c, compilar e RODAR de
13+ verdade contra o schema ` FORMSLANG ` criado por ` demo_schema.sql ` , pra
14+ mostrar o pipeline inteiro Forms -> ` assess ` -> APEX com um form que
15+ tem cara de sistema legado de verdade (toolbar, abas, master-detail,
16+ LOVs, auditoria, janela modal, relatório, exportação).
917
1018Diferente do golden corpus (` fixtures/corpus/ ` , focado em ` rules.py ` /
11- ` depgraph.py ` -- análise de trigger e grafo de dependência), esta fixture
12- mira o motor de layout geométrico e o export APEXlang: tipos de item, lado
13- do prompt, adoção de boilerplate, LOV estática, blocos tabulares/master-
14- detail, canvases (Content/Toolbar/Tab/Stacked) e itens ocultos.
19+ ` depgraph.py ` com módulos pequenos), esta fixture é propositalmente
20+ gorda: 5 blocos, 78 itens, 55 triggers, 4 program units, ~ 500 linhas de
21+ PL/SQL.
1522
1623## O que o módulo contém
1724
18- - ** Trigger de formulário** (escopo módulo).
19- - ** ` BK_PRODUTO ` ** -- bloco single-record, cabeçalho espalhado por
20- ` CV_MAIN ` + ` CV_TABS ` , com ~ 24 itens cobrindo:
25+ - ** 4 ` ModuleParameter ` ** (` P_CATEGORIA_INICIAL ` , ` P_MODO ` , ` P_USUARIO ` ,
26+ ` P_DIR_EXPORT ` ) lidos como ` :PARAMETER.X ` . ` P_MODO='CONSULTA' ` trava o
27+ form em somente-leitura no WHEN-NEW-FORM-INSTANCE.
28+ - ** 14 triggers de formulário** : PRE-FORM (GLOBALs + INSERT direto +
29+ ` FORMS_DDL('COMMIT') ` ), WHEN-NEW-FORM-INSTANCE (título dinâmico,
30+ ` CREATE_TIMER ` , modo consulta, ` EXECUTE_QUERY ` inicial), POST-COMMIT,
31+ POST-FORM (` DELETE_TIMER ` , ` ERASE ` ), WHEN-TIMER-EXPIRED (relógio na
32+ toolbar), ON-ERROR (` ERROR_CODE ` /` DBMS_ERROR_TEXT ` -> alerta), ON-MESSAGE
33+ (` MESSAGE_CODE ` ), KEY-COMMIT / KEY-EXEQRY / KEY-LISTVAL / KEY-CLRFRM /
34+ KEY-EXIT (com ` DisplayInKeyboardHelp ` ), WHEN-WINDOW-CLOSED,
35+ WHEN-TAB-PAGE-CHANGED (` GET_CANVAS_PROPERTY(TOPMOST_TAB_PAGE) ` ).
36+ - ** ` BK_PRODUTO ` ** (` TAB_PRODUTO ` ) -- bloco single-record, cabeçalho
37+ espalhado por ` CV_MAIN ` + três abas de ` CV_TABS ` , cobrindo:
2138 - todo ` item_type ` mapeado (` Text Item ` , ` Display Item ` , ` Check Box ` ,
22- ` Radio Group ` , ` List Item ` , ` Bean Area ` /textarea, ` Push Button ` );
39+ ` Radio Group ` , ` List Item ` , ` Bean Area ` /textarea);
2340 - todo lado de prompt (` Start ` →left, ` Top ` →above, ` End ` →right,
24- ` Bottom ` →below, ` Hidden ` →sem prompt, itens sem ` Prompt ` /` Label ` mas
25- com controle→` control ` );
41+ ` Bottom ` →below, ` Hidden ` →sem prompt, item sem ` Prompt ` /` Label ` );
2642 - boilerplate ` Text ` adotado como prompt (ao lado e acima) e os casos
2743 que NÃO devem ser adotados (negrito, texto longo demais);
28- - uma linha propositalmente lotada ( 8 itens) que reproduz a combinação
29- inválida ` columnSpan ` / ` labelColumnSpan ` (ver "Bug conhecido" abaixo);
44+ - a linha lotada de 8 itens que reproduz o bug ` labelColumnSpan `
45+ (ver "Bug conhecido" abaixo);
3046 - itens ocultos via ` Visible="false" ` ** e** via ausência de
31- ` CanvasName ` (as duas rotas para ` layout.hidden ` ).
32- - ** ` BK_ITENS ` ** -- bloco tabular master-detail (via ` Relation ` ) em
33- ` CV_TABS ` /` TAB_ITENS ` .
34- - ** ` CONTROL ` ** -- bloco não-database com botões, dividido entre o
35- canvas toolbar (` CV_TOOLBAR ` , Horizontal Toolbar) e um canvas
36- stacked-invisible (` CV_CONFIRMA ` , vira inline-dialog).
37- - ** ` ProgramUnit ` ** , ** ` RecordGroup ` +` LOV ` ** estática (completude do lado
38- Forms; o motor de layout não usa RecordGroup/LOV nomeada -- só gera LOV
39- estática a partir de ` List Item ` /` Radio Group ` com choices fixos),
40- ** ` VisualAttribute ` ** , ** ` Relation ` ** , ** ` Alert ` ** , 4 ` Canvas ` (ordem de
47+ ` CanvasName ` ;
48+ - propriedades Forms que o parser ainda não lê mas o compilador exige
49+ ou usa: ` PrimaryKey ` , ` FormatMask ` , ` InitializeValue ` (Radio Group e
50+ Check Box precisam -- FRM-30188 / FRM-30174 sem isso),
51+ ` CheckedValue ` /` UncheckedValue ` , ` ValidateFromList ` , ` MultiLine ` +
52+ ` WrapStyle ` , ` Tooltip ` /` Hint ` , ` LowestAllowedValue ` /` HighestAllowedValue ` ;
53+ - triggers de bloco PRE-QUERY (` DEFAULT_WHERE ` dinâmico), POST-QUERY
54+ (lookups + ` SET_ITEM_INSTANCE_PROPERTY ` com visual attribute por
55+ registro), PRE-INSERT / PRE-UPDATE / PRE-DELETE, WHEN-VALIDATE-RECORD,
56+ WHEN-CREATE-RECORD, WHEN-NEW-RECORD-INSTANCE; triggers de item
57+ WHEN-VALIDATE-ITEM (um deles chama ` PKG_PRODUTO.F_VALIDA_EAN13 ` no
58+ banco), WHEN-LIST-CHANGED, WHEN-RADIO-CHANGED, WHEN-CHECKBOX-CHANGED,
59+ WHEN-MOUSE-DOUBLECLICK (` LIST_VALUES ` e ` SHOW_EDITOR ` );
60+ - duas ` Relation ` (filhas do bloco mestre, como o forms.xsd exige):
61+ ` REL_ITENS ` (Isolated, exclusão dos filhos feita à mão no PRE-DELETE)
62+ e ` REL_AUDIT ` (Deferred + AutoQuery).
63+ - ** ` BK_ITENS ` ** (` TAB_PRODUTO_ITEM ` ) -- bloco tabular master-detail, 5
64+ linhas, scrollbar própria, ` SEQ_ITEM ` por MAX+1 no PRE-INSERT e um item
65+ de fórmula (` VL_TOTAL_LINHA ` , ` CalculateMode="Formula" ` ).
66+ - ** ` BK_AUDIT ` ** (` TAB_PRODUTO_AUDIT ` ) -- detalhe somente-leitura da
67+ trilha gravada pelo trigger de banco; ` DataType="Datetime" ` , filtro
68+ extra vindo de ` CONTROL.PC_TP_OPERACAO ` (LOV estática) no PRE-QUERY.
69+ - ** ` BK_RESUMO ` ** (` VW_PRODUTO_RESUMO ` ) -- bloco sobre view agregada (sem
70+ ROWID: ` KeyMode="Non-Updateable" ` + ` PrimaryKey ` ), duplo clique na
71+ categoria faz drill-down no mestre.
72+ - ** ` CONTROL ` ** -- bloco não-database: toolbar de 2 linhas (Salvar,
73+ Cancelar, Excluir, Consultar, Novo, Duplicar, Reajuste, Relatório,
74+ Exportar, check box "Somente ativos", Site, Calculadora, Ajuda, relógio),
75+ diálogo inline em canvas stacked (` CV_CONFIRMA ` ), janela modal de
76+ reajuste (` CV_REAJUSTE ` /` WIN_REAJUSTE ` , chama procedure do banco com
77+ parâmetro OUT) e botões dentro das abas.
78+ - ** 4 ` ProgramUnit ` ** : Procedure, Function e Package Spec + Body
79+ (` PKG_UTIL ` ).
80+ - ** 3 ` RecordGroup ` ** (Query simples, Query com WHERE, ` Static ` com
81+ ` RecordGroupColumnRow ` ) e ** 4 ` LOV ` ** -- ` RG_CATEGORIA ` é compartilhado
82+ por duas LOVs com ` ReturnItem ` diferentes.
83+ - ** 3 ` VisualAttribute ` ** , ** 4 ` Alert ` ** (Note / Stop / Caution com 3
84+ botões), ** 1 ` Editor ` ** (não existe atributo Item→Editor no forms.xsd;
85+ é aberto por ` SHOW_EDITOR ` num trigger), ** 1 ` Report ` ** (Oracle Reports,
86+ só compila -- não há report server na demo).
87+ - ** 5 ` Canvas ` ** (` CV_TOOLBAR ` Horizontal Toolbar, ` CV_MAIN ` Content com
88+ Frames/Text/Rectangle, ` CV_TABS ` Tab com 5 páginas, ` CV_CONFIRMA `
89+ Stacked invisível, ` CV_REAJUSTE ` Content da segunda janela -- a ordem de
4190 declaração é significativa: ` CV_TOOLBAR ` antes de ` CV_MAIN ` para o
42- hosting do toolbar funcionar) e 1 ` Window ` .
91+ hosting do toolbar funcionar) e ** 2 ` Window ` ** (Document + Dialog Modal).
92+
93+ ### Cobertura de vereditos (` rules.py ` )
94+
95+ ` formslang assess tests/fixtures/showcase/module.xml ` hoje reporta
96+ Triggers ` AUTO=13 DROP=1 ASSISTED=38 MANUAL=2 UNKNOWN=1 ` e Built-ins
97+ ` AUTO=60 DROP=4 ASSISTED=55 MANUAL=14 ` , tier REWRITE. Os MANUAL são
98+ propositais e cada um representa um padrão legado real:
99+ ` CREATE_TIMER ` /` DELETE_TIMER ` , ` FORMS_DDL ` , ` HOST('calc.exe') ` ,
100+ ` RUN_REPORT_OBJECT ` , ` TEXT_IO ` . As chamadas fora do catálogo
101+ (` PKG_PRODUTO.* ` , ` PKG_UTIL.* ` , ` MESSAGE_CODE ` , ` GET_CANVAS_PROPERTY ` ,
102+ ` SET_ITEM_INSTANCE_PROPERTY ` , ` ERASE ` , ` SHOW_EDITOR ` ) caem em
103+ ` unknown_calls ` de propósito -- é o que o assess deve apontar como
104+ "regra já no banco" ou "dívida de catálogo".
105+
106+ ## Rodando de verdade no Forms Builder
107+
108+ ### 1. Schema de demo (` demo_schema.sql ` )
109+
110+ Cria, de forma idempotente (dropa e recria), no schema ` FORMSLANG ` :
111+ ` TAB_CATEGORIA ` , ` TAB_FORNECEDOR ` , ` TAB_PRODUTO ` (+ ` TAB_PRODUTO_SEQ ` ),
112+ ` TAB_PRODUTO_ITEM ` , ` TAB_PRODUTO_AUDIT ` , ` TAB_FORM_ACESSO ` , a view
113+ ` VW_PRODUTO_RESUMO ` , o package ` PKG_PRODUTO ` (` F_VALIDA_EAN13 ` ,
114+ ` F_TOTAL_ITENS ` , ` F_DS_STATUS ` , ` P_APLICA_REAJUSTE ` ) e o trigger de
115+ auditoria ` TRG_TAB_PRODUTO_AUDIT ` . Semeia 4 categorias, 4 fornecedores, 6
116+ produtos com EAN-13 válidos, 6 itens e 9 linhas de auditoria (3 delas
117+ geradas por UPDATEs de exemplo: reajuste de preço, inativação, bloqueio).
118+ Não é lido pelo parser nem pelos testes.
119+
120+ ``` powershell
121+ # SQLcl thin, sem tnsnames (no Git Bash exporte MSYS_NO_PATHCONV=1 antes,
122+ # senão o /nolog vira caminho Windows e o SQLcl imprime o usage)
123+ printf '%s\n' 'connect FORMSLANG/<senha>@localhost:1521/FREEPDB1' `
124+ '@tests\fixtures\showcase\demo_schema.sql' 'exit' | sql -S -thin /nolog
125+ ```
126+
127+ O script termina com SELECTs de conferência (` user_objects ` tudo VALID,
128+ contagens por tabela, ` user_errors ` vazio).
129+
130+ ### 2. Converter e compilar
43131
44- ## Como validar
132+ Pelo Forms Builder: ** File > Convert...** (XML to Forms Module, "Overwrite
133+ existing" marcado) apontando pro ` module.xml ` ; abrir o ` .fmb ` , conectar
134+ como ` FORMSLANG ` e ** Compile All** (Ctrl+Shift+K); depois ** Run Form** .
135+
136+ Por linha de comando (Forms 14.1.2, Home ` C:\Oracle\FR1412 ` -- o antigo
137+ ` C:\Oracle\Middleware ` foi removido em 2026-09-04, era um Home legado
138+ paralelo):
139+
140+ ``` powershell
141+ # XML -> FMB (não precisa de banco; avisa que não validou os Record Groups).
142+ # frmxml2f.bat só acha java/jars com ORACLE_HOME setado.
143+ $env:ORACLE_HOME = "C:\Oracle\FR1412"
144+ C:\Oracle\FR1412\forms\templates\scripts\frmxml2f.bat module.xml OVERWRITE=YES
145+
146+ # FMB -> FMX (precisa de banco). NLS_LANG explícito é obrigatório numa
147+ # instalação cujo registro tem NLS_LANG malformado: sem isso o logon
148+ # falha com ORA-12705 SILENCIOSO (exit 1, sem module.err). Nunca passe
149+ # help=yes -- abre um diálogo e trava o batch.
150+ # frmcmp.exe é executável GUI (PE subsystem 2): o PowerShell NÃO espera
151+ # ele terminar quando chamado com `&` -- volta em ~6ms, $LASTEXITCODE
152+ # vazio, e module.fmx/module.err só aparecem uns 20s depois. Use
153+ # Start-Process -Wait (ou pipe). Não precisa de ORACLE_HOME nem de
154+ # ORACLE_HOME\bin no PATH (testado).
155+ $env:NLS_LANG = "AMERICAN_AMERICA.WE8MSWIN1252"
156+ $env:TNS_ADMIN = "C:\Oracle\FR1412\user_projects\domains\forms_domain\config\fmwconfig"
157+ $p = Start-Process -FilePath "C:\Oracle\FR1412\bin\frmcmp.exe" `
158+ -ArgumentList "module=module.fmb", "userid=FORMSLANG/<senha>@FREEPDB1", `
159+ "logon=yes", "batch=yes", "compile_all=yes", "window_state=minimize" `
160+ -WorkingDirectory (Get-Location) -NoNewWindow -Wait -PassThru
161+ $p.ExitCode # 0
162+ Get-Content module.err # 61 unidades, todas "No compilation errors."
163+ ```
164+
165+ ### 3. Roteiro de teste manual (o que a demo mostra)
166+
167+ 1 . Abrir: título da janela ganha o usuário, toolbar mostra o relógio, o
168+ bloco já vem consultado (6 produtos, ordenado por nome). Uma linha em
169+ ` TAB_FORM_ACESSO ` foi gravada pelo PRE-FORM.
170+ 2 . Navegar (Page Down): "Parafusadeira" aparece com o nome em vermelho
171+ (` VA_BLOQUEADO ` , ` TP_STATUS='BLOQUEADO' ` ). Botão Excluir desabilita em
172+ registro novo (WHEN-NEW-RECORD-INSTANCE).
173+ 3 . Aba ** Itens** : Furadeira totaliza ` 134,90 ` , Notebook ` 209,80 ` , Camiseta e
174+ Parafusadeira ` 0,00 ` ; a coluna Total de cada linha é a fórmula.
175+ 4 . Aba ** Comercial** : fornecedor via LOV (F9 / duplo clique) -- só
176+ homologados aparecem; "Textil Delta" não. Datas/usuário de alteração
177+ são somente-leitura.
178+ 5 . Aba ** Auditoria** : histórico do produto; filtrar por ` UPDATE ` com a LOV
179+ estática + botão Filtrar.
180+ 6 . Aba ** Resumo** : 4 categorias com contagens e valor de estoque; duplo
181+ clique numa categoria reconsulta a tela principal filtrada.
182+ 7 . Toolbar ** Reajuste** : abre a janela modal, escolher categoria (LOV que
183+ reusa ` RG_CATEGORIA ` ), informar 10, Aplicar -> alerta com a quantidade;
184+ a aba Auditoria ganha linhas "Preco alterado de X para Y".
185+ 8 . ** Novo** / ** Duplicar** / ** Salvar** (F10): defaults do WHEN-CREATE-RECORD;
186+ ` PK_ID ` vem da sequence no PRE-INSERT; ` CD_BARRA ` inválido é barrado
187+ pelo ` F_VALIDA_EAN13 ` do banco; peso líquido > bruto é barrado pelo
188+ WHEN-VALIDATE-RECORD.
189+ 9 . ** Excluir** : diálogo inline (canvas stacked) -> Confirmar apaga os itens
190+ (PRE-DELETE) e o produto, alerta de sucesso.
191+ 10 . ** Exportar** : gera ` produto_<id>.csv ` em ` P_DIR_EXPORT ` (TEXT_IO).
192+ ** Site** abre b2dev.tech, ** Calculadora** chama ` HOST ` , ** Ajuda** mostra
193+ usuário e nº de consultas da sessão (` :GLOBAL ` ).
194+ 11 . Sair (Ctrl+Q / X) com alteração pendente: alerta de 3 botões.
195+ 12 . ** Relatório** só funciona com um Oracle Reports server configurado
196+ (` rep_server ` ); sem ele, mostra o status devolvido.
197+
198+ ## Como validar (lado FormsLang)
45199
46200Não existe ainda um teste automatizado pytest para esta fixture (é usada
47201via script ad hoc). Rotina manual:
@@ -52,6 +206,7 @@ from formslang.apexlayout import build_layout
52206from formslang.apexlang import export_apexlang
53207from formslang.store import Store
54208from formslang.convert import build_tasks
209+ from formslang.apeximport import run_import
55210
56211module = parse_xml(" tests/fixtures/showcase/module.xml" )
57212layout = build_layout(module, 1 ) # inspeciona a árvore de regiões
@@ -61,11 +216,28 @@ store.init_session(module.name, "tests/fixtures/showcase/module.xml")
61216store.add_tasks(build_tasks(module))
62217result = export_apexlang(store, module, " /tmp/out" ,
63218 {" app_id" : 101 , " name" : " DEMO_ALL_ELEMENTS" , " alias" : " demo-all-elements" })
219+ run_import(result.zip_path, connect_string = " localhost:1521/FREEPDB1" ,
220+ username = " FORMSLANG" , password = " ..." , validate_only = True ) # apex validate
64221```
65222
66- Depois, ` apex validate ` (SQLcl) no zip exportado confirma que o APEXlang
67- gerado é sintaticamente válido -- mas ** não** pega erros de renderização
68- de página (ver abaixo).
223+ Estado atual: 22 regiões, 78 itens mapeados, 0 pulados, 2 LOVs estáticas
224+ (` TP_UNIDADE ` , ` TP_STATUS ` ), ` apex validate ` "Validation successful."
225+ Também vale conferir o XML contra o ` forms.xsd ` oficial (lxml
226+ ` XMLSchema.validate ` ) -- nenhum atributo aqui foi inventado; todos
227+ existem no xsd do Forms 14c.
228+
229+ ### Limitações conhecidas do mapeamento (de propósito, pra demo ser honesta)
230+
231+ - ` MultiLine ` /` WrapStyle ` de ` OBS_GERAL ` não são lidos pelo parser -- vira
232+ ` textField ` no APEX; só ` Bean Area ` vira ` textarea ` (convenção
233+ documentada em ` test_apexlang.py ` ).
234+ - ` WIN_REAJUSTE ` (janela modal) vira uma região comum na mesma página, não
235+ uma página modal separada.
236+ - ` CalculateMode="Formula" ` de ` VL_TOTAL_LINHA ` vira ` Display Item ` sem
237+ cálculo -- a fórmula fica registrada como dívida no assess.
238+ - ` Report ` , ` Editor ` , ` ModuleParameter ` e os ` RecordGroup ` /` LOV ` nomeados
239+ são contados pelo parser, mas o motor de layout não os consome (a LOV
240+ do APEX é gerada só a partir de ` List Item ` /` Radio Group ` ).
69241
70242## Bug conhecido, reproduzido aqui de propósito -- CORRIGIDO
71243
0 commit comments