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. Ship the rule that registers the job ​

Unlike this doc's own first draft claimed, the platform does not ship this rule for you — every plugin using a shared sweep-style entity (entity_status_date_sweep_config, entity_aggregation_config, entity_compliance_config, ...) ships its own copy of the one small AFTER_CREATE rule that registers the corresponding job, the same way hcm-assets ships its own entity_aggregation_config_register.json / entity_compliance_config_register.json. Skip this file and your config rows sit in the table forever with no job ever scheduled to read them — confirmed live (2026-09-22): a tenant with pre-existing entity_status_date_sweep_config rows from another plugin still had no engine-entity.status-date-sweep job at all until this rule shipped.

spk-assembly/metadata/rules/entity_status_date_sweep_config_register.json (real file):

json
{
  "entityType": "entity_status_date_sweep_config",
  "name": "ensure_status_date_sweep_job_registered",
  "description": "Register the generic EntityStatusDateSweepJob for this tenant on first config row.",
  "triggerEvent": "AFTER_CREATE",
  "conditions": null,
  "actions": "[{\"type\": \"EXECUTE_SERVICE\", \"service\": \"ensureEntityStatusDateSweepJobRegistered\"}]",
  "priority": 10,
  "active": true
}

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_OUT → OVERDUE. "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