Skip to main content

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

Running Scheduled Commands Locally Can Have Real Side Effects

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:

  1. Cart hygiene and seat recovery
  • Expired pending carts are cleaned up hourly.
  • Released seats can immediately trigger waitlist opportunities.
  1. Waiver validity maintenance
  • Signed waivers older than one year are expired daily.
  1. 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.
  1. 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

CommandIntervalEnvironmentAction Performed
clean-expired-cartsHourlyAllRemoves 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:expireDaily at 01:00AllExpires waiver state for accounts with waivers older than 1 year (has_waiver=0, clears signed date and waiver id).
waiting-list:triggerHourly between 06:00 and 20:00When subscriptions are enabledIterates active timeslots and runs maybeTriggerWaitlist() to notify eligible waitlist entries.
create:subscriptionInvoices --emailToRecipient=trueMonthly on day 25 (default scheduler time)Production + subscriptions enabledGenerates subscription invoices for upcoming billing period and emails recipients.
create:subscriptionInvoices --emailToRecipient=trueLast day of month at 23:45Production + subscriptions enabledFinal invoice pass to capture late/newly-enrolled autopay subscriptions before charging.
charge:unpaidMonthly on day 1 at 02:00Production + subscriptions enabledAttempts to charge unpaid subscription invoices. Uses overlap protection.
charge:unpaidMonthly on day 3 (default scheduler time)Production + subscriptions enabledRetry pass for unpaid subscription invoices.
schedule:subscriptionSessionsDaily at 02:00Subscriptions enabledGenerates future subscription sessions and corresponding subscription reservations.
audits:prune --days=90 --forceDaily at 02:30ProductionDeletes audit records older than 90 days.
audits:prune-logs --days=90 --forceDaily at 03:00ProductionDeletes error log records (site_logs) older than 90 days from audit connection.

Basic Timeline (Typical Month)

Hourly

  • clean-expired-carts
  • waiting-list:trigger during 6 AM to 8 PM (when subscriptions are enabled)

Daily

  • 01:00 waiver:expire
  • 02:00 schedule:subscriptionSessions (when subscriptions are enabled)
  • 02:30 audits:prune --days=90 --force (production)
  • 03:00 audits:prune-logs --days=90 --force (production)

Monthly

  • Day 25: invoice generation run (create:subscriptionInvoices --emailToRecipient=true)
  • Last day 23:45: invoice cleanup run
  • Day 1 at 02:00: unpaid charge run
  • Day 3: unpaid charge retry run

Operational Notes

  • Subscription jobs are feature-gated by enable_subscriptions (checked via Util::getSystemSubscriptionStatus()).
  • Invoice and charging jobs are production-restricted where configured.
  • clean-expired-carts and waiting-list:trigger work 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/