Oracle Forms to Mendix Migration Skill

SkillDatabases & data

Lets your agent assess Oracle Forms apps and convert forms and PL/SQL logic into Mendix pages and microflows.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Oracle Forms to Mendix Migration Skill skill

About this capability

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.

What this skill tells your AI

The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/migrate-oracle-forms/SKILL.md and read by ahel’s review.

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 FieldRecommended Mendix NameNotes
checkcheck or CheckStatusWorks unquoted
texttext or TextContentWorks unquoted
formatformat or FormatTypeWorks unquoted
valuevalue or FieldValueWorks unquoted
NameName or ItemNameWorks unquoted (not a keyword)
typetype or ItemTypeWorks unquoted
create"create" or CreatedByRequires quoting (structural keyword)
delete"delete" or IsDeletedRequires quoting (structural keyword)

Example

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

-- 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:

validation feedback 'Customer code is required';  -- Missing attribute!

CORRECT:

-- 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 FormsMendix MDL
when-VALIDATE-item triggerif ... validation feedback in microflow
raise FORM_TRIGGER_FAILUREvalidation feedback + return false
message('error text')validation feedback $entity/attribute message 'error text'

Complete Validation Pattern

/**
 * 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/SQLMendix 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 ...
commitcommit $var
rollbackBuilt-in with error handlers

Control Flow

Oracle PL/SQLMendix MDL
if ... then ... elsif ... else ... end ifif ... then ... else ... end if
for ... loop ... end looploop $item in $list begin ... end loop
while ... loop ... end loopNot directly supported; use recursive microflow
CURSORretrieve $list from ... then loop
EXCEPTION when ... thenon error { ... }

Example: PL/SQL to MDL

Oracle PL/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:

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 ItemMendix WidgetMDL Syntax
Text ItemText InputINPUT 'name' attribute attr
Display ItemTexttext 'content'
Check BoxCheck Boxcheckbox 'name' attribute attr
Radio GroupRadio ButtonsRADIO 'name' attribute attr
List Item (LOV)Drop-downdropdown 'name' attribute attr
Push ButtonButtonbutton 'name' on CLICK ...
Tab CanvasTab ContainerTAB_CONTAINER (TAB 'name' (...))

Oracle Forms Blocks to Mendix DataViews

Oracle Forms Block → Mendix DataView:

-- 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:

-- 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 TriggerMendix Implementation
when-NEW-FORM-INSTANCEPage load microflow (data source)
when-NEW-RECORD-INSTANCEOnChange microflow on data source
when-VALIDATE-itemOnChange microflow or validation in save
when-VALIDATE-RECORDValidation microflow before save
post-queryMicroflow data source with transformation
PRE-insert / PRE-updateBefore commit event handler
post-insert / post-updateAfter commit event handler
key-commitSave button action microflow
on-erroron 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

ErrorCauseFix
"Parse error: mismatched input 'Create'"Structural keyword as attributeUse "create" (quoted) or rename
"microflow not found"Referenced before createdMove microflow definition before page
"page not found"Referenced before createdMove page definition earlier
"VALIDATION FEEDBACK requires attribute"Missing attribute pathUse validation feedback $entity/attribute message 'msg'
CE0117 "Error in expression"Missing module prefixUse 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

Signals

GitHub stars
122
Forks
49
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
migrate-oracle-forms
Source
github.com/mendixlabs/mxcli