Skip to content

An entity with an approval workflow + reminder job

When you need this

A record with a full lifecycle: it's drafted, submitted for approval, approved, acted on, and eventually chased if it goes stale. This recipe combines the approval workflow and the reminder job on one entity — exactly what the tutorial's oeq_checkout does.

The five files

FilePurpose
metadata/entities/oeq_checkout.jsonthe entity; status enum includes every lifecycle value (DRAFT, PENDING_APPROVAL, APPROVED, CHECKED_OUT, DUE_SOON, OVERDUE, RETURNED, REJECTED)
metadata/workflow/office-equipment.checkout-approval.jsonsingle-stage manager approval
metadata/rules/oeq_checkout_submit_workflow.jsonAFTER_UPDATE rule: status → PENDING_APPROVAL starts the workflow; decision writes APPROVED/REJECTED back
metadata/entities/entity_status_date_sweep_config.jsonship the shared config entity (idempotent-additive)
metadata/seed-data/office-equipment-sweep-configs.json2 sweep configs: CHECKED_OUT within 3 days → DUE_SOON; past due → OVERDUE

All five are real in tutorial/example-plugin/.

The lifecycle, end to end

DRAFT
  │  user: Submit for approval  (PUT status = PENDING_APPROVAL)

PENDING_APPROVAL ──rule──▶ workflow instance + manager task
  │  manager approves ──callback──▶ status = APPROVED

APPROVED
  │  equipment handed over  (PUT status = CHECKED_OUT)

CHECKED_OUT
  │  nightly sweep: due_date within 3 days

DUE_SOON
  │  nightly sweep: due_date passed

OVERDUE
  │  equipment returned  (PUT status = RETURNED)

RETURNED

The user drives DRAFT → PENDING_APPROVAL, APPROVED → CHECKED_OUT, * → RETURNED (buttons on the page). The workflow drives PENDING_APPROVAL → APPROVED/REJECTED. The sweep job drives CHECKED_OUT → DUE_SOON → OVERDUE. No two mechanisms fight over the same transition.

The rules that bite

  • The submit rule needs the status__previous guard so it fires only on the transition, not every later update.
  • The OVERDUE sweep config's when_status_in includes DUE_SOON, so a warned checkout still escalates.
  • Sweep configs flip the status enum, not a boolean flag (the generic job can't write a string to a boolean column).