---
title: Create a menu item
summary: Creates a new menu item, illustrating the create pattern shared by every maintenance object in this library (`clsMenuPage`, `clsMenuSecurity`, `clsMenuSecurityUser`, `clsMenuFavorite`, `clsApplicationCode`, `clsMenuCategoryCode`), including the cascading save of the item's child security groups.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-08-17
library: SAIF.Business.Menu
reference_type: package
operation_id: OP-MENU-06
trigger: Administration code calls `clsMenuItem.NewMenuItem(dpi)` then `Save()`
---

# Create a menu item

Creates a new menu item, illustrating the create pattern shared by every maintenance object in this library (`clsMenuPage`, `clsMenuSecurity`, `clsMenuSecurityUser`, `clsMenuFavorite`, `clsApplicationCode`, `clsMenuCategoryCode`), including the cascading save of the item's child security groups.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-MENU-06 |
| **Trigger** | Administration code calls `clsMenuItem.NewMenuItem(dpi)` then `Save()` |
| **Inputs** | Item properties supplied by the caller; `DataPortalInfo.UserID` |
| **Outputs** | New `MENU_ITEM` row and a populated sequence number |
| **Primary components** | `clsMenuItem` (create + insert); the same pattern applies to every maintenance object in this library |
| **Entry point** | `SAIF.Business.Menu/clsMenuItem.vb` |

## Steps

1. `NewMenuItem` calls `DataAccessLayer.CreateObject(New Criteria(dpi))`.
2. `BeforeCreate` issues a `Scalar` request with `CreateSQL` = `SELECT MENU_ITEM_SEQ.NEXTVAL FROM DUAL` ([SQL-002](../sql/SQL-002-menu-item-next-seq.md)). ([BR-MENU-014](../business-rules.md#br-menu-014-sequence-assigned-identity))
3. `AfterCreate` stores the returned sequence into both the object and the criteria, stamps `UpdateDate = Now` and `UpdateUser` from `DataPortalInfo.UserID.Trim.ToUpper`, then `MarkNew` + `MarkClean`. ([BR-MENU-015](../business-rules.md#br-menu-015-natural-key-creation-for-codes), [BR-MENU-016](../business-rules.md#br-menu-016-audit-stamp-on-insert-and-update))
4. The caller sets properties; each setter compares old and new values and calls `MarkDirty` only on an actual change. ([BR-MENU-017](../business-rules.md#br-menu-017-dirty-tracking-on-change))
5. `Save` → `SaveMe`: if the object itself is dirty, call `MyBase.SaveChild()` when it is a child, else `MyBase.Save()`. ([BR-MENU-018](../business-rules.md#br-menu-018-save-only-when-dirty))
   - If not dirty: no database work occurs.
6. `BeforeInsert` issues a `NonQuery` with `InsertSQL` ([SQL-008](../sql/SQL-008-insert-menu-item.md)) and parameters `MenuItemSeqNum`, `MenuPageSeqNum`, `MenuCategoryCode` (5), `VisibleFlag` (1, from `IsVisible.ToY_N_String`), `PageOrder`, `ToolTip` (1000), `UserId` (30). `UPDATE_DATE` is set to `SYSDATE` in SQL, not passed. ([BR-MENU-016](../business-rules.md#br-menu-016-audit-stamp-on-insert-and-update), [BR-MENU-019](../business-rules.md#br-menu-019-yn-boolean-flag-storage); table [MENU_ITEM](../database-tables.md#menu_item))
7. `AfterInsert` requires exactly one affected row; otherwise it adds error message `InsertError` = "Insert Failed". On success it calls `MarkOld`. ([BR-MENU-020](../business-rules.md#br-menu-020-single-row-write-verification))
   - On failure (rows affected ≠ 1): the object records the `InsertError` error message instead of throwing.
8. After the parent save succeeds, if `SecurityGroups.IsDirty` the child `clsMenuSecurityCollection` is saved with `SaveChildObject` ([SQL-010](../sql/SQL-010-insert-menu-security.md), table [MENU_SECURITY](../database-tables.md#menu_security)). ([BR-MENU-021](../business-rules.md#br-menu-021-cascading-child-security-group-save))

## Process flow

```mermaid
flowchart TD
    Start([NewMenuItem]) --> Seq[BeforeCreate: fetch MENU_ITEM_SEQ.NEXTVAL]
    Seq --> Stamp[AfterCreate: stamp UpdateDate/UpdateUser, MarkClean]
    Stamp --> SetProps[Caller sets properties, MarkDirty on change]
    SetProps --> Dirty{Object dirty?}
    Dirty -->|No| Skip([Save is a no-op])
    Dirty -->|Yes| Insert[BeforeInsert: INSERT MENU_ITEM row]
    Insert --> RowCheck{Exactly one row affected?}
    RowCheck -->|No| Fail([AfterInsert: add InsertError])
    RowCheck -->|Yes| MarkOld[AfterInsert: MarkOld]
    MarkOld --> SecDirty{SecurityGroups dirty?}
    SecDirty -->|Yes| SaveChild([SaveChildObject: insert MENU_SECURITY rows])
    SecDirty -->|No| Done([Create complete])
    SaveChild --> Done
```

## Failure and edge-case behavior
- Insert affects zero or more than one row: `AfterInsert` adds `InsertError` = "Insert Failed" instead of throwing.
- Object is not dirty when `Save` is called: no database work occurs.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- SAIF.Business.Menu/clsMenuItem.vb
- SAIF.Business.Menu/clsMenuSecurityCollection.vb
