# Delivery Strategies

# Understanding Window Strategies

Window strategies determine how deliveries are estimated and scheduled by selecting the calculation method used for each system. The window strategy is configured in the system edit dialog and determines which approach is used to trigger deliveries.

---

## What are window strategies?

A window strategy defines the **method** used to calculate when a delivery is needed:

- **Degree Day**: Temperature-based consumption estimation using heating degree days
- **Monitored**: Real-time tank level readings from IoT monitoring devices  
- **Calendar**: Time-based recurring delivery schedules based on intervals

The window strategy determines which calculation approach takes priority for triggering deliveries for all equipment within that system.

---

## Window strategy types

### Degree Day Window

Uses outdoor temperature data to estimate fuel consumption and predict when refills are needed based on accumulated heating degree days.

**Best for:**

- Heating oil customers
- Predictable seasonal consumption patterns
- Locations without tank monitors

**How it works:**

- Accumulates heating degree days since last delivery
- Applies usage rates (winter/summer) to estimate gallons consumed
- Triggers delivery when estimated remaining fuel drops below threshold
- Uses visual degree day windows to determine delivery timing

See: [Understanding Degree Days](https://docs.kozyops.com/books/fuel-systems/page/understanding-degree-days)

### Monitored Window

Uses real-time tank level data from IoT monitoring devices to trigger deliveries based on actual fuel levels.

**Best for:**

- Customers with installed tank monitors
- Critical accounts requiring precise tracking
- Unpredictable consumption patterns

**How it works:**

- Reads current tank level from monitor device via serial number
- Triggers delivery when level drops below configured threshold
- Provides ground truth vs. estimated consumption

See: [Understanding Fuel Monitoring](https://docs.kozyops.com/books/fuel-systems/page/understanding-fuel-monitoring)

### Calendar Window

Uses recurring time-based schedules to plan deliveries at regular intervals regardless of consumption.

**Best for:**

- Commercial accounts with predictable usage
- Will-call customers preferring set schedules
- Service contracts with fixed delivery frequencies

**How it works:**

- Schedules deliveries at configured intervals (days, weeks, months, years)
- Triggers on specific calendar dates or days of the week
- Can have seasonal start/end dates
- Only one schedule can be active at a time

See: [Understanding Calendar Schedules](https://docs.kozyops.com/books/fuel-systems/page/understanding-calendar-schedules)

---

## Configuring window strategies

Window strategies are configured when adding or editing a system through the Add/Edit System dialog.

### Basic system settings

At the top of the dialog:

- **System Name**: Text field for the system identifier
- **Active toggle**: Enable/disable the entire system
- **Auto Delivery toggle**: Enable automatic delivery scheduling based on the selected window strategy
- **Fuel selector**: Choose between Propane, Oil, etc.
- **Window Strategy selector**: Choose **Degree Day**, **Monitored**, or **Calendar**
- **Usage multi-select**: Select applicable usage types

---

## Strategy-specific configuration tabs

### Degree Day Settings Tab

**Configuration fields:**

- **Winter Usage Rate**: Gallons per degree day during winter (up to 4 decimal places)
- **Summer Usage Rate**: Gallons per degree day during summer (up to 4 decimal places)
- **Window Start (Visual DD)**: Lower boundary of the delivery window
- **Target DD**: Target degree day for delivery trigger
- **Window End (Visual DD)**: Upper boundary of the delivery window

**Action buttons:**

- **Force Degree Day** (thumbtack icon): Manually forces the current degree day target
- **Reset DD**: Resets the degree day accumulation
- **Reset Window**: Resets the delivery window boundaries

### Tank Monitor Tab

**Configuration fields:**

- **Monitor Serial Number**: Required field for the tank monitor identifier such as OTOData's DeviceId.
- **Monitor Barcode**: Optional field

**Validation:**

- Submit button is disabled until serial number is provided for monitored systems

### Calendar Schedules Tab

**Schedule configuration:**

- **Frequency**: Numeric value (minimum 1)
- **Units**: Days, Weeks, Months, Years
- **Preferred Day of Week**: Monday-Sunday or "Any day"
- **Season Starts/Ends**: Date pickers
- **Requested Volume (gal)**: Delivery amount
- **Active toggle**: Activate/deactivate schedule

**Schedule actions:**

- **Set Active**: Activates schedule (deactivates others)
- **Deactivate**: Pauses schedule
- **Edit**: Modify schedule
- **Delete**: Remove schedule

---

## Auto Delivery toggle

Controls whether the system should appear on the routing map & automatically generates delivery requests for calendar scheduling:

- **Enabled**: System actively triggers deliveries based on strategy
- **Disabled**: System tracks data but doesn't create automatic requests

---

## Related guides

- [Degree Day System](./degree-day-system.md)
- [Monitoring](./monitoring.md)
- [Calendar Scheduling](./calendar-system.md)
- [Add System](../add-system.md)
- [Editing a System](./editing-a-system.md)

# Understanding Calendar Schedules

Calendar scheduling allows you to set up automatic delivery requests for your customers based on recurring patterns. When you post an invoice for a fuel delivery, the system automatically creates the next delivery request based on the schedule you've configured.

## Key Features

### Flexible Recurring Patterns

You can schedule deliveries to repeat at any interval you need:

- **Every X Days** - For example, every 30 days or every 45 days
- **Every X Weeks** - For example, every week or every 2 weeks
- **Every X Months** - For example, monthly or every 3 months
- **Every X Years** - For annual deliveries

### Specific Day of the Week

If your customers prefer deliveries on certain days, you can set schedules to occur on:

- Monday through Sunday (or any specific day)
- Leave blank if the day of the week doesn't matter

When you set a day of the week, the system will automatically schedule the delivery on the next occurrence of that day after the interval passes.

**Example:** If you schedule "every 2 weeks on Friday," the system will always create delivery requests for Fridays, even if the exact 2-week interval would land on a different day.

### Seasonal Schedules

Many customers have different needs throughout the year. Calendar scheduling supports seasonal patterns:

- **Summer Schedule** - Active only during warm months (e.g., May 1 - October 31)
- **Winter Schedule** - Active only during cold months (e.g., November 1 - March 31)
- **Year-Round** - Active all 12 months

The system uses the month and day from your start and end dates, ignoring the year. This means your seasonal schedules automatically repeat every year without needing to update them.

**Example:** Set a schedule active from November 1 to March 31, and it will automatically activate every winter, year after year.

### Pre-Set Delivery Volume

You can specify how many gallons or liters should be requested for each scheduled delivery. This helps your drivers know approximately how much fuel to bring.

## How It Works

### Setting Up a Schedule

For each fuel system (tank), you can create one or more calendar schedules with:

1. **Frequency** - How often deliveries should occur (number and unit)
2. **Day of Week** (Optional) - Which day deliveries should happen
3. **Start Date** (Optional) - What month/day the schedule becomes active
4. **End Date** (Optional) - What month/day the schedule becomes inactive
5. **Delivery Volume** (Optional) - How many gallons/liters to request

### Automatic Delivery Request Creation

When you post an invoice for a delivery:

1. The system checks if there's an active calendar schedule for that fuel system
2. It verifies the delivery date falls within the schedule's active period
3. It calculates when the next delivery should occur
4. It automatically creates a delivery request for that future date

The new delivery request includes:

- The calculated delivery date
- The requested volume (if specified in the schedule)
- A note explaining it was created by the calendar schedule

### Managing Active Schedules

You can have multiple schedules for the same system, but only one will create a delivery request per invoice posting. Schedules can be:

- **Active** - Currently in use and creating delivery requests
- **Inactive** - Disabled and not creating delivery requests

To stop a schedule from creating delivery requests, mark it as inactive rather than deleting it. This preserves the configuration if you need to reactivate it later.

## Common Use Cases

### Example 1: Summer Propane Fill-Ups

**Scenario:** A customer wants propane deliveries every 60 days during the summer months only.

**Configuration:**
- Frequency: 60 days
- Day of Week: (none)
- Active: May 1 - October 31
- Volume: 100 gallons

**Result:** Every time you post an invoice between May and October, the system creates the next delivery request 60 days out. No deliveries are scheduled during winter months.

### Example 2: Weekly Friday Deliveries

**Scenario:** A commercial customer receives heating oil every week on Fridays.

**Configuration:**
- Frequency: 1 week
- Day of Week: Friday
- Active: Year-round
- Volume: 150 gallons

**Result:** After each delivery, the system automatically schedules the next delivery for the following Friday.

### Example 3: Monthly Winter Fuel Oil

**Scenario:** A residential customer needs heating oil monthly during winter only.

**Configuration:**
- Frequency: 1 month
- Day of Week: (none)
- Active: November 1 - March 31
- Volume: 200 gallons

**Result:** Monthly deliveries are scheduled from November through March each year. The schedule automatically reactivates each winter.

### Example 4: Bi-Weekly Tuesday Deliveries (Winter Only)

**Scenario:** A customer wants deliveries every 2 weeks on Tuesdays, but only during the heating season.

**Configuration:**
- Frequency: 2 weeks
- Day of Week: Tuesday
- Active: October 1 - April 30
- Volume: 175 gallons

**Result:** Deliveries are scheduled every other Tuesday during the specified months, automatically resuming each heating season.

## Tips for Success

### Setting Seasonal Boundaries

- Use month and day only - the year is automatically ignored
- For winter schedules that cross year boundaries (e.g., Nov-Mar), make sure the start month comes after the end month
- Leave both blank if you want year-round scheduling

### Choosing Day of Week

- Use this when customers have specific preferences or when route optimization matters
- Leave blank for maximum flexibility in scheduling
- Remember: Monday = 1, Sunday = 7

### Delivery Volume Estimates

- Set realistic volumes based on the customer's usage patterns
- This helps drivers prepare the right amount of fuel
- You can always adjust the actual delivery amount when fulfilling the request

### Multiple Schedules

- You can create different schedules for different seasons
- Make sure to set appropriate start/end dates so they don't overlap
- Only one schedule will trigger per invoice posting, so avoid conflicting active schedules

## Troubleshooting

### No Delivery Request Created

If a delivery request isn't automatically created after posting an invoice:

- Check that the schedule is active (not marked inactive)
- Verify the delivery date falls within the schedule's active date range
- Ensure the fuel system has the calendar schedule properly configured
- Contact your system administrator if the issue persists

### Wrong Delivery Date

If the calculated delivery date seems incorrect:

- Double-check the frequency and frequency units (days vs. weeks vs. months)
- If using day of week, verify the correct day is selected (1-7)
- Review the seasonal start/end dates to ensure they're set as intended

### Seasonal Schedule Not Activating

For schedules that should work across the year boundary (Nov-Mar):

- Make sure the start date's month number is greater than the end date's month number
- Example: November (month 11) to March (month 3) is correct
- The system automatically handles the year transition

## Getting Started

To begin using calendar scheduling:

1. Navigate to the fuel system you want to schedule
2. Create a new calendar schedule
3. Configure the frequency, optional day of week, and seasonal dates
4. Set the delivery volume if desired
5. Save the schedule
6. Post the next invoice for that system - the delivery request will be created automatically

Calendar scheduling saves time by eliminating manual delivery request creation and ensures consistent service for your customers. Set it up once and let the system handle the rest!

# Understanding Degree Days

The degree day system predicts heating fuel consumption based on outdoor temperature. It's commonly used for automatic oil delivery scheduling to estimate when a customer will need a refill.

---

## What is a degree day?

A **degree day** measures heating demand:

- **Heating Degree Day (HDD)**: Measures how cold it is relative to a base temperature (usually 65°F)
- **Calculation**: For each day, HDD = max(0, Base Temp - Average Outdoor Temp)
- **Accumulation**: Sum HDDs over time to estimate fuel consumption

**Example**:

- Base temperature: 65°F
- Average outdoor temperature: 40°F
- HDD for that day: 65 - 40 = 25 HDD

If it takes 5 HDD to burn 1 gallon of oil (K-factor = 5), then after 100 accumulated HDD, the customer has used approximately 20 gallons.

---

## Key formulas

### Daily heating degree days

$$
HDD_{day} = \max(0, T_{base} - T_{avg})
$$

Where:

- $T_{base}$ = Base temperature (typically 65°F)
- $T_{avg}$ = Average outdoor temperature for the day

### Accumulated degree days

$$
HDD_{accumulated} = \sum_{i=1}^{n} HDD_i
$$

Sum all daily HDDs since the last delivery.

### K-factor (consumption rate)

The K-factor represents the relationship between degree days and gallons consumed:

$$
K = \frac{HDD}{gallons}
$$

**Example**: If a customer used 200 gallons over 1000 HDD, then:

$$
K = \frac{1000}{200} = 5.0
$$

So it takes 5 HDD to consume 1 gallon.

### Estimating gallons used

$$
gallons_{used} = \frac{HDD_{accumulated}}{K}
$$

### Remaining fuel estimate

$$
remaining = capacity - gallons_{used}
$$

Where capacity is the effective tank capacity (total capacity minus a reserve buffer).

### Triggering a delivery

Dispatch a delivery when:

$$
remaining \leq reserve_{threshold}
$$

Typically, reserve threshold is 25-30% of capacity to avoid run-outs.

---

## Configuration steps

### 1. Set base temperature

- **Standard**: 65°F for most residential heating
- **Adjustments**: Some buildings may use 60°F or 68°F based on insulation, thermostat settings, or climate
- **Configuration**: Set at the service or system level

### 2. Determine K-factor

**Initial K-factor** (before delivery history):

- Use industry defaults: 4.0–6.0 for typical homes
- Adjust based on building type:
  - Well-insulated modern homes: 6.0–8.0 (higher K = less consumption per HDD)
  - Older, poorly insulated: 3.0–4.5
  - Commercial buildings: Varies widely; consult historical data or energy audit

**Computed K-factor** (after deliveries):

- After 2-3 deliveries, compute K-factor from actual consumption:
  - Gallons delivered = amount filled
  - HDD accumulated = sum of degree days between deliveries
  - $K = \frac{HDD}{gallons}$
- Update the service's K-factor with the computed value for better accuracy

### 3. Initialize the system

On the first delivery (or when setting up):

- **Last delivery date**: Date of most recent fill
- **Last delivery amount**: Gallons delivered
- **Starting tank level**: Estimated gallons remaining before the delivery (if known)

The system will start accumulating degree days from the last delivery date.

### 4. Set minimum days between deliveries

Prevent too-frequent deliveries by setting a minimum interval (e.g., 14 or 21 days). Even if degree day projections suggest a delivery is needed, the system will wait until the minimum interval passes.

### 5. Set trigger thresholds

Define when to dispatch:

- **Reserve threshold**: Gallons or percentage remaining (e.g., 25% or 75 gallons for a 275-gallon tank)
- **Lookahead days**: Optional; trigger delivery if projection shows run-out within X days

---

## Example scenario

- **Customer**: Residential, 275-gallon oil tank
- **Base temp**: 65°F
- **K-factor**: 5.0 (initial estimate)
- **Last delivery**: January 1, filled 200 gallons
- **Effective capacity**: 250 gallons (reserve 25 gallons)
- **Reserve threshold**: 75 gallons

**Degree day accumulation** (simplified):

- Jan 1–15: 300 HDD
- Estimated gallons used: 300 / 5 = 60 gallons
- Remaining: 250 - 60 = 190 gallons (above threshold, no delivery yet)

- Jan 16–31: Another 300 HDD (total 600 HDD)
- Estimated gallons used: 600 / 5 = 120 gallons
- Remaining: 250 - 120 = 130 gallons (still above 75-gallon threshold)

- Feb 1–15: Another 300 HDD (total 900 HDD)
- Estimated gallons used: 900 / 5 = 180 gallons
- Remaining: 250 - 180 = 70 gallons (**below 75-gallon threshold**)
- **Trigger delivery**

---

## Degree day data sources

- **National Weather Service (NWS)**: Historical and forecast degree days
- **NOAA**: Free weather data APIs
- **Third-party services**: Commercial degree day providers with regional accuracy
- **Internal tracking**: Some systems calculate degree days from local weather station data

Your system may automatically pull degree day data but at any time you can update or modify it.

---

## Improving accuracy

### We Recompute K-factor regularly

- After each delivery, our system automatically recalculates K-factor and uses rolling averages of the last 3-5 deliveries to smooth out anomalies (e.g., vacations, extreme weather).

### Adjust for customer behavior

- **Vacation adjustments**: If a customer is away, consumption drops; this should be noted within the customer account or routing notes to avoid under-delivery
- **Building changes**: New insulation, windows, or equipment can alter K-factor significantly

### Combine with monitors

- If a tank monitor is installed, use **both** degree day projections and real-time level data
- Degree day provides a forecast; monitor provides ground truth
- If monitor reading diverges from degree day estimate, adjust K-factor or investigate consumption anomalies

See: [Monitoring Guide](./monitoring.md)

---

## Troubleshooting common issues

### Projections are too conservative (deliveries too frequent)

- **Cause**: K-factor is too low (overestimating consumption)
- **Fix**: Increase K-factor based on actual delivery history

### Projections are too aggressive (customer runs out)

- **Cause**: K-factor is too high (underestimating consumption)
- **Fix**: Decrease K-factor; consider lowering reserve threshold

### Degree days don't match consumption

- **Cause**: Weather data source is not local enough, or customer behavior changed
- **Fix**: Use more localized weather data by changing the weather location within the admin settings.

### Sudden change in usage

- **Cause**: Equipment failure, thermostat adjustment, occupancy change, or building modification
- **Fix**: Investigate and adjust K-factor or note the anomaly; monitor closely

---

## Related guides

- [Window Strategies](./window-strategies.md) — Add time-of-day constraints to degree day deliveries
- [Calendar Scheduling](./calendar-system.md) — Combine recurring schedules with degree day
- [Monitoring](./monitoring.md) — Use monitors to validate degree day estimates
- [Add Services](../add-services.md) — Apply degree day strategy to services
- [Adding Equipment](./adding-equipment.md) — Track tank capacity for accurate projections

# Understanding Fuel Monitoring

Tank monitoring systems use IoT devices to track fuel levels in real time. This guide explains how to link monitors to equipment, view monitor data in the app (Systems carousel), and use monitor data for delivery scheduling.

---

## What is tank monitoring?

Tank monitoring devices:

- **Measure** fuel level in the tank (percentage, gallons, or both)
- **Transmit** data wirelessly to the cloud (cellular, WiFi, or LoRa)
- **Alert** when levels drop below thresholds or when the device goes offline
- **Integrate** with delivery scheduling to trigger automatic refills

---

## Where to see monitor info in the app

You can quickly see monitor information from the customer dashboard:

### Systems carousel

- Go to the Customer Dashboard for a location
- In the Systems section, use the horizontal carousel to browse systems
- Select a system to view its details at-a-glance

When a system is linked to a tank monitor, the selector shows:

- **Tank Level (%)**: Current level from the monitor (visual gauge/knob)
- **Estimated Gallons Remaining**: Calculated from the current level and recommended capacity
- **Target/Window Dates**: Projected delivery window or target date
  - For a monitored system, dates are still shown; the level drives urgency
  - For non-monitored systems, dates are converted from degree days

Tip: You can toggle the date/time display per system to reveal window start/end and target dates.

---

## How the selector computes gallons remaining

The system selector calculates gallons remaining using the best available data:

1. **If a monitor reading is available**
   - Uses the monitor's current level (e.g., 0.42 for 42%)
   - Multiplies by the system's recommended delivery capacity
   - Formula: `gallons = monitorLevel * recommendedCapacity`

2. **If no monitor reading is available (fallback)**
   - Estimates consumption using degree days since the last full delivery
   - Applies seasonal usage rates (winter/summer) across the heating year
   - Subtracts estimated consumption from recommended capacity to estimate remaining gallons

This mirrors the in-app logic: monitor values take precedence; otherwise, the degree-day model provides a reasonable estimate.

---

## Linking a monitor to a tank

1. Navigate to **Systems** from the Customer Dashboard.
2. Select the system and find the tank equipment.
3. Click **Edit** on the tank.
4. In the **Monitoring** section:
   - **Monitor Link**: Select or add the monitor
   - **Monitor ID / Serial**: Device serial number or identifier (required for monitored strategy)
   - **Monitor Type**: Float gauge, ultrasonic, pressure sensor, etc.
   - **Vendor**: Tank monitor vendor/brand
5. Save the equipment.

The monitor is now linked and data will flow into the system and the Systems carousel.

---

## Using monitor data for scheduling

To have the system automatically trigger deliveries based on monitor readings:

1. Edit the system (Add/Edit System dialog)
2. Set **Window Strategy** to **Monitored**
3. Enter the **Monitor Serial Number** (required)
4. Enable the **Auto Delivery** toggle

- Deliveries are triggered when the level drops below configured thresholds (per your operations policy)
- If the monitor goes offline, the system can fall back to degree day estimates (see Window Strategies)

See: [Window Strategies](./window-strategies.md)

---

## Combining monitors with other strategies

Monitoring works best alongside other estimation methods:

### Monitored (primary) + Degree Day (fallback)

- **Primary**: Tank monitor provides ground-truth level
- **Fallback**: If monitor goes offline/stale, switch to degree day estimates
- **Benefit**: Reliable automation with a safe backup

### Calendar + Monitor (validation)

- **Primary**: Recurring calendar schedule
- **Validation**: Skip a scheduled delivery if the monitor shows the tank is still sufficiently full
- **Benefit**: Avoids unnecessary trips and overfilling

For configuring priorities, see: [Window Strategies](./window-strategies.md)

---

## Monitor data fields

### Real-time readings

- **Current Level (%)**: Percentage of tank capacity (drives the gauge and gallon estimates)
- **Current Level (gallons)**: Calculated gallons based on capacity
- **Last Reading Timestamp**: When the last data transmission occurred
- **Signal Strength**: Cellular or WiFi signal quality
- **Battery Level**: Monitor battery status (for battery-powered units)

### Historical data

- **Reading History**: Chart or log of level over time
- **Consumption Trend**: Rate of fuel usage derived from readings
- **Anomalies**: Sudden drops (delivery) or unusual patterns (leaks, sensor issues)

---

## Offline behavior and stale readings

Monitors can go offline due to signal loss, power failure, or damage. Configure fallback behavior:

### Stale reading grace period

- **Grace period**: Allow readings to be X hours old before marking as stale (e.g., 48 hours)
- **Alert**: Notify dispatch or service tech if the monitor hasn't reported within the grace period
- **Fallback strategy**:
  - Switch to degree day projections (if configured)
  - Switch to manual calendar scheduling
  - Flag customer for manual review

### Status indicators (implementation-dependent)

- **Red/Offline**: No reading in grace period; investigate
- **Yellow/Stale**: Reading is aging; monitor closely
- **Green/Active**: Recent reading within normal bounds

---

## Calibration and accuracy

### Initial calibration

When installing a monitor:

1. Fill the tank to a known level (e.g., 100% after delivery)
2. Calibrate the monitor to read that level accurately
3. Verify readings against gauge or stick measurements for a few weeks

### Ongoing calibration

- Every so often after a delivery, compare:
  - Gallons delivered (from ticket)
  - Pre-delivery monitor reading
  - Post-delivery monitor reading

This can be done with the rate checking feature on the fuel deliveries page [Fuel Deliveries](./somefueldeliverieslinkoneday.md)

- If discrepancies > 5%, recalibrate or investigate:
  - Tank shape irregularities
  - Sensor placement
  - Fuel expansion/contraction with temperature

### Common accuracy issues

- **Fuel temperature**: Cold fuel contracts, warm fuel expands; some monitors compensate, others don't
- **Tank tilt**: Uneven ground can skew float gauge readings
- **Sediment buildup**: Bottom of tank may have sludge reducing effective capacity
- **Sensor drift**: Over time, sensors may need recalibration

---

## Troubleshooting from the Systems carousel

- **No percent shown**: Ensure the system is set to the Monitored strategy and a serial number is saved
- **Gallons remaining seems off**: Verify the tank's recommended capacity on the equipment record
- **Dates look odd**: If not monitored, dates are computed from degree days; check winter/summer usage rates
- **Cannot see schedules**: Calendar schedules are managed in the system dialog; save the system first

---

## Related guides

- [Adding Equipment](./adding-equipment.md) — Set up tank equipment to link monitors
- [Degree Day System](./degree-day-system.md) — Used as fallback and for estimation without monitors
- [Window Strategies](./window-strategies.md) — Configure Monitored primary and fallbacks
- [Calendar Scheduling](./calendar-system.md) — Use with monitor validation to skip unnecessary deliveries
- [Add Services](../add-services.md) — Apply strategies via system configuration

# Creating a Calendar Schedule

# Calendar Scheduling Guide

Calendar scheduling creates time-based recurring delivery requests based on configured rules. This guide explains how to set up, manage, and use calendar schedules within the system dialog.

---

## What is calendar scheduling?

Calendar scheduling automatically generates delivery requests based on:

- **Recurring frequency**: How often deliveries occur (days, weeks, months, years)
- **Preferred day of week**: Optional constraint (Monday–Sunday or any day)
- **Seasonal dates**: Start and end dates for seasonal schedules
- **Requested volume**: Specific gallon amount per delivery (optional)
- **Active status**: Only one schedule can be active at a time

Calendar schedules are configured in the **Calendar Schedules tab** of the Add/Edit System dialog.

---

## When to use calendar scheduling

Use calendar scheduling for:

- **Commercial accounts** with predictable usage patterns
- **Regular delivery schedules** (e.g., every 2 weeks, monthly)
- **Seasonal deliveries** (e.g., heating season only: October–April)
- **Fixed-volume deliveries** (e.g., always deliver 200 gallons)
- **Customers preferring consistent schedules** over automatic keep-full

---

## Accessing calendar schedules

Calendar schedules are managed in the System dialog:

1. Navigate to **Systems** from the Customer Dashboard
2. Click **Add System** (for new) or **Edit** on an existing system
3. Select the **Calendar Schedules** tab
4. You'll see an info message: "Calendar schedules automatically create delivery requests based on recurring rules. Only one schedule can be active at a time."

**Important**: For new systems, you must save the system first before you can add calendar schedules. You'll see a warning: "Save the system before managing calendar schedules."

---

## Creating a calendar schedule

### Step 1: Click "Add Schedule"

After saving the system, click the **Add Schedule** button to open the schedule form.

### Step 2: Configure frequency

The form has three frequency fields in the first row:

**Frequency (number)**
- Numeric value (minimum 1, step 1)
- Example: 2 for "every 2 [units]"

**Units (dropdown)**
- Days
- Weeks
- Months
- Years

**Preferred Day of Week (dropdown, optional)**
- Monday through Sunday
- Or leave blank/clear for "Any day"
- Placeholder shows "Any day" when not selected

**Example combinations:**
- Frequency: 2, Units: Weeks, Day: Tuesday = "Every 2 weeks on Tuesday"
- Frequency: 1, Units: Months, Day: (Any) = "Every month on any day"
- Frequency: 14, Units: Days, Day: (Any) = "Every 14 days"

### Step 3: Set seasonal dates (optional)

**Season Starts** and **Season Ends**
- Date pickers with icon and button bar
- Both are optional
- Use to limit schedule to specific time periods (e.g., heating season)
- Leave blank for year-round schedules

**Example:**
- Season Starts: October 1
- Season Ends: April 30
- Schedule only generates deliveries during this window

### Step 4: Set delivery volume (optional)

**Requested Volume (gal)**
- Numeric field (minimum 0)
- Specify gallons to request per delivery
- Leave at 0 or blank for automatic calculation

### Step 5: Set active status

**Active toggle**
- For new schedules: Label shows "Activate after saving"
- For existing schedules: Label shows "Active"
- Toggle on to make this schedule the active one
- **Important**: Only one schedule can be active at a time; activating a new schedule deactivates others

### Step 6: Save the schedule

Click **Create Schedule** (or **Update Schedule** if editing).

The schedule will appear in the list below with:
- Frequency description (e.g., "Every 2 weeks")
- Active/Inactive badge (green for active, gray for inactive)
- Season, day of week, and requested volume details

---

## Managing existing schedules

Each schedule in the list displays:

**Header:**
- **Frequency**: Human-readable format (e.g., "Every 2 weeks", "Every month")
- **Status badge**: Active (green) or Inactive (gray)

**Details:**
- **Season**: Date range or "No seasonal restrictions"
- **Day of week**: Specific day or "Any day"
- **Requested volume**: "XXX gal" or "Not specified"

**Action buttons:**

- **Set Active** (green checkmark icon)
  - Makes this schedule the active one
  - Automatically deactivates other schedules
  - Disabled if already active or if another action is in flight

- **Deactivate** (pause icon, outlined)
  - Pauses the schedule without deleting it
  - Disabled if already inactive or if another action is in flight

- **Edit** (pencil icon)
  - Opens the schedule in edit mode
  - Same form as creating, with existing values populated
  - Click "Update Schedule" to save changes

- **Delete** (trash icon, text style, danger color)
  - Removes the schedule permanently
  - Typically shows a confirmation dialog

---

## Schedule states and rules

### Only one active schedule

The system enforces that only one calendar schedule can be active at a time per system:
- Setting a schedule to Active automatically deactivates all others
- You can have multiple inactive schedules saved for future use or seasonal switching

### Inactive schedules

Inactive schedules:
- Are saved but do not generate delivery requests
- Can be activated later
- Useful for seasonal switching (e.g., winter schedule vs. summer schedule)

### Loading and error states

- **Loading**: Shows spinner with "Loading schedules…" message
- **No schedules**: Shows info message "No calendar schedules have been created yet."
- **Error**: Shows error message with details if schedules fail to load

---

## Example configurations

### Example 1: Biweekly residential delivery

**Configuration:**
- Frequency: 2
- Units: Weeks
- Preferred Day: Tuesday
- Season Starts: (blank)
- Season Ends: (blank)
- Requested Volume: 200
- Active: Yes

**Result**: Delivers 200 gallons every 2 weeks on Tuesday, year-round

### Example 2: Monthly commercial fill

**Configuration:**
- Frequency: 1
- Units: Months
- Preferred Day: (Any day)
- Season Starts: (blank)
- Season Ends: (blank)
- Requested Volume: 0 (automatic)
- Active: Yes

**Result**: Monthly delivery on any day with automatic volume calculation

### Example 3: Seasonal heating oil

**Configuration:**
- Frequency: 3
- Units: Weeks
- Preferred Day: (Any day)
- Season Starts: October 1
- Season Ends: April 30
- Requested Volume: 0
- Active: Yes

**Result**: Delivery every 3 weeks during heating season only (Oct 1 – Apr 30)

---

## Editing a schedule

1. Click **Edit** on the schedule you want to modify
2. The schedule form appears with current values populated
3. Make your changes
4. Click **Update Schedule** to save
5. The schedule list updates with the new configuration

You can change any field: frequency, units, day of week, seasonal dates, volume, or active status.

---

## Switching between schedules

To switch from one schedule to another (e.g., winter to summer):

1. Create both schedules with appropriate settings
2. Set one as Active (this becomes the current schedule)
3. When the season changes, click **Set Active** on the other schedule
4. The system automatically deactivates the previous schedule and activates the new one

**Example:**
- Winter schedule: Every 2 weeks, Oct 1 – Apr 30, Active
- Summer schedule: Every 6 weeks, May 1 – Sep 30, Inactive
- In May, click **Set Active** on the summer schedule

---

## Deleting a schedule

Click **Delete** on a schedule to remove it permanently. This action typically requires confirmation and cannot be undone.

**Caution**: Deleting an active schedule stops automatic delivery generation. Ensure you have another schedule activated or switch the system to a different window strategy.

---

## Calendar schedules and window strategies

Calendar scheduling is selected as a **Window Strategy** at the system level:

1. In the system dialog, set **Window Strategy** to **Calendar**
2. Enable **Auto Delivery** toggle if you want automatic delivery request generation
3. Configure your calendar schedule(s) in the Calendar Schedules tab

With Auto Delivery enabled and an active calendar schedule, the system will automatically generate delivery requests based on the configured frequency and rules.

See: [Window Strategies](./window-strategies.md)

---

## Combining calendar with other data

### Calendar + Monitor validation

If the system also has a tank monitor:
- The calendar schedule generates requests on schedule
- Monitor data can be used to validate or skip deliveries if the tank is still sufficiently full
- This prevents unnecessary deliveries while maintaining a predictable schedule

### Calendar + Degree Day cross-check

If degree day settings are also configured:
- Calendar provides the schedule cadence
- Degree day projections can validate whether the delivery is needed
- Helps refine the calendar frequency over time

---

## Troubleshooting

### Cannot add schedules

**Issue**: "Save the system before managing calendar schedules" warning appears
**Solution**: Click Submit on the system dialog to save the system first, then edit it again to add schedules

### Schedule not generating deliveries

**Issue**: Schedule is created but deliveries aren't happening
**Check:**
- Is the schedule set to Active? (green badge)
- Is Auto Delivery toggle enabled on the system?
- Is the current date within the seasonal date range (if specified)?
- Is Window Strategy set to Calendar?

### Wrong delivery frequency

**Issue**: Deliveries happening too often or too infrequently
**Solution**: Edit the schedule and adjust Frequency/Units to match desired cadence

### All schedules become inactive

**Issue**: Activating a schedule deactivates all others
**Expected behavior**: This is by design. Only one schedule can be active at a time.

---

## Related guides

- [Window Strategies](./window-strategies.md) — Select Calendar as window strategy and enable Auto Delivery
- [Add System](../add-system.md) — Create the system before adding calendar schedules
- [Monitoring](./monitoring.md) — Use monitor validation with calendar schedules
- [Degree Day System](./degree-day-system.md) — Cross-check calendar with degree day projections