# Volume Calculation Feature - Testing Documentation

**Last Updated:** February 3, 2026  
**Feature:** Equipment Planner with Volume Calculation  
**Version:** 1.0

---

## Table of Contents
1. [Overview](#overview)
2. [API Endpoints](#api-endpoints)
3. [Testing Scenarios](#testing-scenarios)
4. [cURL Examples](#curl-examples)
5. [Postman Collection](#postman-collection)
6. [Expected Responses](#expected-responses)

---

## Overview

The Volume Calculation feature tracks planned vs actual volumes for equipment planning. It calculates volumes based on:
- **Equipment Category:** GRAN_FBP_BLEND or COMP_COAT_CAP_IMP
- **Product-Option Combination:** Combined format (e.g., "SB_Option1_Strength(10)")
- **Time-Based Calculation:** For in-progress batches, calculates proportional volume based on elapsed time

---

## API Endpoints

### 1. Get Available Products (Dropdown)

**Endpoint:** `GET /equipment-planner/available-products`

**Purpose:** Get all available products for dropdown selection

**Query Parameters:** None

**Success Response (200):**
```json
{
  "success": true,
  "data": [
    { "label": "AMT", "value": "AMT" },
    { "label": "ATR", "value": "ATR" },
    { "label": "BUP", "value": "BUP" },
    { "label": "CAF", "value": "CAF" },
    { "label": "CBZ", "value": "CBZ" },
    { "label": "CEL", "value": "CEL" },
    { "label": "COL", "value": "COL" },
    { "label": "DOM", "value": "DOM" },
    { "label": "EMT", "value": "EMT" },
    { "label": "FAM", "value": "FAM" },
    { "label": "FLU", "value": "FLU" },
    { "label": "GRN", "value": "GRN" },
    { "label": "IBU", "value": "IBU" },
    { "label": "MET", "value": "MET" }
  ],
  "count": 14
}
```

---

### 2. Get Product Volume Options (Dropdown)

**Endpoint:** `GET /equipment-planner/product-volume-options`

**Purpose:** Get all available volume options for a product

**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| product_code | string | Yes | Product code (e.g., AMT, ATR, BUP) |

**Success Response (200):**
```json
{
  "success": true,
  "data": {
    "product_code": "AMT",
    "options": [
      {
        "id": 1,
        "label": "SB_Option1_Strength(10)",
        "value": "SB_Option1_Strength(10)",
        "num_batches": 1,
        "volume_gran_fbp_blend": 5.0,
        "volume_comp_coat_cap_imp": 5
      },
      {
        "id": 2,
        "label": "CB_Option1_Strength(25)",
        "value": "CB_Option1_Strength(25)",
        "num_batches": 4,
        "volume_gran_fbp_blend": 7.0,
        "volume_comp_coat_cap_imp": 1.15
      }
    ]
  }
}
```

**Error Response (400):**
```json
{
  "success": false,
  "message": "product_code is required"
}
```

---

### 3. Get Volume for Specific Option

**Endpoint:** `GET /equipment-planner/volume-for-option`

**Purpose:** Get volume details for a product-option-equipment combination

**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| product_code | string | Yes | Product code (e.g., AMT, ATR) |
| option_selected | string | No | Combined option (e.g., SB_Option1_Strength(10)) |
| equipment_id | string | No | Equipment ID to determine volume category |

**Success Response (200):**
```json
{
  "success": true,
  "data": {
    "product_code": "AMT",
    "option_selected": "SB_Option1_Strength(10)",
    "num_batches": 1,
    "volume_gran_fbp_blend": 5.0,
    "volume_comp_coat_cap_imp": 5,
    "equipment_id": "PRE064",
    "volume_category": "COMP_COAT_CAP_IMP",
    "selected_volume": 5
  }
}
```

**Error Response (404):**
```json
{
  "success": false,
  "message": "No volume configuration found for this option"
}
```

---

### 4. Create Equipment Plan

**Endpoint:** `POST /equipment-planner/create-equipment-planner`

**Purpose:** Create new equipment plan records (single or bulk)

**Request Body - Single Record:**
```json
{
  "eqp_id": "PRE064",
  "eqp_activity": "Mixing",
  "eqp_plan_type": "operation",
  "product_name": "AMT",
  "dispensing_request_id": "DR-001",
  "start_date": "2026-02-03 08:00",
  "end_date": "2026-02-03 12:00",
  "option_selected": "SB_Option1_Strength(10)"
}
```

**Request Body - Bulk Insert (Array):**
```json
[
  {
    "eqp_id": "PRE064",
    "eqp_activity": "Mixing",
    "eqp_plan_type": "operation",
    "product_name": "AMT",
    "dispensing_request_id": "DR-001",
    "start_date": "2026-02-03 08:00",
    "end_date": "2026-02-03 12:00",
    "option_selected": "SB_Option1_Strength(10)"
  },
  {
    "eqp_id": "PRE035",
    "eqp_activity": "Blending",
    "eqp_plan_type": "operation",
    "product_name": "BUP",
    "dispensing_request_id": "DR-002",
    "start_date": "2026-02-03 13:00",
    "end_date": "2026-02-03 16:00",
    "option_selected": "CB_Option1_Strength(25)"
  }
]
```

**Success Response (200):**
```json
{
  "success": true,
  "message": "Equipment planner created successfully (draft)",
  "data": {
    "id": 1,
    "eqp_id": "PRE064",
    "eqp_activity": "Mixing",
    "eqp_plan_type": "operation",
    "product_name": "AMT",
    "product_batch": "DR-001",
    "start_date": "2026-02-03T08:00:00.000Z",
    "end_date": "2026-02-03T12:00:00.000Z",
    "option_selected": "SB_Option1_Strength(10)",
    "status": "draft",
    "plan_version": 0,
    "is_active": true,
    "createdAt": "2026-02-03T10:30:00.000Z",
    "updatedAt": "2026-02-03T10:30:00.000Z"
  }
}
```

**Error Response (400):**
```json
{
  "success": false,
  "message": "Request body array is empty, its should contain at least one record of equipment planner"
}
```

---

### 5. Get Equipment Plan

**Endpoint:** `GET /equipment-planner/get-equipment-planner`

**Purpose:** Retrieve planned equipment activities

**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| date | string | No | Date in DD-MM-YYYY format (e.g., "03-02-2026") or YYYY-MM-DD |
| equipment_id | string | No | Equipment ID filter (uses eqp_id) |
| version_id | number | No | Version ID for snapshot mode |

**Success Response (200):**
```json
{
  "success": true,
  "activities": [
    {
      "id": 1,
      "equipment_id": "PRE064",
      "equipment_name": "Comp-01",
      "activity_type": "manufacturing",
      "batch_number": "DR-001",
      "product_name": "AMT",
      "cleaning_type": null,
      "start_time": "2026-02-03 08:00:00.000 +05:30",
      "end_time": "2026-02-03 12:00:00.000 +05:30",
      "option_selected": "SB_Option1_Strength(10)"
    },
    {
      "id": 2,
      "equipment_id": "PRE035",
      "equipment_name": "Comp-02",
      "activity_type": "manufacturing",
      "batch_number": "DR-002",
      "product_name": "BUP",
      "cleaning_type": null,
      "start_time": "2026-02-03 13:00:00.000 +05:30",
      "end_time": "2026-02-03 16:00:00.000 +05:30",
      "option_selected": "CB_Option1_Strength(25)"
    }
  ],
  "meta": {
    "display_start_time": "2026-02-02T18:30:00.000Z",
    "display_end_time": "2026-02-03T18:30:00.000Z",
    "total_activities": 2,
    "version_id": null
  }
}
```

---

### 6. Get Equipment Activities by Location (Plan vs Actual with Volumes)

**Endpoint:** `GET /equipment-planner/activities-by-location`

**Purpose:** Get detailed comparison of planned vs actual activities with volume calculations

**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| start_date | string | Yes | Start date (YYYY-MM-DD or YYYY-MM-DD HH:mm) |
| end_date | string | Yes | End date (YYYY-MM-DD or YYYY-MM-DD HH:mm) |
| plan_version | number | No | Version to compare (default: latest) |

**Success Response (200):**
```json
{
  "success": true,
  "data": {
    "Comp-01": {
      "matched": [
        {
          "status": "matched",
          "plan_id": 1,
          "plan_product": "DR-001",
          "plan_product_name": "AMT",
          "plan_activity": "Mixing",
          "plan_start_date": "2026-02-03T08:00:00.000 +05:30",
          "plan_end_date": "2026-02-03T12:00:00.000 +05:30",
          "plan_hrs": "4:00",
          "plan_version": 1,
          "option_selected": "SB_Option1_Strength(10)",
          "actual_id": 1,
          "equipment_id": "PRE064",
          "equipment_name": "Comp-01",
          "activity_type": "manufacturing",
          "actual_product": "DR-001",
          "product_name": "AMT",
          "actual_activity": "Mixing",
          "cleaning_type": null,
          "actual_start_time": "2026-02-03T08:05:00.000 +05:30",
          "actual_end_time": "2026-02-03T12:10:00.000 +05:30",
          "actual_hrs": "4:05",
          "master_volume": 5.0,
          "planned_volume": 5.0,
          "actual_volume": 5.0,
          "eqp_sequence": 1
        }
      ],
      "not_started": [
        {
          "status": "not_started",
          "plan_id": 2,
          "plan_product": "DR-003",
          "plan_product_name": "BUP",
          "plan_activity": "Granulation",
          "plan_start_date": "2026-02-03T14:00:00.000 +05:30",
          "plan_end_date": "2026-02-03T18:00:00.000 +05:30",
          "plan_hrs": "4:00",
          "plan_version": 1,
          "option_selected": "CB_Option1_Strength(25)",
          "actual_id": null,
          "equipment_id": "PRE064",
          "equipment_name": "Comp-01",
          "activity_type": "manufacturing",
          "actual_product": null,
          "product_name": null,
          "actual_activity": null,
          "cleaning_type": null,
          "actual_start_time": null,
          "actual_end_time": null,
          "actual_hrs": null,
          "master_volume": 1.15,
          "planned_volume": 1.15,
          "actual_volume": 0,
          "eqp_sequence": 1
        }
      ],
      "unplanned": [
        {
          "status": "unplanned",
          "plan_id": null,
          "plan_product": null,
          "plan_product_name": null,
          "plan_activity": null,
          "plan_start_date": null,
          "plan_end_date": null,
          "plan_hrs": null,
          "plan_version": null,
          "option_selected": null,
          "actual_id": 2,
          "equipment_id": "PRE064",
          "equipment_name": "Comp-01",
          "activity_type": "cleaning",
          "actual_product": "CLEANING",
          "product_name": null,
          "actual_activity": "Type A",
          "cleaning_type": "Type A",
          "actual_start_time": "2026-02-03T12:15:00.000 +05:30",
          "actual_end_time": "2026-02-03T13:45:00.000 +05:30",
          "actual_hrs": "1:30",
          "master_volume": null,
          "planned_volume": null,
          "actual_volume": null,
          "eqp_sequence": 1
        }
      ],
      "activity_timeline": [
        { "... all matched, not_started, unplanned merged chronologically ..." }
      ],
      "unutilized_time": "6:30",
      "production_variance": "0.08",
      "eqp_sequence": 1
    }
  },
  "meta": {
    "plan_version_used": 1,
    "latest_version": 1,
    "source_table": "equipment_planner",
    "query_start": "2026-02-03T00:00:00.000Z",
    "query_end": "2026-02-03T23:59:59.999Z"
  },
  "summary": {
    "total_matched": 1,
    "total_not_started": 1,
    "total_unplanned": 1,
    "total_activities": 3,
    "avg_variance": "0.08 hrs"
  }
}
```

---

### 7. Publish Equipment Plan

**Endpoint:** `POST /equipment-planner/publish`

**Purpose:** Publish all draft records and create historical snapshot

**Request Body:**
```json
{}
```

**Success Response (200):**
```json
{
  "success": true,
  "message": "Equipment plan published successfully",
  "new_version": 1,
  "records_published": 2,
  "published_at": "2026-02-03T10:45:00.000Z"
}
```

---

## Testing Scenarios

### Scenario 1: Basic Plan Creation and Retrieval

**Objective:** Create a simple plan and verify it returns correctly

**Steps:**
1. Create a single equipment plan
2. Retrieve the plan for the same date
3. Verify option_selected is present in response

**cURL Command:**
```bash
# Create plan
curl -X POST http://localhost:3000/equipment-planner/create-equipment-planner \
  -H "Content-Type: application/json" \
  -d '{
    "eqp_id": "PRE064",
    "eqp_activity": "Mixing",
    "eqp_plan_type": "operation",
    "product_name": "AMT",
    "dispensing_request_id": "DR-001",
    "start_date": "2026-02-03 08:00",
    "end_date": "2026-02-03 12:00",
    "option_selected": "SB_Option1_Strength(10)"
  }'

# Get plan for date
curl -X GET "http://localhost:3000/equipment-planner/get-equipment-planner?date=03-02-2026"
```

**Expected Results:**
- ✅ Plan created with status="draft"
- ✅ option_selected saved correctly
- ✅ Retrieved plan includes option_selected field
- ✅ activity_type normalized to "manufacturing"

---

### Scenario 2: Volume Dropdown and Selection

**Objective:** Test volume options dropdown and option selection

**Steps:**
1. Get product volume options
2. Select one option and get its volume details
3. Verify volume calculation for equipment category

**cURL Command:**
```bash
# Get volume options for AMT
curl -X GET "http://localhost:3000/equipment-planner/product-volume-options?product_code=AMT"

# Get volume for specific option and equipment
curl -X GET "http://localhost:3000/equipment-planner/volume-for-option?product_code=AMT&option_selected=SB_Option1_Strength(10)&equipment_id=PRE064"
```

**Expected Results:**
- ✅ Returns all options for AMT (9 variants)
- ✅ Each option has label matching value (e.g., "SB_Option1_Strength(10)")
- ✅ Volume returned matches equipment's volume_category:
  - If PRE064 is COMP_COAT_CAP_IMP → returns volume_comp_coat_cap_imp
  - If PRE064 is GRAN_FBP_BLEND → returns volume_gran_fbp_blend

---

### Scenario 3: Plan vs Actual Comparison with Volume

**Objective:** Verify volume calculations in plan vs actual comparison

**Steps:**
1. Create a plan (4 hours, 08:00-12:00)
2. Create actual activity log matching the plan
3. Get activities by location to see volume calculations
4. Verify: planned_volume and actual_volume

**cURL Command:**
```bash
# Create plan
curl -X POST http://localhost:3000/equipment-planner/create-equipment-planner \
  -H "Content-Type: application/json" \
  -d '{
    "eqp_id": "PRE064",
    "eqp_activity": "Mixing",
    "eqp_plan_type": "operation",
    "product_name": "AMT",
    "dispensing_request_id": "DR-TEST-001",
    "start_date": "2026-02-03 08:00",
    "end_date": "2026-02-03 12:00",
    "option_selected": "SB_Option1_Strength(10)"
  }'

# Publish plan
curl -X POST http://localhost:3000/equipment-planner/publish

# Create actual activity (via equipment_log_books)
# (This would be done through the equipment logging system)

# Get plan vs actual comparison
curl -X GET "http://localhost:3000/equipment-planner/activities-by-location?start_date=2026-02-03&end_date=2026-02-03"
```

**Expected Results:**
- ✅ Plan created with plan_version=0 initially
- ✅ After publish, plan_version increments to 1
- ✅ Matched activity shows:
  - planned_volume = master_volume (5.0)
  - actual_volume = master_volume (5.0) if completed
  - master_volume matches equipment's volume_category
- ✅ Not_started shows planned_volume but actual_volume=0

---

### Scenario 4: In-Progress Batch Volume Calculation

**Objective:** Verify time-based volume calculation for ongoing batches

**Details:**
- Plan duration: 4 hours (08:00-12:00)
- Actual start: 08:00
- Current time: 10:00 (50% elapsed)
- Expected actual_volume: ~2.5 (50% of 5.0)

**cURL Command:**
```bash
# Get activities with time-based calculation
curl -X GET "http://localhost:3000/equipment-planner/activities-by-location?start_date=2026-02-03&end_date=2026-02-03"
```

**Expected Results:**
- ✅ master_volume = 5.0
- ✅ actual_volume ≈ 2.5 (proportional based on elapsed time)
- ✅ Calculation: (5.0 / 240 minutes) × 120 minutes elapsed = 2.5

---

### Scenario 5: Bulk Create and Publish

**Objective:** Test bulk insert and multi-plan publishing

**Steps:**
1. Create multiple plans (different equipment, products)
2. Publish all at once
3. Verify all get same version number
4. Retrieve historical records

**cURL Command:**
```bash
# Bulk create plans
curl -X POST http://localhost:3000/equipment-planner/create-equipment-planner \
  -H "Content-Type: application/json" \
  -d '[
    {
      "eqp_id": "PRE064",
      "eqp_activity": "Mixing",
      "eqp_plan_type": "operation",
      "product_name": "AMT",
      "dispensing_request_id": "DR-BULK-001",
      "start_date": "2026-02-03 08:00",
      "end_date": "2026-02-03 12:00",
      "option_selected": "SB_Option1_Strength(10)"
    },
    {
      "eqp_id": "PRE035",
      "eqp_activity": "Blending",
      "eqp_plan_type": "operation",
      "product_name": "BUP",
      "dispensing_request_id": "DR-BULK-002",
      "start_date": "2026-02-03 13:00",
      "end_date": "2026-02-03 17:00",
      "option_selected": "CB_Option1_Strength(25)"
    }
  ]'

# Publish all
curl -X POST http://localhost:3000/equipment-planner/publish

# Get plan history for version 1
curl -X GET "http://localhost:3000/equipment-planner/plan-history?version=1"
```

**Expected Results:**
- ✅ Both records created with status="draft"
- ✅ Both option_selected fields saved
- ✅ Publish returns records_published=2
- ✅ Both copied to history with plan_version=1
- ✅ History shows option_selected preserved

---

### Scenario 6: Equipment Category Mapping

**Objective:** Verify correct volume column selection based on equipment category

**Test Data:**
- Equipment PRE064: volume_category = "COMP_COAT_CAP_IMP"
- Equipment PRE009: volume_category = "GRAN_FBP_BLEND"
- Product: AMT, Option: SB_Option1_Strength(10)
  - volume_comp_coat_cap_imp = 5
  - volume_gran_fbp_blend = 5.0

**cURL Commands:**
```bash
# Get volume for PRE064 (COMP_COAT_CAP_IMP)
curl -X GET "http://localhost:3000/equipment-planner/volume-for-option?product_code=AMT&option_selected=SB_Option1_Strength(10)&equipment_id=PRE064"

# Get volume for PRE009 (GRAN_FBP_BLEND)
curl -X GET "http://localhost:3000/equipment-planner/volume-for-option?product_code=AMT&option_selected=SB_Option1_Strength(10)&equipment_id=PRE009"
```

**Expected Results:**
- ✅ PRE064: selected_volume = 5 (from volume_comp_coat_cap_imp)
- ✅ PRE009: selected_volume = 5.0 (from volume_gran_fbp_blend)
- ✅ Both show correct volume_category in response

---

## cURL Examples

### Quick Test Commands

**0. Get Available Products**
```bash
curl -X GET "http://localhost:3000/equipment-planner/available-products"
```

**1. Create a Plan**
```bash
curl -X POST http://localhost:3000/equipment-planner/create-equipment-planner \
  -H "Content-Type: application/json" \
  -d '{
    "eqp_id": "PRE064",
    "eqp_activity": "Mixing",
    "eqp_plan_type": "operation",
    "product_name": "AMT",
    "dispensing_request_id": "DR-001",
    "start_date": "2026-02-03 08:00",
    "end_date": "2026-02-03 12:00",
    "option_selected": "SB_Option1_Strength(10)"
  }'
```

**2. Get Volume Options**
```bash
curl -X GET "http://localhost:3000/equipment-planner/product-volume-options?product_code=AMT"
```

**3. Get Specific Volume**
```bash
curl -X GET "http://localhost:3000/equipment-planner/volume-for-option?product_code=AMT&option_selected=SB_Option1_Strength(10)&equipment_id=PRE064"
```

**4. Get Plan for Date**
```bash
curl -X GET "http://localhost:3000/equipment-planner/get-equipment-planner?date=03-02-2026"
```

**5. Get Activities by Location (Plan vs Actual)**
```bash
curl -X GET "http://localhost:3000/equipment-planner/activities-by-location?start_date=2026-02-03&end_date=2026-02-03"
```

**6. Publish Plan**
```bash
curl -X POST http://localhost:3000/equipment-planner/publish \
  -H "Content-Type: application/json" \
  -d '{}'
```

---

## Postman Collection

Import this into Postman for easier testing:

```json
{
  "info": {
    "name": "Volume Calculation API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "0. Get Available Products",
      "request": {
        "method": "GET",
        "url": {
          "raw": "{{baseUrl}}/equipment-planner/available-products",
          "host": ["{{baseUrl}}"],
          "path": ["equipment-planner", "available-products"]
        }
      }
    },
    {
      "name": "1. Get Volume Options",
      "request": {
        "method": "GET",
        "url": {
          "raw": "{{baseUrl}}/equipment-planner/product-volume-options?product_code=AMT",
          "host": ["{{baseUrl}}"],
          "path": ["equipment-planner", "product-volume-options"],
          "query": [{ "key": "product_code", "value": "AMT" }]
        }
      }
    },
    {
      "name": "2. Get Volume for Option",
      "request": {
        "method": "GET",
        "url": {
          "raw": "{{baseUrl}}/equipment-planner/volume-for-option?product_code=AMT&option_selected=SB_Option1_Strength(10)&equipment_id=PRE064",
          "host": ["{{baseUrl}}"],
          "path": ["equipment-planner", "volume-for-option"],
          "query": [
            { "key": "product_code", "value": "AMT" },
            { "key": "option_selected", "value": "SB_Option1_Strength(10)" },
            { "key": "equipment_id", "value": "PRE064" }
          ]
        }
      }
    },
    {
      "name": "3. Create Plan",
      "request": {
        "method": "POST",
        "url": "{{baseUrl}}/equipment-planner/create-equipment-planner",
        "body": {
          "mode": "raw",
          "raw": "{\n  \"eqp_id\": \"PRE064\",\n  \"eqp_activity\": \"Mixing\",\n  \"eqp_plan_type\": \"operation\",\n  \"product_name\": \"AMT\",\n  \"dispensing_request_id\": \"DR-001\",\n  \"start_date\": \"2026-02-03 08:00\",\n  \"end_date\": \"2026-02-03 12:00\",\n  \"option_selected\": \"SB_Option1_Strength(10)\"\n}"
        }
      }
    },
    {
      "name": "4. Get Plan",
      "request": {
        "method": "GET",
        "url": {
          "raw": "{{baseUrl}}/equipment-planner/get-equipment-planner?date=03-02-2026",
          "host": ["{{baseUrl}}"],
          "path": ["equipment-planner", "get-equipment-planner"],
          "query": [{ "key": "date", "value": "03-02-2026" }]
        }
      }
    },
    {
      "name": "5. Get Activities by Location",
      "request": {
        "method": "GET",
        "url": {
          "raw": "{{baseUrl}}/equipment-planner/activities-by-location?start_date=2026-02-03&end_date=2026-02-03",
          "host": ["{{baseUrl}}"],
          "path": ["equipment-planner", "activities-by-location"],
          "query": [
            { "key": "start_date", "value": "2026-02-03" },
            { "key": "end_date", "value": "2026-02-03" }
          ]
        }
      }
    },
    {
      "name": "6. Publish Plan",
      "request": {
        "method": "POST",
        "url": "{{baseUrl}}/equipment-planner/publish",
        "body": {
          "mode": "raw",
          "raw": "{}"
        }
      }
    }
  ]
}
```

**Setup Variables in Postman:**
```
baseUrl: http://localhost:3000
```

---

## Expected Responses

### Success Cases

| Endpoint | HTTP Status | Notes |
|----------|-------------|-------|
| GET available-products | 200 | Returns list of all products for dropdown |
| GET product-volume-options | 200 | Returns list of options or empty array |
| GET volume-for-option | 200 | Returns volume details for combination |
| POST create-equipment-planner | 200 | Creates record(s) with status="draft" |
| GET get-equipment-planner | 200 | Returns activities with option_selected |
| GET activities-by-location | 200 | Returns plan vs actual with volumes |
| POST publish | 200 | Publishes all drafts, returns version |

### Error Cases

| Scenario | HTTP Status | Response |
|----------|-------------|----------|
| Missing product_code | 400 | `{ "success": false, "message": "product_code is required" }` |
| Invalid product code | 404 | `{ "success": false, "message": "No volume configuration found for this product" }` |
| Empty bulk array | 400 | `{ "success": false, "message": "Request body array is empty..." }` |
| Invalid date format | 200 | Returns activities with fallback time range |
| No records to publish | 400 | `{ "success": false, "message": "No active records found to publish" }` |

---

## Test Checklist

- [ ] Product volume options dropdown returns correct options
- [ ] Volume selection returns different values based on equipment category
- [ ] Single plan creation saves option_selected
- [ ] Bulk plan creation saves all option_selected values
- [ ] Get plan returns option_selected field
- [ ] Plan vs actual comparison calculates volumes correctly
- [ ] Completed batch shows full master_volume
- [ ] In-progress batch shows proportional volume based on time
- [ ] Publishing increments plan version
- [ ] Published records copied to history with option_selected
- [ ] Plan history retrieval includes option_selected
- [ ] All API endpoints validate required parameters
- [ ] Null handling for optional fields (equipment_id, version_id)
- [ ] Date parsing handles multiple formats (DD-MM-YYYY, YYYY-MM-DD, with time)

---

## Notes

- All timestamps are in Asia/Kolkata (IST) timezone
- Volume calculations use equipment's volume_category to select correct column
- Time-based calculations: `(masterVolume / totalDuration) × timeElapsed`
- Plans start as "draft" until published
- Publishing creates immutable snapshots in history table
- option_selected format: "{VARIANT}_{OPTION}_{STRENGTH}" (e.g., "SB_Option1_Strength(10)")

---

**Last Updated:** February 3, 2026  
**Status:** Ready for Testing ✅
