Files
crewli/api/app/Console/Commands
bert.hausmans 609280d061 feat(timetable): DemoteExpiredOptions scheduled command
`artist:demote-expired-options` artisan command finds every
ArtistEngagement still in Option whose option_expires_at has passed,
transitions it back to Draft via the existing state-machine
(transitionStatus), and writes an `option_expired` activity entry
with the original expiry timestamp captured in properties so the
audit log distinguishes system-driven expiries from manual demotions.

Idempotency: the state-machine bails when the engagement is no longer
in Option, so a second run within the same minute is a no-op for any
given row. The auto-logged `updated` row + the explicit
`status_changed` + the `option_expired` entries are emitted only by
the run that actually performs the transition.

Scheduled in routes/console.php daily at 03:00 Europe/Amsterdam,
matching the existing nightly low-traffic window.

Notification (email project leader on demotion) is deferred to the
notification framework that lands post-Accreditation; tracked under
BACKLOG entry ART-DEMOTE-NOTIFICATION.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:59:39 +02:00
..