Skip to content

Celery Beat Schedule

Daily scheduled tasks that run automatically via Celery Beat.

Task Schedule

Task Schedule Time Purpose
Interest accrual Daily --- Accrue daily interest on all active loans
Amortization period transitions Daily --- Transition period statuses (future → due → overdue)
Delinquency engine Daily 2:30 AM Recalculate DPD, update buckets, fire webhook events
Default detection Daily --- Auto-transition loans exceeding DPD threshold to defaulted
Late fee assessment Daily --- Assess late fees per fee schedule rules
Broken promise detection Daily --- Check for unfulfilled promise-to-pay commitments
Dunning execution Daily 4:00 AM Send dunning communications based on DPD triggers
Scheduled fee assessment Daily --- Assess recurring servicing fees
Variable rate adjustment Daily --- Update variable-rate loan rates from latest index
Portfolio snapshots Daily --- Capture aggregate portfolio metrics
Lien expiration check Daily --- Alert on liens expiring within 180 days
Forbearance expiry Daily --- Auto-complete expired forbearance agreements
Non-accrual evaluation Daily --- Place/clear loans on non-accrual based on DPD threshold
Collection queue assignment Daily --- Auto-assign delinquent loans to collection queues

Dispatch Pattern

Beat dispatch tasks run in the public schema and fan out to individual tenants:

@shared_task
def dispatch_daily_interest_accrual() -> None:
    """Fan out interest accrual to all active tenants."""
    for tenant in Tenant.objects.filter(status="active"):
        # Each tenant task uses TenantTask base to activate the schema
        accrue_interest_for_tenant.delay(schema_name=f"tenant_{tenant.slug}")

Each per-tenant task uses the TenantTask base class, which activates the correct PostgreSQL schema before execution.

Task Dependencies

Some tasks have implicit ordering requirements:

  1. Interest accrual should run before delinquency engine (ensures accrued interest is current)
  2. Delinquency engine should run before late fee assessment (DPD must be current for fee triggers)
  3. Delinquency engine should run before dunning execution (bucket must be current for dunning triggers)
  4. Amortization period transitions should run before delinquency engine (period statuses must be current)

These dependencies are handled through task scheduling (ordering by time) rather than explicit Celery chains.

Configuration

The beat schedule is defined in config/celery.py:

app.conf.beat_schedule = {
    "accrue-daily-interest": {
        "task": "apps.loans.tasks.dispatch_daily_interest_accrual",
        "schedule": crontab(hour=1, minute=0),
    },
    "run-delinquency-engine": {
        "task": "apps.collections.tasks.dispatch_delinquency_engine",
        "schedule": crontab(hour=2, minute=30),
    },
    # ... additional tasks
}

Monitoring

  • Tasks log start/completion timestamps for auditing
  • Failed tasks are retried based on individual task retry configuration
  • Celery Flower or similar monitoring tools can track task execution

See Also