Skip to content

7. The return-due reminder job

What we're adding

A checkout that is CHECKED_OUT and:

  • within 3 days of its due_date → status DUE_SOON
  • past its due_date → status OVERDUE

Zero Java. The platform's generic engine-entity.status-date-sweep job runs daily and applies config rows we seed.

1. Ship the shared config entity

So a fresh environment that predates the shared table still gets it. spk-assembly/metadata/entities/entity_status_date_sweep_config.json (real file) — ships only the additive columns (day_offset, seeded_by); the installer is idempotent-additive, so an existing table just gains anything missing.

2. Seed the config rows

spk-assembly/metadata/seed-data/office-equipment-sweep-configs.json (real file):

json
{
  "entity": "entity_status_date_sweep_config",
  "keyFields": ["entity_name", "date_field", "set_status_to", "day_offset"],
  "source": "office-equipment",
  "rows": [
    {
      "entity_name": "oeq_checkout",
      "status_field": "status",
      "when_status_in": "CHECKED_OUT",
      "date_field": "due_date",
      "compare_op": "lte",
      "day_offset": 3,
      "set_status_to": "DUE_SOON",
      "active": true,
      "seeded_by": "office-equipment"
    },
    {
      "entity_name": "oeq_checkout",
      "status_field": "status",
      "when_status_in": "CHECKED_OUT,DUE_SOON",
      "date_field": "due_date",
      "compare_op": "lt",
      "day_offset": 0,
      "set_status_to": "OVERDUE",
      "active": true,
      "seeded_by": "office-equipment"
    }
  ]
}
  • Row 1: day_offset: 3 + compare_op: lte → "fires when due_date is within 3 days" — the "N days before" reminder.
  • Row 2: day_offset: 0 + compare_op: lt → "fires once due_date has passed". when_status_in includes DUE_SOON so a warned checkout still escalates to overdue.
  • Neither sets set_field, so they write status_field (status) itself.
  • keyFields includes day_offset so the two rows for the same date column are distinct on re-install.

Status value vs. boolean flag

This module flips a status enum because that reads well on the checkout grid. A boolean flag works too: since 2026-09-10 the generic job coerces set_status_to: "true" / "false" to a real boolean (and numeric strings to numbers) before the write, so a config targeting a boolean/integer/numericset_field is fully supported. An earlier draft of this tutorial hit a SQL type error doing exactly that — that platform bug is fixed.

3. The job registers itself

The platform ships an AFTER_CREATE rule on entity_status_date_sweep_config that calls ensureEntityStatusDateSweepJobRegistered. So the first row you seed auto-registers the job for your tenant — you ship no register rule for this job (unlike the aggregation/cadence/compliance jobs).

Verify

bash
erp schema validate spk-assembly/metadata/seed-data/office-equipment-sweep-configs.json --schema plugin-seed-data

OK. After publish, the job is enabled:

bash
$ erp api get "/api/v1/jobs/engine-entity.status-date-sweep"
{ "jobCode": "engine-entity.status-date-sweep", "status": "ENABLED",
  "cronExpression": "0 5 0 * * *", "concurrencyPolicy": "PER_TENANT" }

This exact sequence was run against the tutorial module:

bash
# a CHECKED_OUT checkout whose due_date is in the past
$ erp api post "/api/v1/entities/oeq_checkout/records" \
    --body '{"checkout_number":"CO-100","equipment_id":1,"employee_id":1,"checkout_date":"2026-08-01","due_date":"2026-09-04","status":"CHECKED_OUT"}'
{ "id": 2, "checkout_number": "CO-100", "status": "CHECKED_OUT" }

$ erp api post "/api/v1/jobs/engine-entity.status-date-sweep/execute" --body "{}"
{ "executionId": 534 }

$ erp api get "/api/v1/jobs/engine-entity.status-date-sweep/executions?size=1"
... "resultJson": "{\"swept\": 4, \"failed\": 0, \"rowsScanned\": 102, \"configsScanned\": 30}" ...

$ erp api get "/api/v1/entities/oeq_checkout/records/2"
{ "checkout_number": "CO-100", "status": "OVERDUE" }

CHECKED_OUTOVERDUE. "failed": 0. Verified end to end.

Common mistakes

  • set_field a boolean/numeric column with an unparseable set_status_to (e.g. "yes" into a boolean) → that row counts failed. Use "true"/"false".
  • when_status_in casing not matching the enum → nothing sweeps.
  • Forgot day_offset → the "3 days before" rule fires only on the exact day.

Next: 8. Menus, i18n, validate, publish