Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Business Calendar & Scheduler

Overview

The business calendar drives deterministic, auditable scheduling for the tax preparation pipeline. Rather than relying on ad-hoc manual triggers, every recurring obligation — quarterly estimated tax payments, BAS lodgements, monthly statement ingest runs — is encoded as a ScheduledEvent in a TOML manifest. The BusinessCalendar evaluates upcoming events against a horizon window and feeds them to the OperationDispatcher.

This approach provides several guarantees critical for expat tax work:

  • No missed deadlines: US and AU obligations are codified with tax code citations; the scheduler surfaces them before they are due.
  • Reproducible automation: Monthly ingest and classification runs fire on fixed day-of-month rules, not human memory.
  • Audit trail: Every dispatched operation records its triggering event ID, so the audit log can be traced back to the calendar manifest.
  • Agent-editable without recompile: TOML manifests and Rhai dispatch rules can be updated by an agent or operator without rebuilding the Rust binary.

Calendar-Driven Operation Pipeline

The scheduling loop is expressed as a Rhai function chain. Each step either proceeds or short-circuits based on the result of the previous step.

fn check_calendar() -> find_due_events
fn find_due_events() -> dispatch_operations
fn dispatch_operations() -> execute_waterfall
fn execute_waterfall() -> record_completion
if horizon_days > 30 -> warn_upcoming
if horizon_days <= 7 -> escalate_urgent
flowchart TD
    check_calendar["check_calendar"]
    find_due_events["find_due_events"]
    dispatch_operations["dispatch_operations"]
    execute_waterfall["execute_waterfall"]
    record_completion["record_completion"]
    horizon_days_gt_30{"horizon_days > 30"}
    warn_upcoming["warn_upcoming"]
    horizon_days____7{"horizon_days <= 7"}
    escalate_urgent["escalate_urgent"]
    check_calendar --> find_due_events
    find_due_events --> dispatch_operations
    dispatch_operations --> execute_waterfall
    execute_waterfall --> record_completion
    horizon_days_gt_30 -->|"true"|warn_upcoming
    horizon_days____7 -->|"true"|escalate_urgent
  • check_calendar — loads the active BusinessCalendar and computes today’s date relative to each event’s RecurrenceRule.
  • find_due_events — returns events whose next fire date falls within the configured horizon window.
  • dispatch_operations — resolves each event’s OperationKind and forwards it to the OperationDispatcher.
  • execute_waterfall — runs each operation in dependency order (ingest before classify, classify before export).
  • record_completion — writes the completion record to the audit trail with the triggering event ID.
  • warn_upcoming — logs upcoming events beyond 7 days but within the horizon (informational).
  • escalate_urgent — emits a high-priority notification for events due within 7 days.

Scheduling Loop Diagram

fn business_calendar() -> events_due
fn events_due() -> operation_dispatcher
fn operation_dispatcher() -> ingest_statement
fn operation_dispatcher() -> classify_transactions
fn operation_dispatcher() -> check_tax_deadline
fn operation_dispatcher() -> export_workbook
fn ingest_statement() -> operation_result
fn classify_transactions() -> operation_result
fn check_tax_deadline() -> operation_result
fn export_workbook() -> operation_result
fn operation_result() -> audit_trail
if recurrence == monthly -> events_due
if recurrence == quarterly_estimated -> events_due
if recurrence == annual -> events_due
flowchart TD
    business_calendar["business_calendar"]
    events_due["events_due"]
    operation_dispatcher["operation_dispatcher"]
    ingest_statement["ingest_statement"]
    classify_transactions["classify_transactions"]
    check_tax_deadline["check_tax_deadline"]
    export_workbook["export_workbook"]
    operation_result["operation_result"]
    audit_trail["audit_trail"]
    recurrence____monthly{"recurrence == monthly"}
    recurrence____quarterly_estimated{"recurrence == quarterly_estimated"}
    recurrence____annual{"recurrence == annual"}
    business_calendar --> events_due
    events_due --> operation_dispatcher
    operation_dispatcher --> ingest_statement
    operation_dispatcher --> classify_transactions
    operation_dispatcher --> check_tax_deadline
    operation_dispatcher --> export_workbook
    ingest_statement --> operation_result
    classify_transactions --> operation_result
    check_tax_deadline --> operation_result
    export_workbook --> operation_result
    operation_result --> audit_trail
    recurrence____monthly -->|"true"|events_due
    recurrence____quarterly_estimated -->|"true"|events_due
    recurrence____annual -->|"true"|events_due

US Tax Calendar

Key US federal deadlines encoded in calendar/tax_calendar_us.toml:

EventDateTax Code CitationTags
Q1 Estimated Tax PaymentApril 15IRC §6654estimated_tax, quarterly, schedule_c
Q2 Estimated Tax PaymentJune 15IRC §6654estimated_tax, quarterly
Q3 Estimated Tax PaymentSeptember 15IRC §6654estimated_tax, quarterly
Q4 Estimated Tax PaymentJanuary 15 (next year)IRC §6654estimated_tax, quarterly
Form 1040 Filing DeadlineApril 15 (June 15 expat auto-extension)26 USC §6072annual_return, form_1040
Form 1040 Extended DeadlineOctober 15Form 4868annual_return, extension, form_4868
FBAR (FinCEN Form 114)April 15 (auto-extension to Oct 15)31 USC §5314fbar, foreign_accounts, fincen_114
FATCA Form 8938April 15IRC §6038Dfatca, form_8938, foreign_assets
Monthly Statement Ingest1st of each monthautomation, ingest, recurring
Monthly Classification Run2nd of each monthautomation, classification, recurring

AU Tax Calendar

Key Australian tax deadlines encoded in calendar/tax_calendar_au.toml:

EventDateTax Code CitationTags
BAS GST Q1 LodgementOctober 28ATO BAS programbas, gst, quarterly, q1
BAS GST Q2 LodgementFebruary 28ATO BAS programbas, gst, quarterly, q2
BAS GST Q3 LodgementApril 28ATO BAS programbas, gst, quarterly, q3
BAS GST Q4 LodgementJuly 28ATO BAS programbas, gst, quarterly, q4
Individual Tax ReturnOctober 31ITAA 1997annual_return, individual, itaa_1997
Tax Agent ExtensionMay 15 (next year)ATO lodgement programannual_return, extension, tax_agent
Concessional Super ContributionJune 30ITAA 1997 s.290-180superannuation, concessional, s290-180
CGT Discount Asset ReviewJune 30ITAA 1997 s.115-Acgt, capital_gains, s115-a, discount_method
Monthly Statement Ingest1st of each monthautomation, ingest, recurring
Monthly Classification Run2nd of each monthautomation, classification, recurring

TOML Format Reference

Each entry in a calendar manifest is a [[event]] block:

[[event]]
id = "fbar_deadline"
# Human-readable description including tax code citation
description = "FinCEN Form 114 (FBAR) deadline — April 15 with auto-extension to October 15 (31 USC §5314)"

# RecurrenceRule — determines when the event fires
# Supported types: Annual, Monthly, QuarterlyEstimated, EveryNDays, CronExpr (stub)
recurrence = { type = "Annual", month = 4, day = 15 }

# OperationKind — the operation dispatched when this event fires
# Supported: CheckTaxDeadline, IngestStatement, ClassifyTransactions,
#            GenerateAuditTrail, ExportWorkbook
operation = { type = "CheckTaxDeadline", deadline_id = "fbar_deadline" }

jurisdiction = "US"   # "US" | "AU" | "UK" | ...
enabled = true        # false to suspend without removing the event
tags = ["fbar", "foreign_accounts", "fincen_114"]

Field descriptions:

FieldTypeDescription
idStringUnique event identifier; referenced in audit trail records
descriptionStringHuman-readable label; include tax code citation for compliance events
recurrenceInline tableRecurrenceRule variant with type-specific fields
operationInline tableOperationKind variant dispatched on fire
jurisdictionStringRegulatory jurisdiction; used to filter calendars per entity
enabledBoolSet false to suspend without deleting
tagsString arrayFree-form labels for filtering and grouping

Using the Calendar

#![allow(unused)]
fn main() {
use ledger::calendar::{BusinessCalendar, ScheduledEvent};
use chrono::Local;

// Load a TOML calendar manifest
let cal = BusinessCalendar::from_toml_file("calendar/tax_calendar_us.toml")?;

// Find events due within the next 30 days
let today = Local::now().date_naive();
let due = cal.upcoming(today, 30);

for event in &due {
    println!("Due: {} — {}", event.next_date, event.description);
}

// Dispatch all due operations
let dispatcher = OperationDispatcher::new();
for event in due {
    let result = dispatcher.dispatch(&event.operation, &context).await?;
    audit_trail.record(event.id, result);
}
}

Key methods on BusinessCalendar:

  • from_toml_file(path) — parse a TOML manifest from disk; returns Result<BusinessCalendar>
  • upcoming(today, horizon_days) — returns Vec<ScheduledEvent> due within horizon_days
  • overdue(today) — returns events whose last expected fire date has passed without a completion record
  • merge(other) — combine US and AU calendars for multi-jurisdiction entities

Stub: CronExpr

The CronExpr recurrence type is defined in the RecurrenceRule enum for forward compatibility but its evaluation is not yet implemented. Calling next_date() on a CronExpr event returns None. This reserves the variant in TOML manifests without blocking the current annual/monthly scheduling paths.

# CronExpr — planned, not yet evaluated
recurrence = { type = "CronExpr", expr = "0 9 1 * *" }

When CronExpr evaluation is implemented it will use the cron crate and integrate with the existing upcoming() method without changes to the TOML format.