# Test Scenario Guide: Price Offer Splitting (MOD-01 | FR-004)

### 1. Scenario Introduction 🏢
In the **rzain ERP - Maintenance Management** module, corrective maintenance requests often involve a high volume of spare parts (sometimes exceeding 200 items). Previously, the system treated a price offer as a single monolithic entity. The **Price Offer Splitting** feature introduces the ability to partition a single corrective offer into multiple independent sub-quotations. 

This is critical for:
* **Phased Billing:** Invoicing the client for completed phases (e.g., HVAC vs. Electrical).
* **Supplier Separation:** Grouping items based on different lead times or vendors.
* **Partial Approvals:** Allowing the client to approve essential repairs while deferring others.

---

### 2. Technical Schema Changes 🔍

#### **Endpoint:** `POST /maintenance/price-offers/{id}/split`

* **Validation Rules:**
    * `splits`: **Required** array. Minimum of **2 splits** must be provided.
    * `splits.*.title`: **Required** string. Label for the specific sub-quotation (e.g., "Phase 1 - Urgent").
    * `splits.*.item_ids`: **Required** array. List of part IDs to include in the split.
    * **Integrity Checks:** 
        * All `item_ids` must exist in the `mod_maintenance_price_offer_parts` table.
        * All `item_ids` must belong to the **parent offer**.
        * **No Overlap:** An item cannot appear in more than one split.
        * **Total Coverage:** Every item in the parent offer must be assigned to exactly one split (no orphaned items).
        * **Status Check:** Offer cannot be split if it is already in `split`, `approved_by_customer`, or `waiting_customer_response` (already sent) status.

* **Response Changes (MaintenancePriceOfferResource):**
    * Added `is_split`: **Boolean**. Returns `true` if the offer has been partitioned.
    * Added `split_children`: **Array**. List of sub-offers containing `id`, `split_title`, `status`, and `total`.

---

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

| Feature | Previous Behavior | New Behavior |
| :--- | :--- | :--- |
| **Offer Structure** | Single offer only. | Supports **Parent-Child** hierarchy via `parent_offer_id`. |
| **Billing Flexibility** | Client must approve/reject the whole list. | Client can approve sub-offers **independently**. |
| **Data Integrity** | No status for split offers. | New `split` status makes the parent **read-only** for history. |
| **Naming** | Standard sequential numbering. | Sub-offers use **Alpha-suffixes** (e.g., `QT-2026-0042-A`). |

---

### 4. API Endpoints Table 📊

| Process | Endpoint | Method | Description |
| :--- | :--- | :--- | :--- |
| **Split Quotation** | `/api/v1/maintenance/price-offers/{id}/split` | `POST` | Partitions an offer into multiple sub-offers based on item selection. |
| **List History** | `/api/v1/maintenance/price-offers/{id}/versions` | `GET` | View parent and sub-offer relationships. |

---

### 5. Test Cases 🧪

#### **[Case 1] The Happy Path (Standard Split)**
* **Input:** A corrective offer with 10 spare parts. Send a request with 2 splits: "Urgent" (4 items) and "Standard" (6 items).
* **Expected Result:**
    1. Parent offer status changes to `split`.
    2. Two new offers created in `draft` status.
    3. New totals recalculated for each sub-offer (Subtotal + Tax).
    4. Sub-offers assigned codes `QT-XXXX-A` and `QT-XXXX-B`.

#### **[Case 2] Backward Compatibility**
* **Input:** Viewing an old price offer created before the split feature.
* **Expected Result:**
    1. API response includes `is_split: false`.
    2. `split_children` returns an empty array or `null`.
    3. Existing approval workflows for these offers remain functional.

#### **[Case 3] Edge Cases & Constraints**
* **Validation Failure (Overlapping Items):** Attempt to include Part ID `5` in both Split A and Split B. 
    * *Result:* `422 Unprocessable Entity` with message "Item ID 5 appears in more than one split."
* **Validation Failure (Orphaned Items):** Attempt to split an offer with 20 items but only provide 15 items in the split request.
    * *Result:* `422 Unprocessable Entity` with message "All items in original offer must be covered by splits."
* **Forbidden Action (Double Split):** Attempt to call `/split` on an offer that already has the status `split`.
    * *Result:* `422 Unprocessable Entity` with message "Cannot split an offer with status: Split."

---
