Cron Lifecycle and Schedule
This page documents how scheduled automation works in Lancaster Archery Academy:
- What triggers the scheduler
- Which commands run
- What each command does
- When each command executes
Local Safety Warning
Many of these commands can technically be run from a local environment, but they are not all safe to run casually.
Before running any scheduler or Artisan command locally, review the command implementation and environment configuration first. Some jobs can:
- Send real email notifications
- Attempt live payment charges
- Modify or delete reservation, invoice, waiver, audit, or waitlist data
Always verify .env values (mail, payment gateway, queue, and related integration credentials), use a safe test/sandbox setup, and avoid running production-targeted commands unless you are intentionally validating that behavior.
Time-based behavior uses application timezone (APP_TIMEZONE), defaulting to America/Chicago.
High-Level Lifecycle
Every minute, schedule:run checks due tasks and executes matching commands.
The recurring lifecycle is:
- Cart hygiene and seat recovery
- Expired pending carts are cleaned up hourly.
- Released seats can immediately trigger waitlist opportunities.
- Waiver validity maintenance
- Signed waivers older than one year are expired daily.
- Subscription automation (only when enabled)
- Waitlist notifications are processed during daytime hours.
- Subscription invoices are generated near end-of-month and month close.
- Unpaid subscription invoices are charged at the beginning of the month, with a retry run.
- Subscription sessions/reservations are generated daily.
- Audit DB retention
- Old audit and error-log records are pruned daily in production.
If subscriptions are disabled (Util::getSystemSubscriptionStatus() returns false), subscription automation does not run.
Command Schedule Matrix
| Command | Interval | Environment | Action Performed |
|---|---|---|---|
clean-expired-carts | Hourly | All | Removes expired pending reservations, carts/orders, declined transactions, related credits, related team records, and expired user-defined sessions (TechnoHunt). Then triggers waitlist checks for affected sessions/timeslots. |
waiver:expire | Daily at 01:00 | All | Expires waiver state for accounts with waivers older than 1 year (has_waiver=0, clears signed date and waiver id). |
waiting-list:trigger | Hourly between 06:00 and 20:00 | When subscriptions are enabled | Iterates active timeslots and runs maybeTriggerWaitlist() to notify eligible waitlist entries. |
create:subscriptionInvoices --emailToRecipient=true | Monthly on day 25 (default scheduler time) | Production + subscriptions enabled | Generates subscription invoices for upcoming billing period and emails recipients. |
create:subscriptionInvoices --emailToRecipient=true | Last day of month at 23:45 | Production + subscriptions enabled | Final invoice pass to capture late/newly-enrolled autopay subscriptions before charging. |
charge:unpaid | Monthly on day 1 at 02:00 | Production + subscriptions enabled | Attempts to charge unpaid subscription invoices. Uses overlap protection. |
charge:unpaid | Monthly on day 3 (default scheduler time) | Production + subscriptions enabled | Retry pass for unpaid subscription invoices. |
schedule:subscriptionSessions | Daily at 02:00 | Subscriptions enabled | Generates future subscription sessions and corresponding subscription reservations. |
audits:prune --days=90 --force | Daily at 02:30 | Production | Deletes audit records older than 90 days. |
audits:prune-logs --days=90 --force | Daily at 03:00 | Production | Deletes error log records (site_logs) older than 90 days from audit connection. |
Basic Timeline (Typical Month)
Hourly
clean-expired-cartswaiting-list:triggerduring 6 AM to 8 PM (when subscriptions are enabled)
Daily
01:00waiver:expire02:00schedule:subscriptionSessions(when subscriptions are enabled)02:30audits:prune --days=90 --force(production)03:00audits:prune-logs --days=90 --force(production)
Monthly
- Day
25: invoice generation run (create:subscriptionInvoices --emailToRecipient=true) - Last day
23:45: invoice cleanup run - Day
1at02:00: unpaid charge run - Day
3: unpaid charge retry run
Operational Notes
- Subscription jobs are feature-gated by
enable_subscriptions(checked viaUtil::getSystemSubscriptionStatus()). - Invoice and charging jobs are production-restricted where configured.
clean-expired-cartsandwaiting-list:triggerwork together to keep seat availability and waitlist notifications current.- After deploys, any schedule changes are code-driven and picked up automatically; no per-command Forge cron edits are needed unless intentionally bypassing Laravel scheduler.
Source of Truth
- Scheduler:
app/Console/Kernel.php - Command implementations:
app/Console/Commands/