---
title: Favorite menu management
summary: Lets a user bookmark frequently used menu items, retrieve their favorites, and deactivate or delete a bookmark.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Menu
reference_type: package
operation_id: OP-MNU-03
trigger: Caller requests a user's favorites (or all favorites), or saves a new/changed/deactivated favorite
---

# Favorite menu management

Favorite menu management maintains `MENU_FAVORITE`, per-user bookmarks that let a user pin frequently used menu items for quick access. Each favorite carries a display order, an optional employee ID, and an optional `ItemInactiveDate` that supports soft deactivation without a hard delete. `clsMenuFavoriteCollection` can retrieve all favorites or scope to a single user; the [menu authorization data lookup](menu-authorization-data-lookup.md) operation folds a user's active favorites into the authorized-item projection.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-MNU-03 |
| **Trigger** | Caller requests `clsMenuFavorite`/`clsMenuFavoriteCollection` by key, by user ID, or in full, or calls `Save()` after `Create()`/retrieve |
| **Inputs** | `MenuFavoriteSeqNum` (key lookup); `UserID` (user-scoped retrieve); `MenuItemSeqNum`, `UserID`, `MenuOrder`, `UserEmplID`, `ItemInactiveDate` (insert/update) |
| **Outputs** | Populated `clsMenuFavorite`/`clsMenuFavoriteCollection`; on save, a new sequence-generated favorite, an updated row, or an error message on DML contention |
| **Primary components** | `clsMenuFavorite`, `clsMenuFavoriteCollection` |
| **Entry point** | `Business\SAIF.Business.Menu\clsMenuFavorite.vb` |

## Steps

1. **Retrieve a single favorite.** `clsMenuFavorite.GetMenuFavorite` creates a `Criteria` with the requested `MenuFavoriteSeqNum` and invokes the data portal; `BeforeRetrieve` binds the key and executes `RetrieveSQL` ([SQL-019](../sql/SQL-019-saif-business-menu.md), [MENU_FAVORITE](../database-tables.md#menu-favorite)). `AfterRetrieve` fills the object (including `ItemInactiveDate` as a `clsSmartDate`) and marks it old.
2. **Retrieve favorites for a user.** `clsMenuFavoriteCollection.GetMenuFavoritesForUser` supplies a non-empty `UserID`; `BeforeRetrieve` detects `MyCriteria.UserID.Trim.Length > 0` and executes `RetrieveByUserIDSQL` ([SQL-020](../sql/SQL-020-saif-business-menu.md), [MENU_FAVORITE](../database-tables.md#menu-favorite)) with `:UserID` bound.
   - If `UserID` is empty: `clsMenuFavoriteCollection.GetAllMenuFavorites` executes the unfiltered `RetrieveSQL` ([SQL-021](../sql/SQL-021-saif-business-menu.md)) instead, returning every favorite for every user.
3. **Create a new favorite.** `clsMenuFavorite.NewMenuFavorite` calls `Create()`; `BeforeCreate`/`AfterCreate` execute a scalar request for `MENU_FAVORITE_SEQ.NEXTVAL FROM DUAL` ([SQL-001](../sql/SQL-001-saif-business-menu.md), [DUAL](../database-tables.md#dual)) and seed `MenuFavoriteSeqNum` on the object and its criteria before marking it new and clean.
4. **Save (insert) the favorite.** `BeforeInsert` binds the generated sequence, the target `MenuItemSeqNum`, `UserID`, `UserEmplID`, and `MenuOrder`, then executes `InsertSQL` ([SQL-007](../sql/SQL-007-saif-business-menu.md), [MENU_FAVORITE](../database-tables.md#menu-favorite)).
   - On success (`RowsAffected = 1`): `AfterInsert` calls `MarkOld()`.
   - On failure: `AddErrorMessage("InsertError", "Insert Failed")` ([BR-MNU-02](../business-rules.md#br-mnu-02-dml-row-count-enforcement)).
5. **Update or deactivate a favorite.** `BeforeUpdate` binds the mutable fields including `ItemInactiveDate`, then executes `UpdateSQL` ([SQL-033](../sql/SQL-033-saif-business-menu.md), [MENU_FAVORITE](../database-tables.md#menu-favorite)).
   - Setting `ItemInactiveDate` to a value (rather than clearing it via `SetItemInactiveDateNull`) is the soft-deactivation path: the row remains in the table but is excluded from future favorite-lookup results that filter on `ITEM_INACTIVE_DATE IS NULL` ([BR-MNU-05](../business-rules.md#br-mnu-05-favorite-active-status-enforcement)).
   - `AfterUpdate` applies the row-count check, calling `MarkOld()` on success or adding `UpdateError` with `BOC.Messages.DBContentionError` otherwise ([BR-MNU-02](../business-rules.md#br-mnu-02-dml-row-count-enforcement)).
6. **Hard-delete a favorite.** `BeforeDelete` binds the key and executes `DeleteSQL` ([SQL-040](../sql/SQL-040-saif-business-menu.md), [MENU_FAVORITE](../database-tables.md#menu-favorite)). `AfterDelete` calls `MarkNew()` on a rows-affected value of `1`, or adds a `DeleteError` otherwise ([BR-MNU-02](../business-rules.md#br-mnu-02-dml-row-count-enforcement)).
7. **Fold favorites into authorized-item lookup.** The [menu authorization data lookup](menu-authorization-data-lookup.md) operation's per-user UNION query includes a favorites leg that filters on `FAV.MENU_USER = :MenuUser AND FAV.ITEM_INACTIVE_DATE IS NULL`, so only active (non-deactivated) favorites for that specific user appear in the authorized-item projection ([BR-MNU-05](../business-rules.md#br-mnu-05-favorite-active-status-enforcement)).

## Process flow

The following diagram visualizes the retrieve and deactivation control flow for favorites.

```mermaid
flowchart TD
	Start([Caller requests favorites<br>by key, user, all, or new]) --> Which{Operation?}
	Which -->|Retrieve by key| RetKey[Execute RetrieveSQL<br>with key parameter]
	Which -->|Retrieve by user| UserCheck{UserID provided?}
	UserCheck -->|Yes| RetUser[Execute RetrieveByUserIDSQL]
	UserCheck -->|No| RetAll[Execute unfiltered RetrieveSQL]
	Which -->|Create new| Seq[Obtain NEXTVAL from<br>MENU_FAVORITE_SEQ]
	Seq --> SaveCall{Save called?}
	SaveCall -->|Insert| Insert[Execute InsertSQL]
	SaveCall -->|Update: set ItemInactiveDate| Deactivate[Execute UpdateSQL<br>row remains, excluded from active lookups]
	Insert --> Rows{RowsAffected = 1?}
	Deactivate --> Rows
	Rows -->|Yes| Ok([MarkOld])
	Rows -->|No| Err([AddErrorMessage])
```

## Failure and edge-case behavior

- Every insert, update, and delete enforces a strict rows-affected-equals-1 check; any other value raises `InsertError`, `UpdateError`, or `DeleteError` and the object is not marked clean ([BR-MNU-02](../business-rules.md#br-mnu-02-dml-row-count-enforcement)).
- Soft deactivation (setting `ItemInactiveDate`) and hard delete are two independent paths; a deactivated favorite still occupies a row and a sequence value, and must be explicitly deleted if the row itself needs to be removed.
- `GetAllMenuFavorites` returns every user's favorites with no filtering; a caller must apply its own access control before displaying results scoped to a single user.*
- `ItemInactiveDate` is represented as a `clsSmartDate`/nullable `Date`; a null value (the default) means the favorite is active, which the [menu authorization data lookup](menu-authorization-data-lookup.md) operation relies on for its `IS NULL` filter ([BR-MNU-05](../business-rules.md#br-mnu-05-favorite-active-status-enforcement)).

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

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources
- `Business\SAIF.Business.Menu\clsMenuFavorite.vb`
- `Business\SAIF.Business.Menu\clsMenuFavoriteCollection.vb`
