migrate-oracle-forms · git:20260824.826e4cd · 2026-08-24 · sha256 e6c081e0b10bbb1c

migrate-oracle-forms git:20260824.826e4cdA

Immutable. This exact content is served forever at /api/v1/blob/e6c081e0b10bbb1c.

---
name: migrate-oracle-forms
description: "Assess and migrate Oracle Forms applications to Mendix — .fmb forms to pages, PL/SQL to microflows, and a staged migration strategy. Use when analysing or converting an Oracle Forms system."
---

# Oracle Forms to Mendix Migration Skill

This skill provides comprehensive guidance for migrating Oracle Forms applications to Mendix using MDL (Mendix Definition Language).

## When to Use This Skill

Use this skill when:
- Converting Oracle Forms (.fmb) applications to Mendix
- Translating PL/SQL logic to Mendix microflows
- Mapping Oracle Forms UI elements to Mendix widgets
- Planning a migration strategy for legacy Oracle Forms systems

## Migration Overview

Oracle Forms migration to Mendix involves:
1. **Data Model**: Oracle tables → Mendix entities
2. **Business Logic**: PL/SQL triggers/procedures → Mendix microflows
3. **User Interface**: Forms blocks/items → Mendix pages/widgets
4. **Navigation**: Form canvases → Mendix page navigation

## Reserved Word Conflicts

Most common words (`check`, `text`, `format`, `value`, `type`, `index`, `status`, `select`, etc.) now work **unquoted** as attribute names in MDL. Only structural keywords (`create`, `delete`, `begin`, `end`, `return`, `entity`, `module`) need quoting.

### Naming Best Practices

While most words are no longer reserved, using descriptive names is still recommended for clarity:

| Oracle Forms Field | Recommended Mendix Name | Notes |
|-------------------|-------------------------|-------|
| `check` | `check` or `CheckStatus` | Works unquoted |
| `text` | `text` or `TextContent` | Works unquoted |
| `format` | `format` or `FormatType` | Works unquoted |
| `value` | `value` or `FieldValue` | Works unquoted |
| `Name` | `Name` or `ItemName` | Works unquoted (not a keyword) |
| `type` | `type` or `ItemType` | Works unquoted |
| `create` | `"create"` or `CreatedBy` | **Requires quoting** (structural keyword) |
| `delete` | `"delete"` or `IsDeleted` | **Requires quoting** (structural keyword) |

### Example

```mdl
create persistent entity MyModule.FormField (
  check: boolean default false,  -- Works unquoted
  text: string(500),             -- Works unquoted
  format: string(50),            -- Works unquoted
  CheckFlag: boolean default false, -- Renamed alternative (also fine)
  TextContent: string(500), -- Renamed
  FormatType: string(50)    -- Renamed
);
```

## Script Organization

### Execution Order Dependencies

MDL scripts execute statements sequentially. Items created in one statement can be referenced in subsequent statements within the **same script execution**.

**Key Insight**: Microflows and pages created earlier in the script are tracked and can be resolved by later statements.

### Recommended Script Structure

```mdl
-- check-skip: the PHASE 3 page block uses shorthand pseudo-syntax
-- (layout/title/parameter/widgets, dataview source, INPUT ...) to sketch the
-- migrated UI concept; it is not runnable MDL. See create-page / overview-pages.
-- ============================================
-- PHASE 1: Domain Model (Entities & Associations)
-- ============================================

create persistent entity MyModule.Customer (
  CustomerCode: string(50),
  CustomerName: string(200),
  Email: string(200),
  IsActive: boolean default true
);

create persistent entity MyModule.Order (
  OrderNumber: string(50),
  OrderDate: datetime,
  TotalAmount: decimal
);

create association MyModule.Order_Customer
from MyModule.Order to MyModule.Customer
type reference;
/

-- ============================================
-- PHASE 2: Microflows (Business Logic)
-- ============================================

/**
 * Validates and saves a customer record
 * Replaces Oracle Forms POST-INSERT/POST-UPDATE triggers
 */
create microflow MyModule.ACT_Customer_Save ($Customer: MyModule.Customer)
returns boolean as $success
begin
  declare $success boolean = false;

  -- Validation (replaces WHEN-VALIDATE-ITEM)
  if $Customer/CustomerCode = empty then
    validation feedback $Customer/CustomerCode message 'Customer code is required';
    return false;
  end if;

  commit $Customer;
  set $success = true;
  return $success;
end;
/

-- ============================================
-- PHASE 3: Pages (User Interface)
-- ============================================

-- Now this page can reference the microflow created above
create page MyModule.Customer_Edit
layout Atlas_Default
title 'Edit Customer'
parameter $Customer: MyModule.Customer
widgets (
  dataview source $Customer (
    INPUT 'CustomerCode' attribute CustomerCode label 'Customer Code',
    INPUT 'CustomerName' attribute CustomerName label 'Name',
    INPUT 'Email' attribute Email label 'Email',

    container 'ButtonBar' (
      -- Reference to microflow created in Phase 2
      button 'Save' call microflow MyModule.ACT_Customer_Save (
        Customer = $Customer
      ),
      button 'Cancel' on CLICK close page
    )
  )
);
/
```

## Validation Feedback

### VALIDATION FEEDBACK Syntax

**CRITICAL**: VALIDATION FEEDBACK requires an attribute path, not just a message.

**WRONG:**
```mdl
validation feedback 'Customer code is required';  -- Missing attribute!
```

**CORRECT:**
```mdl
-- Syntax: VALIDATION FEEDBACK $entity/attribute MESSAGE 'message'
validation feedback $Customer/CustomerCode message 'Customer code is required';
validation feedback $Order/OrderDate message 'Order date cannot be in the future';
```

### Mapping Oracle Forms Validation

| Oracle Forms | Mendix MDL |
|--------------|------------|
| `when-VALIDATE-item` trigger | `if ... validation feedback` in microflow |
| `raise FORM_TRIGGER_FAILURE` | `validation feedback` + `return false` |
| `message('error text')` | `validation feedback $entity/attribute message 'error text'` |

### Complete Validation Pattern

```mdl
/**
 * Validates order before save
 * Replaces Oracle Forms WHEN-VALIDATE-RECORD trigger
 */
create microflow MyModule.ACT_Order_Validate ($Order: MyModule.Order)
returns boolean as $IsValid
begin
  declare $IsValid boolean = true;

  -- Required field validation
  if $Order/OrderNumber = empty then
    validation feedback $Order/OrderNumber message 'Order number is required';
    set $IsValid = false;
  end if;

  -- Date validation
  if $Order/OrderDate > [%CurrentDateTime%] then
    validation feedback $Order/OrderDate message 'Order date cannot be in the future';
    set $IsValid = false;
  end if;

  -- Cross-field validation
  if $Order/TotalAmount < 0 then
    validation feedback $Order/TotalAmount message 'Total amount cannot be negative';
    set $IsValid = false;
  end if;

  return $IsValid;
end;
/
```

## PL/SQL to Microflow Mapping

### Data Manipulation

| Oracle PL/SQL | Mendix MDL |
|---------------|------------|
| `insert into table ...` | `$var = create Module.Entity (...)` |
| `update table set ...` | `change $var (...)` + `commit $var` |
| `delete from table ...` | `delete $var` |
| `select ... into ...` | `retrieve $var from Module.Entity where ...` |
| `commit` | `commit $var` |
| `rollback` | Built-in with error handlers |

### Control Flow

| Oracle PL/SQL | Mendix MDL |
|---------------|------------|
| `if ... then ... elsif ... else ... end if` | `if ... then ... else ... end if` |
| `for ... loop ... end loop` | `loop $item in $list begin ... end loop` |
| `while ... loop ... end loop` | Not directly supported; use recursive microflow |
| `CURSOR` | `retrieve $list from ...` then `loop` |
| `EXCEPTION when ... then` | `on error { ... }` |

### Example: PL/SQL to MDL

**Oracle PL/SQL:**
```sql
declare
  v_count NUMBER := 0;
  v_total NUMBER := 0;
begin
  for rec in (select * from orders where status = 'PENDING') loop
    v_count := v_count + 1;
    v_total := v_total + rec.amount;

    update orders set status = 'PROCESSED' where id = rec.id;
  end loop;

  commit;
  DBMS_OUTPUT.PUT_LINE('Processed ' || v_count || ' orders, total: ' || v_total);
EXCEPTION
  when OTHERS then
    rollback;
    raise;
end;
```

**Mendix MDL:**
```mdl
create microflow MyModule.ACT_ProcessPendingOrders ()
returns string as $Result
begin
  declare $count integer = 0;
  declare $Total decimal = 0;
  declare $Result string = '';

  -- Retrieve pending orders (replaces CURSOR) — retrieve creates $OrderList,
  -- never declare a list variable first (CE0053/CE0038, MDL040)
  retrieve $OrderList from MyModule.Order
    where status = 'PENDING';

  -- Process each order (replaces FOR LOOP)
  loop $Order in $OrderList
  begin
    set $count = $count + 1;
    set $Total = $Total + $Order/Amount;

    change $Order (status = 'PROCESSED');
    commit $Order on error {
      log error 'Failed to process order: ' + $Order/OrderNumber;
    };
  end loop;

  log info 'Processed ' + toString($count) + ' orders, total: ' + toString($Total);
  set $Result = 'Processed ' + toString($count) + ' orders';
  return $Result;
end;
/
```

## UI Component Mapping

### Oracle Forms Items to Mendix Widgets

| Oracle Forms Item | Mendix Widget | MDL Syntax |
|-------------------|---------------|------------|
| Text Item | Text Input | `INPUT 'name' attribute attr` |
| Display Item | Text | `text 'content'` |
| Check Box | Check Box | `checkbox 'name' attribute attr` |
| Radio Group | Radio Buttons | `RADIO 'name' attribute attr` |
| List Item (LOV) | Drop-down | `dropdown 'name' attribute attr` |
| Push Button | Button | `button 'name' on CLICK ...` |
| Tab Canvas | Tab Container | `TAB_CONTAINER (TAB 'name' (...))` |

### Oracle Forms Blocks to Mendix DataViews

**Oracle Forms Block → Mendix DataView:**
```mdl
-- Single-record block
dataview source $Customer (
  INPUT 'Code' attribute CustomerCode,
  INPUT 'Name' attribute CustomerName
)

-- Multi-record block (tabular)
datagrid source $OrderList (
  column 'OrderNumber' attribute OrderNumber,
  column 'OrderDate' attribute OrderDate,
  column 'Amount' attribute TotalAmount
)
```

### Master-Detail Pattern

**Oracle Forms Master-Detail → Mendix:**
```mdl
-- check-skip: shorthand pseudo-syntax sketch of the migrated page, not runnable
-- MDL. See create-page / overview-pages for real page syntax.
create page MyModule.CustomerOrders
layout Atlas_Default
title 'Customer Orders'
parameter $Customer: MyModule.Customer
widgets (
  -- Master block
  dataview source $Customer (
    INPUT 'Code' attribute CustomerCode readonly,
    INPUT 'Name' attribute CustomerName readonly
  ),

  -- Detail block (orders for this customer)
  datagrid 'OrderGrid' source database MyModule.Order
    where '[MyModule.Order_Customer = $Customer]' (
    column 'OrderNumber' attribute OrderNumber,
    column 'OrderDate' attribute OrderDate,
    column 'Amount' attribute TotalAmount
  )
);
/
```

## Triggers to Microflows

### Common Trigger Mappings

| Oracle Forms Trigger | Mendix Implementation |
|---------------------|----------------------|
| `when-NEW-FORM-INSTANCE` | Page load microflow (data source) |
| `when-NEW-RECORD-INSTANCE` | OnChange microflow on data source |
| `when-VALIDATE-item` | OnChange microflow or validation in save |
| `when-VALIDATE-RECORD` | Validation microflow before save |
| `post-query` | Microflow data source with transformation |
| `PRE-insert` / `PRE-update` | Before commit event handler |
| `post-insert` / `post-update` | After commit event handler |
| `key-commit` | Save button action microflow |
| `on-error` | `on error { ... }` blocks |

## Migration Checklist

Before starting migration:
- [ ] Export Oracle Forms XML (.xml) or use Forms2XML utility
- [ ] Document all triggers and their purposes
- [ ] Map database tables to Mendix entities
- [ ] Identify LOVs and map to enumerations
- [ ] Check for structural keyword conflicts (Create, Delete, Begin, End, Return)

During migration:
- [ ] Create entities first (Phase 1)
- [ ] Create microflows second (Phase 2)
- [ ] Create pages last (Phase 3) - they can reference microflows
- [ ] Test validation patterns thoroughly
- [ ] Use `validation feedback $entity/attribute message 'message'` for all validations

After migration:
- [ ] Run `mxcli check script.mdl -p app.mpr --references`
- [ ] Open in Mendix Studio Pro to verify
- [ ] Test all validation scenarios
- [ ] Verify master-detail relationships work correctly

## Common Migration Errors

| Error | Cause | Fix |
|-------|-------|-----|
| "Parse error: mismatched input 'Create'" | Structural keyword as attribute | Use `"create"` (quoted) or rename |
| "microflow not found" | Referenced before created | Move microflow definition before page |
| "page not found" | Referenced before created | Move page definition earlier |
| "VALIDATION FEEDBACK requires attribute" | Missing attribute path | Use `validation feedback $entity/attribute message 'msg'` |
| CE0117 "Error in expression" | Missing module prefix | Use fully qualified names |

## Tips for Success

1. **Plan attribute names carefully**: Most words work unquoted; only structural keywords (`create`, `delete`, `begin`, `end`, `return`) need quoting
2. **Organize scripts by phase**: Entities → Microflows → Pages
3. **Test incrementally**: Migrate one form at a time
4. **Keep validation close to logic**: Embed validation in save microflows
5. **Document mappings**: Track which Oracle Forms items map to which Mendix elements
6. **Use meaningful names**: `ACT_Customer_Save` not `SUB_SAVE`
7. **Leverage CRUD generation**: Use `/create-crud` skill for standard operations

## Related Skills

- [/write-microflows](../write-microflows/SKILL.md) - Detailed microflow syntax
- [/create-crud](./create-crud.md) - Generate CRUD operations
- [/overview-pages](../overview-pages/SKILL.md) - Page building patterns
- [/master-detail-pages](../master-detail-pages/SKILL.md) - Master-detail layouts