# Test Scenario Guide: Multi-Year Annual Visit Planning 🗓️

### 1. Scenario Introduction
The **Multi-Year Annual Visit Planning** enhancement addresses a critical limitation where maintenance visit plans were previously constrained to a single calendar year. In real-world service management, maintenance contracts often span multiple years or start mid-year, requiring scheduling that crosses year boundaries (e.g., a plan covering December 2025 and January 2026).

By decoupling the visit year from the parent plan's reference year and moving it to the individual **Visit Item** level, the system now provides the flexibility to map out a complete maintenance lifecycle for any given contract addendum, regardless of its duration.

---

### 2. Technical Schema Changes (Detailed) 🔍

* **Endpoint:** `POST /api/v1/annual-visit-plans` and `PATCH /api/v1/annual-visit-plans/{id}`
* **Validation Changes:**
    * **`plan_year`**: Changed from **required** to **nullable**. If not provided, the system intelligently defaults to the minimum year found within the `items` array.
    * **`items.*.year`**: Now **required**. Each visit item must explicitly state its year to support cross-year planning.
    * **`items.*.month`**: Remains **required** (1-12).
* **Response Changes:**
    * **`items.*.year`**: Now explicitly returned in the JSON response for each item.
    * **Summary View**: The `summary` endpoint now returns a month-by-month breakdown covering the **entire contract duration**, rather than a fixed 12-month window.
    * **Distribution View**: Similarly expanded to reflect the full lifecycle of the scoped contract/addendum.

---

### 3. Why the Update? (Change Log) 🔄

| Feature | Previous Behavior | New Behavior | Benefit |
| :--- | :--- | :--- | :--- |
| **Year Scoping** | Shared `plan_year` for all items. | Per-item `year` property. | Flexibility for multi-year contracts. |
| **Summary Window** | Fixed 12-month window from `plan_year`. | Dynamic window based on contract dates. | Complete visibility of required vs. existing visits. |
| **Validation** | Items outside `plan_year` were rejected. | Items validated against contract start/end. | Data integrity across year boundaries. |
| **File Extraction** | Only extracted month and count. | Extracts year, month, and count. | Reduced manual data entry from Excel uploads. |

---

### 4. API Endpoints Table 📊

| Process | Endpoint | Method | Description |
| :--- | :--- | :--- | :--- |
| Create Draft Plan | `/api/v1/annual-visit-plans` | `POST` | Saves a multi-year draft plan |
| Update Draft Plan | `/api/v1/annual-visit-plans/{id}` | `PATCH` | Modifies items across multiple years |
| Review Summary | `/api/v1/annual-visit-plans/{id}/summary` | `GET` | Multi-year audit of planned vs. backlog visits |
| Distribution View | `/api/v1/annual-visit-plans/{id}/distribution` | `GET` | Full contract lifecycle distribution map |
| Parse Schedule | `/api/v1/annual-visit-plans/upload` | `POST` | Extracts multi-year data from Excel templates |

---

### 5. Test Cases 🧪

#### **[Case 1] The Happy Path (Standard Success)**
* **Input:** A plan containing items for `2025-12` and `2026-01` scoped to a contract spanning those years.
* **Expected Result:** Plan is saved successfully. The `summary` endpoint correctly displays both months with their respective `required_visits` and `status_indicator`.

#### **[Case 2] Backward Compatibility**
* **Input:** Loading a plan created before this update (where `year` might be missing in the `mod_annual_visit_plan_items` table).
* **Expected Result:** The system utilizes the backfill logic (populated via migration) to use the parent `plan_year` as the default for legacy items. API responses remain consistent.

#### **[Case 3] Edge Cases & Constraints**
* **Out of Bounds Year:** Attempt to save a visit for `2027` when the contract ends in `2026`.
    * *Expected:* Validation fails with: `"Month [X/2027] is outside contract period..."`
* **Missing Plan Year:** Send a request with `plan_year: null` but valid `items` for `2025`.
    * *Expected:* Success. The parent `plan_year` is automatically set to `2025`.
* **Zero Visit Warning:** A month within the contract period is omitted in the multi-year plan.
    * *Expected:* The `summary` endpoint returns a `warning` for that specific month in the `warnings` array.

---
