The visualizer runs in the tab
No account, no upload, no server. The XML is read in the page, converted on this thread, and forgotten when the tab closes. The page’s own test fails on any request that is not one of its static assets.
For the team that keeps Oracle Forms running
Export a form from Forms Builder, drop the XML in the page, and see the screen your users have worked in for years: the same window, the same prompts, the same grid, the same keys. Nothing is uploaded, and nobody is retrained.
Three screens, converted as they are
Each screen below was read from a real Forms export and drawn by the engine the product ships: every field where the canvas put it, at the window’s own size, with generated sample rows and nothing of any customer’s data.



What your users keep
Retraining is the cost nobody budgets. The screen is drawn at the size it has always been, with every prompt where Forms wrote it. A grid shows the columns Forms repeated, one line per record, with the current record marked at the left and a scroll bar when there are more records than lines. Forms’ own list icon opens the list. And the keyboard does what it did.
For the IT administrator
No account, no upload, no server. The XML is read in the page, converted on this thread, and forgotten when the tab closes. The page’s own test fails on any request that is not one of its static assets.
A connected account reads and writes your existing database through one pooled connection. Nothing moves and nothing is copied. Forms keeps running beside it, and your users switch over one screen at a time.
Every rule runs in our engine, as a clause with a test that reads its expectation from the trigger it came from. What the converter cannot express is listed with its source text, never dropped, and never run blind.
One trigger, before and after
This is the division check on the ledger’s voucher header, exactly as the converter reads it: six clauses, every one verified against the PL/SQL, the two global writes included. A statement it cannot read is kept as text and listed.
If :TH_Divn_Code Is Not Null Then
SELECT count(*) into v_count
FROM M_Company, M_Division
WHERE Comp_code = Divn_Comp_code
AND Divn_Comp_Code = :Global.Company
AND Divn_Code = :TH_Divn_Code;
IF v_count = 0 then
message ('Invalid Division code');
raise form_trigger_failure;
End IF;
SELECT Divn_Name, Divn_Status, Comp_Status
INTO :Divn_Name, v_Divn_Status, v_Comp_Status
FROM M_Company, M_Division
WHERE Comp_code = Divn_Comp_code
AND Divn_Comp_Code = :Global.Company
AND Divn_Code = :TH_Divn_Code;
If Nvl(v_Comp_Status,'O') = 'L' Then
Show_Message('Alert1','Company Locked');
Raise Form_Trigger_Failure;
End If;
If Nvl(v_Divn_Status,'O') = 'L' Then
Show_Message('Alert1','Division Locked');
Raise Form_Trigger_Failure;
End If;
:Global.TD_Divn_Code := :TH_Divn_Code;
:Global.TD_Dept_Code := Null;
End If;on change of TH_DIVN_CODE when filled: exists in DIVISIONS else "Invalid Division code" set DIVN_NAME from DIVN_NAME deny when COMP_STATUS = 'L' else "Company Locked" deny when DIVN_STATUS = 'L' else "Division Locked" set GLOBAL.TD_DIVN_CODE to TH_DIVN_CODE set GLOBAL.TD_DEPT_CODE to null # 6 clauses. Nothing refused. # GLOBAL.* lives in the session store, one per browser tab.
Real output. A statement that does not convert is listed with its source text, never dropped in silence. The whole vocabulary is on one page.
What arrives, and what waits for a person
A form’s logic lives in its triggers and program units, and no converter reads all of it. Ours says exactly which parts it read, which it refused, and why, so the work left for your team is sized before the pilot and not in month three.
Nothing is dropped in silence. A statement the converter cannot express keeps its source text and its place in the list, and the screen it belongs to still draws. This is what that looks like, on the invoice line’s save check: one call converts, one is refused, and the refusal says why.
Declare
v_qty number(15,3);
Begin
If :InvI_Item_Code Is Not Null
And :InvI_Uom_Code Is Not Null Then
v_qty := NVL(:Invi_Qty * :Max_Loose, 0)
+ NVL(:Invi_Loose, 0)
+ NVL(:Invi_Foc_Qty * :Max_Loose, 0)
+ NVL(:Invi_Foc_Loose, 0);
Proc_Validate_Qty(v_qty); --To Prevent saving zero Qty.
VALIDATE_INVOICE;
End If;
End;on save insert, update of T_INV_ITEM when INVI_ITEM_CODE is filled and INVI_UOM_CODE is filled: call PROC_VALIDATE_QTY( nvl(INVI_QTY * MAX_LOOSE, 0) + nvl(INVI_LOOSE, 0) + nvl(INVI_FOC_QTY * MAX_LOOSE, 0) + nvl(INVI_FOC_LOOSE, 0)) # 1 statement did not convert. # VALIDATE_INVOICE; # a procedure of this form whose body cannot be read whole. # No part of it is inlined; no guard is dropped in silence.
Real output. The procedure it refused is one line in the worklist, with this reason and the original code beside it, and the invoice screen still draws. Every trigger that converts in part is on one page, with its reasons.
Deployment
The screens are the same either way. What changes is where the rows live, and whether the licence renewal is still yours to pay.
Available at launch
We read and write the database you already run. Nothing moves, nothing is copied, and Forms keeps working beside us while your users switch over one screen at a time.
The migration, designed for
Your rules already run on either database, because they are ours rather than Oracle’s. When you are ready, the data follows them and the licence stops.
Before the pilot
Every one of these is measured on the corpus. We would rather you read them here than find them in month three.
Start with one form
Export any form with frmf2xml, drop it in the visualizer and look at the screen beside the list of what did not convert. If the screen looks like yours and the list looks finishable, the pilot is one Oracle connection away.