FormsRefresh

Home / Demo

CareWell clinic

Five Forms modules with a menu module and an attached PL/SQL library: sign-in with a list of values, a home page, patients, appointments (a list item, a double-booking check) and billing (invoices with lines and payments, an invoice that cannot be deleted).

Open the live migrated app Same app, Angular front end Scenarios and diffs Faithful to the original

Target: .NET 10 and PostgreSQL, the forms, the menu and the library's shared state in the browser. Original Forms source: github.com/devvinish/oracle-forms-book-code at commit 8c1281a, licence: MIT, copyright 2026 Vinish Kapoor.

Demo login: Sign in as ADMIN, RECEPTION1 or BILLING1 (the original has no passwords).

The live demos share one database per app: visitors see each other's changes, and the data resets to the scenario fixture every night.

Parity: 22 of 22 scenarios

Each scenario was recorded on the original and replayed against the migrated app. Three runs from a fresh database gave byte-identical diffs for every scenario; none has a defect. Open a scenario for its steps, screenshots of both apps, the golden record, the diff and its waivers.

ScenarioWhat it doesmatchtoleratedwaiveddefectunmappedThree runs
appointments-bookBook an appointment for the first patient (Adam Ali). CTL.APPTS opens CW_APPOINTMENTS for the patient (CW_NAV.OPEN_MODULE sets CW_CTX.PATIENT_ID; LOAD_PATIENT queries). WHEN-NEW-FORM-INSTANCE fills the department list from RG_DEPTS. Records > Insert adds a row; the department list and the doctors' LOV (RG_DOCTORS, the department's active doctors) fill it. File > Save validates the record (CHECK_DOUBLE_BOOKING) and inserts it (PRE-INSERT: sequence, CHECK_DOUBLE_BOOKING again).847281100identical
appointments-cancelCancel an appointment. Choosing Cancelled fires WHEN-LIST-CHANGED: SET_REASON_REQUIRED makes REASON required for that record. With the reason emptied, File > Save stops at FRM-40202, which ON-ERROR (CW_ERR.ON_ERROR) passes on as the Forms message. With a reason the update goes through: PRE-UPDATE's CHECK_DOUBLE_BOOKING returns at once for a cancelled appointment.839271100identical
appointments-delete-refusedDelete a completed appointment that has a visit. Records > Delete removes it from the block; File > Save posts the delete, the database refuses it with ORA-02292 (VISITS_APPT_FK), and ON-ERROR (CW_ERR.ON_ERROR) shows its translation "Other records still refer to this one." Nothing is deleted.52518800identical
appointments-departmentChange an appointment's department. WHEN-LIST-CHANGED on DEPT_ID looks up the doctor's department: a doctor of another department is cleared. Changing it again, with no doctor left, skips the lookup. File > Clear then asks whether to save the changes (the Forms dialog); No discards them.62332900identical
appointments-double-bookingBook Dr Kavya Pillai at 13:40 on 26 October 2026, when she already sees the patient from 13:30 to 14:00 (appointment 51140). WHEN-VALIDATE-RECORD runs CHECK_DOUBLE_BOOKING, which finds the overlap, shows the message and raises FORM_TRIGGER_FAILURE: nothing is saved.838281100identical
appointments-invalid-durationAn appointment of 300 minutes. The form accepts it; the database refuses it (check constraint APPOINTMENTS_DUR_CK), and ON-ERROR's CW_ERR.ON_ERROR turns the error into "An appointment lasts 5 to 240 minutes." through CW_ERR.CONSTRAINT_OF.839281100identical
appointments-no-patientClinic > Appointments from the home page, before any patient was chosen: LOAD_PATIENT finds no current patient (CW_CTX.PATIENT_ID) and says "Choose a patient in Patients first."7617400identical
appointments-returnBack to the open appointments of the same patient. Clinic > Patients goes to the open CW_PATIENTS; Appointments... with the same current patient finds CW_APPOINTMENTS open and goes to it with GO_FORM. Its WHEN-FORM-NAVIGATE runs LOAD_PATIENT, which sees the patient already loaded and does nothing.42115700identical
billing-delete-refusedRecords > Delete on an invoice. The INVOICES block does not allow deletes, so Forms refuses with FRM-41049 before the master-detail check (ON-CHECK-DELETE-MASTER) could run.19056600identical
billing-patientInvoices of one patient. Appointments... in CW_PATIENTS puts the first patient into the shared CW_CTX (CW_NAV.OPEN_MODULE). Clinic > Home goes back to CW_MAIN, whose menu was set up for the administrator, and its Clinic > Invoices opens CW_BILLING: LOAD_INVOICES finds the patient in CW_CTX and shows only that patient's invoices.46527700identical
billing-payPay two invoices. The home page's Invoices button opens CW_BILLING without a patient: LOAD_INVOICES shows the invoices still to be paid. Choosing invoice 3850 (open, 75) coordinates its lines and payments (ON-CLEAR-DETAILS, ON-POPULATE-DETAILS, QUERY_MASTER_DETAILS). A payment of 30 (PAID_ON defaults to today, $$DATE$$) is saved with File > Save, which runs the form's KEY-COMMIT: PRE-INSERT takes the sequence, POST-INSERT sets the invoice PARTIAL, and the list is queried again. Invoice 3852 (partial, 42.50 of 85 paid) gets the remaining 42.50 (a click into a new row lands on its first item, PAID_ON, so AMOUNT is clicked twice): POST-INSERT sets it PAID and the new query no longer lists it.4661485400identical
billing-returnClinic > Invoices opens CW_BILLING through the menu (CW_NAV.OPEN_MODULE, OPEN_FORM). CW_BILLING's START_FORM finds no signed-in user and signs in the P_USER default, Front Desk One; the library data is shared, so Clinic > Home shows "Welcome, Front Desk One" and REFRESH_HOME disables the home page's Invoices button. CW_MAIN's menu was set up for the administrator, so Clinic > Invoices still works: it finds CW_BILLING open and goes to it with GO_FORM, and WHEN-FORM-NAVIGATE's LOAD_INVOICES sees the invoices already shown and does nothing.19254600identical
billing-unsavedLeave an invoice with an unsaved payment. Moving to another invoice fires ON-CLEAR-DETAILS: CLEAR_ALL_MASTER_DETAILS finds the changed PAYMENTS block below the master and clears it with ASK_COMMIT; Forms asks whether to save, answered No, and the next invoice's details are queried.29392800identical
login-billingSign in as BILLING1. APPLY_MENU disables Records > Delete (HAS_ROLE false for ADMIN,RECEPTION) and the home page's Invoices button stays enabled. CW_PATIENTS, opened by OPEN_FORM, does not see the sign-in: START_FORM signs in its P_USER default (title "Front Desk One (Reception)"), so its Invoices... button is disabled for the billing clerk too.1096400identical
login-failAn unknown user. USERNAME validates from its LOV (ValidateFromList), so leaving it opens LOV_USERS with every active user; Escape closes the LOV and Forms refuses the value (FRM-40212). The sign-in never reaches CW_SEC.LOGIN with an unknown name.480400identical
login-lovChoose the user from the LOV (RG_USERS, active users by full name) and sign in as RECEPTION1. For the RECEPTION role APPLY_MENU disables Clinic > Invoices and REFRESH_HOME disables the Invoices button. CW_PATIENTS, opened by OPEN_FORM, does not see the sign-in in its library data: START_FORM signs in its P_USER default (title "Front Desk One (Reception)") and HAS_ROLE disables Invoices... there.1116400identical
login-okSign in as ADMIN: CW_SEC.LOGIN finds the user, NEW_FORM opens CW_MAIN with the shared library data, CW_NAV.START_FORM applies the menu rules (APPLY_MENU: CLINIC.FEES does not exist, nothing is disabled for ADMIN) and sets the title, REFRESH_HOME fills the home page.423300identical
main-aboutHelp > About runs the menu item's code: MESSAGE with CW_SEC.FULL_NAME. Menu code has its own instance of the attached library's package data, which no sign-in filled, so the name is empty.496500identical
main-print-schedulePrint Schedule runs PRINT_SCHEDULE: a parameter list for today and RUN_REPORT_OBJECT on the report object APPT_SCHEDULE. No Reports server is configured on the host; the scenario records what the runtime does.526400identical
navigationMove between open forms. Appointments for the first patient; Clinic > Patients goes back to the open CW_PATIENTS and Clinic > Home to CW_MAIN (menu code: CW_NAV.OPEN_MODULE finds the form open and calls GO_FORM); CW_MAIN's WHEN-FORM-NAVIGATE refreshes the home page. The home page's Patients button goes to the open CW_PATIENTS (GO_FORM from form code). Appointments for the second patient goes to the open CW_APPOINTMENTS, whose WHEN-FORM-NAVIGATE (LOAD_PATIENT) loads the new patient: its EXECUTE_QUERY first asks whether to save changes, answered No; the first patient's current appointment is then validated, and CHECK_DOUBLE_BOOKING refuses it, because the seed data has Dr Maya Wilson booked twice at that time.835331200identical
patients-findSearch patients. FIND_PATIENTS builds the WHERE clause from the criteria the user filled (name, city, born from, born to), counts the hits (COUNT_QUERY, message level 25) and queries. Moving to another row fires WHEN-NEW-RECORD-INSTANCE, which shows "Patient n of total".580271100identical
patients-queryQuery by example through the menu module: Records > Enter Query, a last name in the block, Records > Execute Query. File > Exit then closes CW_PATIENTS and returns to the home page.31718900identical
total875767520900

The same app with an Angular front end: 22 of 22

The check does not depend on what the new interface is built with. This app has a second front end written in Angular on the same back end and API, and the same scenarios, mapping, tolerance policy and waivers check it: 22 of 22 scenarios at parity, 0 defects, three runs from a fresh database byte-identical, and every diff byte-identical to the default front end's.

ScenariomatchtoleratedwaiveddefectunmappedThree runsDiff as the default front end
appointments-book847281100identicalidentical
appointments-cancel839271100identicalidentical
appointments-delete-refused52518800identicalidentical
appointments-department62332900identicalidentical
appointments-double-booking838281100identicalidentical
appointments-invalid-duration839281100identicalidentical
appointments-no-patient7617400identicalidentical
appointments-return42115700identicalidentical
billing-delete-refused19056600identicalidentical
billing-patient46527700identicalidentical
billing-pay4661485400identicalidentical
billing-return19254600identicalidentical
billing-unsaved29392800identicalidentical
login-billing1096400identicalidentical
login-fail480400identicalidentical
login-lov1116400identicalidentical
login-ok423300identicalidentical
main-about496500identicalidentical
main-print-schedule526400identicalidentical
navigation835331200identicalidentical
patients-find580271100identicalidentical
patients-query31718900identicalidentical

The app is also rebuilt from native APEX components on its original Oracle schema, checked with the same scenarios: the APEX version.

Faithful to the original

The migrated app copies the original's behaviour, including what looks like a bug. These are the places a visitor will notice.

The interface is new; the behaviour is the original's, and the scenarios check it value by value. Some of that behaviour looks like a bug on a web page. It is kept on purpose, because the point of the demo is that nothing changed:

Five places where the migrated app does not copy the original are proposed as waivers: the frame title before sign-in (C1), the collected-today gauge, which the Forms client exposes no value for (C2), Help > About naming nobody (C3), the home page forgetting the user after a module exits (C4) and the focus after a refused record (C5). A sixth, C6, covers the one place the original loses data: in the billing-pay scenario the original drops a second payment the user entered without saying so; the migrated app saves it.

Coverage of the original

Every trigger and program unit of the Forms modules, measured with Forms Runtime Diagnostics while the scenarios ran; database PL/SQL measured with DBMS_PLSQL_CODE_COVERAGE.

ModuleTriggersProgram unitsCoveredUnreachableBlocked
CW_APPOINTMENTS931200
CW_BILLING1041310
CW_LOGIN10100
CW_MAIN52700
CW_PATIENTS51510
CW_APP1201200
CW_LIB130850
total55105870

Decision branches: 30, of which 24 covered, 5 unreachable (retirement candidates) and 1 blocked. Database PL/SQL: 2 unit(s) in the coverage report, 0 of 0 blocks covered. Exit check: PASS.

Every branch with its class
Trigger or unitBranchClassScenarios or reason
CW_LIB/unit:CW_SEC.ALLOWrole may not use the item - disabledcoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query
CW_LIB/unit:CW_SEC.ALLOWrole may use the item - left enabledcoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query
CW_LIB/unit:CW_SEC.LOGINknown user - signed incoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, login-ok, main-about, main-print-schedule, navigation, patients-find, patients-query
CW_LIB/unit:CW_SEC.LOGINunknown or inactive user - stop alertunreachablesee CW_LIB/unit:CW_MSG.FAIL
CW_LIB/unit:CW_NAV.OPEN_MODULEform not open - OPEN_FORMcoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query
CW_LIB/unit:CW_NAV.OPEN_MODULEform open - GO_FORMcoverednavigation
CW_LIB/unit:CW_ERR.ON_ERRORcheck constraint APPOINTMENTS_DUR_CK - translatedcoveredappointments-invalid-duration
CW_LIB/unit:CW_ERR.ON_ERRORchild records (ORA-02292) - translatedcoveredappointments-delete-refused
CW_LIB/unit:CW_ERR.ON_ERRORother error - Forms' own messagecoveredbilling-delete-refused
CW_LIB/unit:CW_ERR.ON_ERRORduplicate key (ORA-00001) - translatedunreachableEvery key the forms insert comes from a sequence in PRE-INSERT (APPOINTMENTS_SEQ, PAYMENTS_SEQ).
CW_LIB/unit:CW_ERR.ON_ERRORNOT NULL column (ORA-01400) - translatedunreachableEvery NOT NULL column the forms insert is a Required item (FRM-40202 first), has an initial value or is set in PRE-INSERT.
CW_LIB/unit:CW_ERR.ON_ERRORAPPOINTMENTS_PATIENT_FK or APPOINTMENTS_DOCTOR_FK - translatedunreachablePATIENT_ID comes from CTL.PATIENT_ID (a queried patient) and DOCTOR_ID from LOV_DOCTORS; neither is typed, and the forms delete no patient or doctor.
CW_APPOINTMENTS/unit:CHECK_DOUBLE_BOOKINGoverlap - refusedcoveredappointments-double-booking, navigation
CW_APPOINTMENTS/unit:CHECK_DOUBLE_BOOKINGno overlap or cancelled - acceptedcoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, navigation
CW_APPOINTMENTS/unit:LOAD_PATIENTno patient chosen - messagecoveredappointments-no-patient
CW_APPOINTMENTS/unit:LOAD_PATIENTanother patient - querycoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, navigation
CW_APPOINTMENTS/unit:LOAD_PATIENTsame patient - nothingcoveredappointments-return
CW_BILLING/unit:LOAD_INVOICESno patient - invoices to be paidcoveredbilling-delete-refused, billing-pay, billing-return, billing-unsaved
CW_BILLING/unit:LOAD_INVOICESalready showing - nothingcoveredbilling-return
CW_BILLING/unit:LOAD_INVOICESa patient - that patient's invoicescoveredbilling-patient
CW_BILLING/KEY-COMMITsaved - requery and messagecoveredbilling-pay
CW_BILLING/KEY-COMMITnot saved - no messagecoveredbilling-pay
CW_MAIN/unit:PRINT_SCHEDULEreport finishedblockedNeeds Oracle Reports: the PoC host runs Forms without a Reports server, so RUN_REPORT_OBJECT fails (FRM-41214) and the job never finishes. Missing resource, not a harness limit.
CW_MAIN/unit:PRINT_SCHEDULEreport not finishedcoveredmain-print-schedule
CW_BILLING/unit:QUERY_MASTER_DETAILSimmediate relation - detail queriedcoveredbilling-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved
CW_BILLING/unit:QUERY_MASTER_DETAILSdeferred relationunreachableBoth relations of INVOICES (INVOICES_LINES, INVOICES_PAYMENTS) have Deferred No.
CW_BILLING/unit:CLEAR_ALL_MASTER_DETAILSunchanged details - CLEAR_BLOCK(NO_VALIDATE)coveredbilling-delete-refused, billing-pay, billing-unsaved
CW_BILLING/unit:CLEAR_ALL_MASTER_DETAILSchanged details - CLEAR_BLOCK(ASK_COMMIT)coveredbilling-unsaved
CW_PATIENTS/unit:FIND_PATIENTSno search fields - every patientcoveredappointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, login-billing, login-lov, navigation, patients-find, patients-query
CW_PATIENTS/unit:FIND_PATIENTSsearch fields filled - filteredcoveredpatients-find
Database PL/SQL units
UnitTypeBlocksCoveredClass
CW_APIPACKAGE BODYNone0unreachable
CW_FEE_APIPACKAGE BODYNone0unreachable

Waivers

Source behaviour a customer would probably not replicate, proposed for the customer's decision. Each is pinned to the exact values it covers; the counts are values waived in the final diffs. File: waivers.yaml.

Waived: 6 waivers are proposed for the customer's decision; 6 of them cover 209 values here, where the migrated app deliberately does not copy the original (see waivers).

IdWaiverValuesReason
C1Oracle's frame title before sign-in46Until CW_NAV.START_FORM sets the MDI title, the Forms frame shows the runtime's own title "Oracle Fusion Middleware Forms Services". The target's frame reads "CareWell Clinic" from the start: the web page is the clinic's application, not Oracle's runtime. From sign-in on, both show the same title.
C2The home page's collected-today gauge reads empty118HOME.COLLECTED is a display item drawn as a half gauge on the original canvas; the Forms client's component tree exposes no text for it, so the source records '' at every step. The target shows the amount REFRESH_HOME computes (the same query as the source), as a number with a bar.
C3Help > About names nobody1The menu module CW_APP runs its code with its own instance of the library CW_LIB, whose package data nobody signed into, so Help > About reads "CareWell Clinic, signed in as ." for every user. The target names the signed-in user, which is what the menu code asks for (cw_sec.full_name).
C4The home page forgets the user after File > Exit of a module1CW_PATIENTS, opened with OPEN_FORM after the NEW_FORM from CW_LOGIN, signs in its own library instance (P_USER); when it exits, CW_MAIN's WHEN-FORM-NAVIGATE (REFRESH_HOME) finds the library's package data empty and shows "Welcome," without a name. The target keeps the signed-in user's name on the home page.
C5No item has the focus after a fetched record is refused1When LOAD_PATIENT's query fetches an appointment that CHECK_DOUBLE_BOOKING refuses (the seed data books Dr Maya Wilson twice at one time), the Forms client leaves the keyboard focus on the block's scroll area rather than on an item. The target fetches, refuses and stops at the same record, with the same message and record count, and keeps the cursor in the first item of the first record, where typing goes.
C6The second payment of billing-pay is lost42In billing-pay, after the 42.50 is typed into the new payment row and Tab leaves AMOUNT, the Forms client reports "Amount Required 42.50" on the message line, and File > Save then shows the alert "FRM-40401: No changes to save." (record indicator 0/1). PRE-INSERT has drawn PAYMENTS_SEQ (the source draws 6733 and 6734, as the target does), yet nothing is saved: payment 6734 is not in PAYMENTS and invoice 3852 stays PARTIAL, while the screen still shows the unsaved payment. Identical in all three runs. The target saves the payment as the form's code says: POST-INSERT sets invoice 3852 PAID, KEY-COMMIT queries the invoices to be paid again (3852 is no longer among them) and shows "Saved. Invoice 3852 is PAID.". Replicating would mean losing a payment the user entered.