Retail order system
Six Forms modules: a main menu, ORDERS (master-detail with a list of values and alerts) and four single-table forms (categories, customers, employees, products).
The live demos share one database per app: visitors see each other's changes, and the data resets to the scenario fixture every night.
Original Forms source: github.com/v7med7elmy-ai/oracle-retail-management-system
at commit 30b5409, MIT licence.
Parity: 17 of 17 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.
| Scenario | What it does | match | tolerated | waived | defect | unmapped | Three runs |
|---|---|---|---|---|---|---|---|
| categories-add-delete | Add a record, save, delete the unreferenced fixture record. | 64 | 0 | 0 | 0 | 12 | identical |
| categories-browse | Open categories from the main menu, browse first/last/next/previous, exit to the menu. | 57 | 0 | 0 | 0 | 14 | identical |
| customers-add-delete | Add a record, save, delete the unreferenced fixture record. | 68 | 4 | 0 | 0 | 12 | identical |
| customers-browse | Open customers from the main menu, browse first/last/next/previous, exit to the menu. | 62 | 0 | 0 | 0 | 14 | identical |
| employees-add-delete | Add a record, save, delete the unreferenced fixture record. | 78 | 4 | 0 | 0 | 12 | identical |
| employees-browse | Open employees from the main menu, browse first/last/next/previous, exit to the menu. | 66 | 6 | 0 | 0 | 15 | identical |
| main-menu-exit | Press the main menu's exit button. | 22 | 0 | 0 | 0 | 4 | identical |
| main-menu-orders | Open orders from the main menu, exit back to the menu. | 34 | 27 | 0 | 0 | 11 | identical |
| order-two-lines | Create an order with two lines, change a quantity, save. | 259 | 290 | 0 | 0 | 99 | identical |
| orders-add-new | Query the orders, press ADD NEW, fill a header and save; ORDER_ID is mandatory and empty, so the save stops. | 115 | 216 | 0 | 0 | 62 | identical |
| orders-browse | Execute the query, decline to save the PRE-FORM record, browse the orders and their lines. | 118 | 170 | 0 | 0 | 51 | identical |
| orders-delete-empty | Query the orders, go to order 902 (no lines), press DELETE; the order is deleted. | 97 | 123 | 0 | 0 | 37 | identical |
| orders-delete-refused | Query the orders, press DELETE on order 901, which has lines; nothing is deleted. | 91 | 120 | 0 | 0 | 39 | identical |
| orders-navigate-unsaved | Query, change a line quantity, press Next; Cancel, then Next again and No. | 140 | 189 | 0 | 0 | 62 | identical |
| orders-save-fails | Enter an order with a line whose ITEM_ID already exists, press SAVE; the commit fails. | 135 | 194 | 0 | 0 | 60 | identical |
| products-add-delete | Add a record, save, delete the unreferenced fixture record. | 102 | 8 | 0 | 0 | 17 | identical |
| products-browse | Open products from the main menu, browse first/last/next/previous, exit to the menu. | 62 | 10 | 0 | 0 | 14 | identical |
| total | 1570 | 1361 | 0 | 0 | 535 |
Tolerance policy: tolerance-policy.yaml.
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.
| Module | Triggers | Program units | Covered | Unreachable | Blocked |
|---|---|---|---|---|---|
| MAIN_MENU | 7 | 0 | 7 | 0 | 0 |
| ORDERS | 14 | 3 | 17 | 0 | 0 |
| CATEGORIES_FORM | 9 | 0 | 9 | 0 | 0 |
| CUSTOMERS_FORM | 9 | 0 | 9 | 0 | 0 |
| EMPLOYEES_FORM | 9 | 0 | 9 | 0 | 0 |
| PRODUCT_FORM | 9 | 0 | 9 | 0 | 0 |
| total | 57 | 3 | 60 | 0 | 0 |
Decision branches: 18, of which 14 covered, 2 unreachable (retirement candidates) and 2 blocked. Database PL/SQL: 1 unit(s) in the coverage report, 1 of 1 blocks covered. Exit check: PASS.
Every branch with its class
| Trigger or unit | Branch | Class | Scenarios or reason |
|---|---|---|---|
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSED | commit succeeded (FORM_SUCCESS TRUE) | covered | order-two-lines |
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSED | commit failed (FORM_SUCCESS FALSE) | covered | orders-add-new, orders-save-fails |
ORDERS/ORDERS/ON-CHECK-DELETE-MASTER | detail rows exist, delete refused | covered | orders-delete-refused |
ORDERS/ORDERS/ON-CHECK-DELETE-MASTER | no detail rows, delete goes ahead | covered | orders-delete-empty |
ORDERS/ORDERS/ON-POPULATE-DETAILS | master record NEW or INSERT (or ORDER_ID null) - no detail query | covered | orders-add-new |
ORDERS/ORDERS/ON-POPULATE-DETAILS | detail queried | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/ORDERS/ON-POPULATE-DETAILS | cursor moved by the query, returned to the start item | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/ORDERS/ON-POPULATE-DETAILS | cursor still on the start item after the query | unreachable | The relation is not deferred, so QUERY_MASTER_DETAILS always does GO_BLOCK('ORDER_ITEMS') and the cursor always leaves the master item it started on. |
ORDERS/unit:QUERY_MASTER_DETAILS | relation not deferred - GO_BLOCK and EXECUTE_QUERY | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:QUERY_MASTER_DETAILS | relation deferred - SET_BLOCK_PROPERTY non-coordinated | unreachable | The only relation (ORDERS_ORDER_ITEMS) is not deferred and nothing changes that at run time. |
ORDERS/unit:QUERY_MASTER_DETAILS | GO_BLOCK to the detail failed - exception handler | blocked | Needs GO_BLOCK('ORDER_ITEMS') to fail during master coordination. The block has no PRE-BLOCK, WHEN-NEW-BLOCK-INSTANCE or record-level validation that can refuse entry, and the master record was validated before coordination began; no input found that makes it fail. FRD would show it as CHECK_PACKAGE_FAILURE with FORM_SUCCESS FALSE. |
ORDERS/unit:CHECK_PACKAGE_FAILURE | previous built-in succeeded | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:CHECK_PACKAGE_FAILURE | previous built-in failed - raise FORM_TRIGGER_FAILURE | blocked | Called after GO_BLOCK and GO_ITEM in the relation code. Needs one of those navigations to fail, the same condition as the QUERY_MASTER_DETAILS exception branch; no input found. |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | detail block NEW - nothing to clear | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | detail block queried - CLEAR_BLOCK(NO_VALIDATE), cursor returned | covered | orders-add-new, orders-browse, orders-delete-empty |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changed block below the master found (First_Changed_Block_Below) | covered | orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changes - CLEAR_BLOCK(ASK_COMMIT), user discards or saves, detail cleared | covered | orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changes - CLEAR_BLOCK(ASK_COMMIT), user cancels, FORM_TRIGGER_FAILURE, cursor returned | covered | orders-navigate-unsaved |
Database PL/SQL units
| Unit | Type | Blocks | Covered | Class |
|---|---|---|---|---|
| TRG_CALC_ITEM_TOTAL | TRIGGER | 1 | 1 | covered |
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.
Each waiver applies to 0 values: the migrated app implements the source behaviour, so the customer can still choose either way.
| Id | Waiver | Values | Reason |
|---|---|---|---|
| W1 | ORDER_DATA saved as NULL | 0 | The source saves a new order without an order date: the empty ORDER_DATA item is inserted as NULL, so no order the form creates has a date. A target that stores the time of the order instead keeps data the source loses. Parity is implemented (the target sends every empty header item as NULL). |
| W2 | Order number consumed when the form opens | 0 | Opening ORDERS draws an order number from ORD_SEQ and shows it (16); saving draws another one and stores that (17). The number the user sees while entering the order is not the number of the saved order, and every opened form uses up a number. A target that assigns the number at save draws one value per order and shows it only after saving. Parity is implemented (the target draws a number on opening, shows it, and lets the insert draw the stored one). |
| W3 | Line ITEM_ID typed by the user | 0 | The user types the key of each order line (ITEM_ID 1, 2), and ITEM_SEQ is not used. ITEM_ID is the primary key of all order lines, not a line number within the order, so a second order whose lines are typed as 1 and 2 again fails on a duplicate key. A target that draws the key from ITEM_SEQ avoids the collision. Parity is implemented (new lines take the typed key; a key that belongs to another order is refused). |
| W4 | Two modal alerts after saving | 0 | After a save the source shows two modal alerts, each needing OK: the commit confirmation (FRM-40400, "3 records applied and saved") and then the form's own success message. A web target would normally confirm once, without a modal dialog, in the status line. Parity is implemented (the target shows the two confirmations as browser alerts, in the same order, with the same messages in the status line). |
| W5 | Key debug message on record navigation (single-table forms) | 0 | Next, Previous and Last in Categories, Customers, Employees and Products leave a message such as "Category Id Required902" on the message line: the record's key appended to a fixed text, left over from a validation trigger. It tells the user nothing (no field is required at that point) and First does not show it. A target would show no message. Parity is implemented (the same text on the same buttons). |
| W6 | Customer debug message in ORDERS | 0 | After a query in ORDERS the message line reads "Customer Id1": the first order's customer number appended to a fixed text, left over from a trigger, and it stays while the user navigates. A target would show no message. Parity is implemented. |
| W7 | Placeholder alert on leaving Employees | 0 | The Employees exit button shows a modal alert "gooooooooooooooooooooood" that must be acknowledged before the form closes: a developer's placeholder. A target would close the form directly. Parity is implemented. Without the alert the scenario's acknowledging step cannot be performed; that entry stays a defect to review with this waiver (it names no value to pin). |
| W8 | Error message when the application starts | 0 | The main menu opens with "FRM-41003: This function cannot be performed here." on the message line: a built-in started from the menu's startup trigger fails in that context. The user did nothing wrong; a target would start with an empty message line. Parity is implemented. |
| W9 | Keyboard hint of the Forms client while a question is open | 0 | While the save question (Yes/No/Cancel) is open, the Forms Java client writes its keyboard hint "Press Tab to move between choices, Return to select." to the message line. It belongs to the client's dialog handling, not to the application. A web target's dialog needs no such hint. Parity is implemented (the target writes the same hint while its question dialog is open). |
| W10 | An order added with Add cannot be saved | 0 | Add in ORDERS creates a record without an order number. ORDER_ID is required and disabled, so the user cannot enter one and saving fails every time: "FRM-40202: Field must be entered.", then "FRM-40222: Disabled item 'ORDERS.ORDER_ID' failed validation.", then the form's own failure message. Only the record the form opens on (which draws a number) can be saved. A target would number the added order and save it. Parity is implemented. A target that saves it also differs in the database rows and ORD_SEQ draws of that save; those entries are reviewed with this waiver, not covered by it. |