# Admin Features Documentation

Complete reference for admin-side features including order notes, contracts, tags, and packages with full API specifications, validation rules, and response formats.

---

## Table of Contents

1. [Order Notes Management](#1-order-notes-management)
2. [Provider Contracts Management](#2-provider-contracts-management)
3. [Provider Tags Management](#3-provider-tags-management)
4. [Package Management](#4-package-management)

---

## 1. Order Notes Management

### Overview
Admins can add internal tracking notes to both service orders and urgent orders. These notes are for internal administrative use and include tracking of who added the note and when.

### 1.1 Service Orders - Admin Notes

#### Database Fields
- `admin_note` (text): The note content
- `admin_note_by` (foreign key): References the admin user who added/updated the note
- `admin_note_at` (timestamp): When the note was last updated

#### Create or Update Admin Note

**Endpoint:** `PUT /admin/service-orders/{id}/admin-note`

**Request Body:**
```json
{
  "admin_note": "Client requested specific time slot. Provider confirmed availability."
}
```

**Validation Rules:**
- `admin_note`: required, string, max 5000 characters

**Success Response (200):**
```json
{
  "success": true,
  "message": "Admin note updated successfully",
  "data": {
    "id": 12345,
    "order_number": "ORD-2026-00123",
    "status": "active",
    "admin_note": "Client requested specific time slot. Provider confirmed availability.",
    "admin_note_at": 1730102400,
    "admin_note_by": {
      "id": 1,
      "full_name": "Admin User",
      "avatar": "https://example.com/avatar.jpg"
    },
    // ... other order fields
  }
}
```

**Error Responses:**

*Validation Error (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "admin_note": [
      "The admin note field is required."
    ]
  }
}
```

*Not Found (404):*
```json
{
  "success": false,
  "message": "Resource not found"
}
```

#### Delete Admin Note

**Endpoint:** `DELETE /admin/service-orders/{id}/admin-note`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Admin note deleted successfully",
  "data": {
    "id": 12345,
    "order_number": "ORD-2026-00123",
    "admin_note": null,
    "admin_note_at": null,
    "admin_note_by": null,
    // ... other order fields
  }
}
```

### 1.2 Urgent Orders - Admin Notes

#### Database Fields
- `admin_note` (text): The note content
- `admin_note_by` (foreign key): References the admin user who added/updated the note
- `admin_note_at` (timestamp): When the note was last updated

#### Create or Update Admin Note

**Endpoint:** `PUT /admin/urgent-orders/{id}/admin-note`

**Request Body:**
```json
{
  "admin_note": "Emergency request - prioritize assignment."
}
```

**Validation Rules:**
- `admin_note`: required, string, max 5000 characters

**Success Response (200):**
```json
{
  "success": true,
  "message": "Admin note updated successfully",
  "data": {
    "id": 789,
    "order_number": "URG-2026-00045",
    "status": "pending",
    "admin_note": "Emergency request - prioritize assignment.",
    "admin_note_at": 1730102400,
    "admin_note_by": {
      "id": 1,
      "full_name": "Admin User",
      "avatar": "https://example.com/avatar.jpg"
    },
    // ... other urgent order fields
  }
}
```

#### Delete Admin Note

**Endpoint:** `DELETE /admin/urgent-orders/{id}/admin-note`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Admin note deleted successfully",
  "data": {
    "id": 789,
    "admin_note": null,
    "admin_note_at": null,
    "admin_note_by": null
  }
}
```

---

## 2. Provider Contracts Management

### Overview
The system uses a unified master contract template that can be customized per provider. Contracts are stored in a dedicated `provider_contracts` table with complete lifecycle management.

### Contract Structure

**Contract Fields:**
- `contract_number`: Auto-generated (format: CNT-YYYY-0001)
- `provider_id`: Target provider (user ID)
- `admin_id`: Admin who created the contract
- `title`: Contract title
- `duration`: Contract length (integer)
- `duration_unit`: Time unit (day, month, or year)
- `start_date`: Contract start date (calculated on acceptance)
- `end_date`: Contract end date (calculated based on duration)
- `clauses`: Array of contract clauses
- `status`: Contract status (sent, accepted, rejected, expired)
- `accepted_at`: Acceptance timestamp
- `rejected_at`: Rejection timestamp
- `rejection_reason`: Provider's rejection reason

**Clause Structure:**
Each clause contains:
- `order`: Clause sequence number
- `title`: Clause heading
- `content`: Clause full text

**Note:** Duration is stored as an integer with a unit. The actual date range is calculated automatically and stored in start_date/end_date fields.

### 2.1 Master Contract Template

#### Get Master Template

**Endpoint:** `GET /admin/provider-contracts/template`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Master contract template retrieved successfully",
  "data": {
    "id": 1,
    "title": "Standard Provider Service Agreement",
    "duration": 12,
    "duration_unit": "month",
    "clauses": [
      {
        "order": 1,
        "title": "Service Obligations",
        "content": "The provider agrees to deliver high-quality services..."
      },
      {
        "order": 2,
        "title": "Payment Terms",
        "content": "Payments shall be processed within 48 hours..."
      },
      {
        "order": 3,
        "title": "Termination",
        "content": "Either party may terminate this agreement..."
      }
    ],
    "created_at": 1730102400,
    "updated_at": 1730102400
  }
}
```

#### Update Master Template

**Endpoint:** `PUT /admin/provider-contracts/template`

**Request Body:**
```json
{
  "title": "Updated Provider Service Agreement",
  "duration": 12,
  "duration_unit": "month",
  "clauses": [
    {
      "order": 1,
      "title": "Service Obligations",
      "content": "Provider must maintain service quality standards..."
    },
    {
      "order": 2,
      "title": "Payment Terms",
      "content": "Payments processed via platform within 48 hours..."
    }
  ]
}
```

**Validation Rules:**
- `title`: optional, string, max 255 characters
- `duration`: optional, integer, min 1, max 120
- `duration_unit`: optional, string, values: day|month|year
- `clauses`: optional, array, min 1 clause
- `clauses.*.order`: optional, integer, min 1
- `clauses.*.title`: required if clauses present, string, max 255 characters
- `clauses.*.content`: required if clauses present, string

**Success Response (200):**
```json
{
  "success": true,
  "message": "Master contract template updated successfully",
  "data": {
    "id": 1,
    "title": "Updated Provider Service Agreement",
    "duration": 12,
    "duration_unit": "month",
    "clauses": [
      {
        "order": 1,
        "title": "Service Obligations",
        "content": "Provider must maintain service quality standards..."
      },
      {
        "order": 2,
        "title": "Payment Terms",
        "content": "Payments processed via platform within 48 hours..."
      }
    ],
    "updated_at": 1730109600
  }
}
```

### 2.2 Individual Contract Management

#### List Contracts

**Endpoint:** `GET /admin/provider-contracts`

**Query Parameters:**
- `provider_id` (optional): Filter by specific provider
- `status` (optional): Filter by status (sent|accepted|rejected|expired)
- `search` (optional): Search by contract number or provider name/phone
- `sort_by` (optional): Sort field (created_at|status|start_date|end_date)
- `sort_order` (optional): Sort direction (asc|desc)
- `per_page` (optional): Results per page (default: 15)

**Example Request:**
```
GET /admin/provider-contracts?status=sent&per_page=20
```

**Success Response (200):**
```json
{
  "success": true,
  "message": "Contracts retrieved successfully",
  "data": [
    {
      "id": 123,
      "contract_number": "CNT-2026-0045",
      "title": "Provider Service Agreement",
      "duration": 12,
      "duration_unit": "month",
      "status": "sent",
      "provider_data": {
        "id": 456,
        "full_name": "John Provider",
        "phone": "+966501234567"
      },
      "created_at": 1730102400
    }
  ],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 3,
    "per_page": 20,
    "to": 20,
    "total": 45
  }
}
```

#### Show Contract

**Endpoint:** `GET /admin/provider-contracts/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Contract details retrieved successfully",
  "data": {
    "id": 123,
    "contract_number": "CNT-2026-0045",
    "title": "Provider Service Agreement",
    "duration": 12,
    "duration_unit": "month",
    "start_date": "2026-10-28",
    "end_date": "2027-10-28",
    "status": "sent",
    "accepted_at": null,
    "rejected_at": null,
    "rejection_reason": null,
    "created_at": 1730102400,
    "updated_at": 1730102400,
    "provider_data": {
      "id": 456,
      "full_name": "John Provider",
      "phone": "+966501234567",
      "email": "provider@example.com"
    },
    "admin_data": {
      "id": 1,
      "full_name": "Admin User"
    },
    "clauses_data": [
      {
        "order": 1,
        "title": "Service Obligations",
        "content": "Provider agrees to deliver high-quality services..."
      },
      {
        "order": 2,
        "title": "Payment Terms",
        "content": "Payments shall be processed within 48 hours..."
      }
    ]
  }
}
```

#### Create Contract

**Endpoint:** `POST /admin/provider-contracts`

**Request Body:**
```json
{
  "provider_id": 456,
  "title": "Custom Provider Agreement",
  "duration": 12,
  "duration_unit": "month",
  "clauses": [
    {
      "order": 1,
      "title": "Service Obligations",
      "content": "Provider agrees to..."
    },
    {
      "order": 2,
      "title": "Payment Terms",
      "content": "Payments processed..."
    }
  ]
}
```

**Validation Rules:**
- `provider_id`: required, integer, must exist in users table
- `title`: optional, string, max 255 characters (uses master template if not provided)
- `duration`: optional, integer, min 1, max 120 (uses master template if not provided)
- `duration_unit`: optional, string, values: day|month|year (uses master template if not provided)
- `clauses`: optional, array, min 1 (uses master template clauses if not provided)
- `clauses.*.order`: optional, integer, min 1
- `clauses.*.title`: required if clauses present, string, max 255 characters
- `clauses.*.content`: required if clauses present, string

**Business Rules:**
- Provider cannot have an existing ACCEPTED contract
- Provider cannot have an existing SENT contract
- If no custom data provided, uses master template
- Contract number auto-generated
- Status automatically set to SENT
- Start and end dates calculated immediately

**Success Response (201):**
```json
{
  "success": true,
  "message": "Contract created and sent to the provider successfully",
  "data": {
    "id": 124,
    "contract_number": "CNT-2026-0046",
    "title": "Custom Provider Agreement",
    "duration": 12,
    "duration_unit": "month",
    "start_date": "2026-10-28",
    "end_date": "2027-10-28",
    "status": "sent",
    "provider_data": {
      "id": 456,
      "full_name": "John Provider"
    },
    "admin_data": {
      "id": 1,
      "full_name": "Admin User"
    },
    "clauses_data": [
      {
        "order": 1,
        "title": "Service Obligations",
        "content": "Provider agrees to..."
      }
    ],
    "created_at": 1730102400
  }
}
```

**Error Responses:**

*Provider Already Has Active Contract (400):*
```json
{
  "success": false,
  "message": "This provider already has an active contract. Please wait until it expires or cancel it first."
}
```

*Provider Has Pending Contract (400):*
```json
{
  "success": false,
  "message": "This provider already has a pending contract. Please update the existing one or wait for a response."
}
```

*Validation Error (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "provider_id": [
      "The provider id field is required."
    ],
    "clauses.0.title": [
      "The clauses.0.title field is required when clauses is present."
    ]
  }
}
```

#### Update Contract

**Endpoint:** `PUT /admin/provider-contracts/{id}`

**Request Body:**
```json
{
  "title": "Updated Agreement Terms",
  "duration": 18,
  "duration_unit": "month",
  "clauses": [
    {
      "order": 1,
      "title": "Modified Service Obligations",
      "content": "Updated terms for service delivery..."
    }
  ]
}
```

**Validation Rules:**
- `provider_id`: optional, integer, must exist in users table
- `title`: optional, string, max 255 characters
- `duration`: optional, integer, min 1, max 120
- `duration_unit`: optional, string, values: day|month|year
- `clauses`: optional, array, min 1
- `clauses.*.order`: optional, integer, min 1
- `clauses.*.title`: required if clauses present, string, max 255 characters
- `clauses.*.content`: required if clauses present, string

**Business Rules:**
- Cannot edit ACCEPTED contracts
- If updating REJECTED contract, status automatically changes to SENT
- Only updates provided fields

**Success Response (200):**
```json
{
  "success": true,
  "message": "Contract updated successfully",
  "data": {
    "id": 123,
    "contract_number": "CNT-2026-0045",
    "title": "Updated Agreement Terms",
    "duration": 18,
    "duration_unit": "month",
    "status": "sent",
    "clauses_data": [
      {
        "order": 1,
        "title": "Modified Service Obligations",
        "content": "Updated terms for service delivery..."
      }
    ],
    "updated_at": 1730109600
  }
}
```

**Error Responses:**

*Cannot Edit Accepted Contract (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "contract": [
      "Cannot edit an accepted and active contract."
    ]
  }
}
```

#### Resend Contract

**Endpoint:** `POST /admin/provider-contracts/{id}/resend`

**Request Body:**
```json
{
  "reset_to_unified": false
}
```

**Parameters:**
- `reset_to_unified` (optional, boolean): If true, resets clauses to master template

**Success Response (200):**
```json
{
  "success": true,
  "message": "Contract resent to the provider successfully",
  "data": {
    "id": 123,
    "contract_number": "CNT-2026-0045",
    "status": "sent",
    "rejected_at": null,
    "rejection_reason": null,
    "updated_at": 1730109600
  }
}
```

**Error Response:**

*Contract Already Accepted (400):*
```json
{
  "success": false,
  "message": "Contract is already accepted."
}
```

#### Delete Contract

**Endpoint:** `DELETE /admin/provider-contracts/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Contract deleted successfully",
  "data": null
}
```

**Error Response:**

*Cannot Delete Accepted Contract (400):*
```json
{
  "success": false,
  "message": "Cannot delete an accepted and active contract."
}
```

---

## 3. Provider Tags Management

### Overview
Provider tags are flexible labels that can be assigned to both individual providers and teams. Tags help organize, categorize, and filter providers based on custom criteria.

### Tag Structure

**Model:** `ProviderTag` (translatable)

**Fields:**
- `id`: Unique identifier
- `title`: Tag name (translatable in all configured locales)
- `is_active`: Enable/disable flag

### 3.1 Tag CRUD Operations

#### List Tags

**Endpoint:** `GET /admin/provider-tags`

**Query Parameters:**
- `per_page` (optional): Results per page (default: 15)
- `search` (optional): Search by title
- `is_active` (optional): Filter by active status (true|false)

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tags retrieved successfully",
  "data": [
    {
      "id": 1,
      "title": "Premium Provider",
      "is_active": true,
      "created_at": "2026-10-28 10:00"
    },
    {
      "id": 2,
      "title": "Verified",
      "is_active": true,
      "created_at": "2026-10-28 11:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "total": 15
  }
}
```

#### Show Tag

**Endpoint:** `GET /admin/provider-tags/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tag retrieved successfully",
  "data": {
    "id": 1,
    "title": "Premium Provider",
    "is_active": true,
    "translations": {
      "ar": {
        "title": "مزود متميز"
      },
      "en": {
        "title": "Premium Provider"
      }
    },
    "created_at": "2026-10-28 10:00"
  }
}
```

#### Create Tag

**Endpoint:** `POST /admin/provider-tags`

**Request Body:**
```json
{
  "ar": {
    "title": "مزود متميز"
  },
  "en": {
    "title": "Premium Provider"
  },
  "is_active": true
}
```

**Validation Rules:**
- `ar.title`: required, string, max 255 characters, unique per locale
- `en.title`: required, string, max 255 characters, unique per locale
- `is_active`: optional, boolean

**Success Response (201):**
```json
{
  "success": true,
  "message": "Tag created successfully",
  "data": {
    "id": 3,
    "title": "Premium Provider",
    "is_active": true,
    "translations": {
      "ar": {
        "title": "مزود متميز"
      },
      "en": {
        "title": "Premium Provider"
      }
    },
    "created_at": "2026-10-28 12:00"
  }
}
```

**Error Responses:**

*Validation Error (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "ar.title": [
      "The ar.title field is required."
    ],
    "en.title": [
      "The en.title has already been taken."
    ]
  }
}
```

#### Update Tag

**Endpoint:** `PUT /admin/provider-tags/{id}`

**Request Body:**
```json
{
  "ar": {
    "title": "مزود محدث"
  },
  "en": {
    "title": "Updated Provider"
  },
  "is_active": false
}
```

**Validation Rules:**
- `ar.title`: sometimes required, string, max 255 characters, unique per locale (excluding current tag)
- `en.title`: sometimes required, string, max 255 characters, unique per locale (excluding current tag)
- `is_active`: optional, boolean

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tag updated successfully",
  "data": {
    "id": 3,
    "title": "Updated Provider",
    "is_active": false,
    "translations": {
      "ar": {
        "title": "مزود محدث"
      },
      "en": {
        "title": "Updated Provider"
      }
    }
  }
}
```

#### Toggle Tag Status

**Endpoint:** `PATCH /admin/provider-tags/{id}/status`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Status updated successfully",
  "data": {
    "id": 3,
    "title": "Premium Provider",
    "is_active": true
  }
}
```

#### Delete Tag

**Endpoint:** `DELETE /admin/provider-tags/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tag deleted successfully",
  "data": null
}
```

**Note:** Deleting a tag removes all assignments to providers and teams.

### 3.2 Assigning Tags

#### Assign Tags to Provider

**Endpoint:** `POST /admin/providers/{provider}/tags`

**Request Body:**
```json
{
  "tag_ids": [1, 2, 3]
}
```

**Validation Rules:**
- `tag_ids`: required, array (can be empty to clear all tags)
- `tag_ids.*`: integer, must exist in provider_tags table

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tags assigned successfully",
  "data": [
    {
      "id": 1,
      "title": "Premium Provider",
      "is_active": true,
      "translations": {
        "ar": {
          "title": "مزود متميز"
        },
        "en": {
          "title": "Premium Provider"
        }
      }
    },
    {
      "id": 2,
      "title": "Verified",
      "is_active": true,
      "translations": {
        "ar": {
          "title": "موثق"
        },
        "en": {
          "title": "Verified"
        }
      }
    }
  ]
}
```

**Error Responses:**

*Validation Error (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "tag_ids": [
      "The tag ids field is required."
    ],
    "tag_ids.0": [
      "The selected tag ids.0 is invalid."
    ]
  }
}
```

#### Assign Tags to Team

**Endpoint:** `POST /admin/teams/{team}/tags`

**Request Body:**
```json
{
  "tag_ids": [2, 4]
}
```

**Validation Rules:**
- `tag_ids`: required, array
- `tag_ids.*`: integer, must exist in provider_tags table

**Success Response (200):**
```json
{
  "success": true,
  "message": "Tags assigned successfully",
  "data": [
    {
      "id": 2,
      "title": "Verified",
      "is_active": true
    },
    {
      "id": 4,
      "title": "Fast Response",
      "is_active": true
    }
  ]
}
```

### 3.3 Tag and Service Filtering

#### Filter Providers by Tag

**Endpoint:** `GET /admin/providers?tag_id=1,2,3`

**Query Parameters:**
- `tag_id`: Single ID, comma-separated IDs, or array format

**Supported Formats:**
```
GET /admin/providers?tag_id=5
GET /admin/providers?tag_id=1,2,3
GET /admin/providers?tag_id[]=1&tag_id[]=2
```

#### Filter Providers by Service

**Endpoint:** `GET /admin/providers?service_id=1,2,3`

**Query Parameters:**
- `service_id`: Single ID, comma-separated IDs, or array format

**Supported Formats:**
```
GET /admin/providers?service_id=5
GET /admin/providers?service_id=1,2,3
GET /admin/providers?service_id[]=1&service_id[]=2
```

**Returns:** Providers who offer **ANY** of the specified services.

**Use Case for Packages:**
When creating a package with multiple services, use this filter to find providers who can fulfill at least one of those services:
```
GET /admin/providers?service_id=1,2,3
```
This returns all providers offering Plumbing (1) OR Electrical (2) OR Carpentry (3).

#### Combined Filtering

You can combine tag and service filters:
```
GET /admin/providers?service_id=1,2&tag_id=5
```
Returns providers who:
- Offer service 1 OR service 2 (ANY match)
- AND have tag 5 (must match)

**Success Response (200):**
```json
{
  "success": true,
  "data": [
    {
      "id": 456,
      "full_name": "John Provider",
      "tags_data": [
        {
          "id": 1,
          "title": "Premium Provider",
          "is_active": true
        },
        {
          "id": 2,
          "title": "Verified",
          "is_active": true
        }
      ],
      "provider_service": [
        {
          "id": 1,
          "name": "Plumbing",
          "price": 100.00
        },
        {
          "id": 2,
          "name": "Electrical",
          "price": 120.00
        }
      ]
    }
  ]
}
```

**Service Filter Response (200):**
```json
{
  "success": true,
  "data": [
    {
      "id": 456,
      "full_name": "John Provider",
      "provider_service": [
        {
          "id": 1,
          "name": "Plumbing",
          "price": 100.00
        }
      ]
    },
    {
      "id": 789,
      "full_name": "Jane Provider",
      "provider_service": [
        {
          "id": 2,
          "name": "Electrical",
          "price": 150.00
        },
        {
          "id": 3,
          "name": "Carpentry",
          "price": 90.00
        }
      ]
    }
  ]
}
```

#### Filter Teams by Tag

**Endpoint:** `GET /admin/teams?tag_id=2`

**Success Response (200):**
```json
{
  "success": true,
  "data": [
    {
      "id": 10,
      "name": "Team Alpha",
      "tags_data": [
        {
          "id": 2,
          "title": "Verified",
          "is_active": true
        }
      ]
    }
  ]
}
```

### 3.4 Tags in API Responses

All provider and team responses (both list and detail views) include a `tags_data` array:

**Provider Response Example:**
```json
{
  "id": 456,
  "full_name": "John Provider",
  "tags_data": [
    {
      "id": 1,
      "title": "Premium Provider",
      "is_active": true
    },
    {
      "id": 2,
      "title": "Verified",
      "is_active": true
    }
  ]
}
```

**Team Response Example:**
```json
{
  "id": 10,
  "name": "Team Alpha",
  "tags_data": [
    {
      "id": 2,
      "title": "Verified",
      "is_active": true
    },
    {
      "id": 4,
      "title": "Fast Response",
      "is_active": true
    }
  ]
}
```

**Permissions Required:**
- `provider-tags.index` - List tags
- `provider-tags.store` - Create tag
- `provider-tags.show` - View tag
- `provider-tags.update` - Update tag
- `provider-tags.destroy` - Delete tag

---

## 4. Package Management

### Overview
Packages bundle multiple service orders at a discounted rate. The system enforces bidirectional validation to ensure all services in a package can be fulfilled by assigned providers or teams.

### Package Structure

**Core Fields:**
- `id`: Unique identifier
- `name`: Package name (translatable)
- `description`: Package description (translatable)
- `price`: Package price
- `orders_count`: Number of orders included
- `discount_percentage`: Total discount percentage
- `provider_discount_percentage`: Provider's share of discount
- `admin_discount_percentage`: Admin's share of discount
- `package_type`: Type (individual|team)
- `client_type`: Target client type (all|individual|company)
- `is_active`: Active status

**Relationships:**
- `providers`: Individual providers (for individual packages)
- `teams`: Teams (for team packages)
- `services`: Services included in package

### 4.1 Package CRUD Operations

#### List Packages

**Endpoint:** `GET /admin/packages`

**Query Parameters:**
- `per_page` (optional): Results per page (default: 15)
- `package_type` (optional): Filter by type (individual|team)
- `is_active` (optional): Filter by status (true|false)

**Success Response (200):**
```json
{
  "success": true,
  "message": "Packages retrieved successfully",
  "data": [
    {
      "id": 1,
      "name": "Home Maintenance Package",
      "price": 500.00,
      "orders_count": 5,
      "discount_percentage": 20.00,
      "package_type": "individual",
      "is_active": true,
      "services_count": 3,
      "providers_count": 5
    }
  ],
  "meta": {
    "current_page": 1,
    "total": 12
  }
}
```

#### Show Package

**Endpoint:** `GET /admin/packages/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Package retrieved successfully",
  "data": {
    "id": 1,
    "name": "Home Maintenance Package",
    "description": "Complete home maintenance services",
    "translations": {
      "ar": {
        "name": "باقة صيانة المنزل",
        "description": "خدمات صيانة منزلية شاملة"
      },
      "en": {
        "name": "Home Maintenance Package",
        "description": "Complete home maintenance services"
      }
    },
    "price": 500.00,
    "orders_count": 5,
    "discount_percentage": 20.00,
    "provider_discount_percentage": 15.00,
    "admin_discount_percentage": 5.00,
    "package_type": "individual",
    "client_type": "all",
    "is_active": true,
    "providers_data": [
      {
        "id": 456,
        "full_name": "John Provider",
        "avatar": "https://example.com/avatar.jpg"
      }
    ],
    "providers_count": 5,
    "services_data": [
      {
        "id": 1,
        "name": "Plumbing",
        "icon": "https://example.com/icon.png"
      },
      {
        "id": 2,
        "name": "Electrical",
        "icon": "https://example.com/icon.png"
      },
      {
        "id": 3,
        "name": "Carpentry",
        "icon": "https://example.com/icon.png"
      }
    ],
    "services_count": 3,
    "created_at": 1730102400,
    "updated_at": 1730102400
  }
}
```

#### Create Package

**Endpoint:** `POST /admin/packages`

**Request Body for Individual Package:**
```json
{
  "ar": {
    "name": "باقة صيانة المنزل",
    "description": "خدمات صيانة منزلية شاملة"
  },
  "en": {
    "name": "Home Maintenance Package",
    "description": "Complete home maintenance services"
  },
  "price": 500.00,
  "orders_count": 5,
  "discount_percentage": 20.00,
  "provider_discount_percentage": 15.00,
  "admin_discount_percentage": 5.00,
  "package_type": "individual",
  "client_type": "all",
  "provider_ids": [456, 789, 101],
  "service_ids": [1, 2, 3],
  "is_active": true
}
```

**Request Body for Team Package:**
```json
{
  "ar": {
    "name": "باقة الفرق",
    "description": "خدمات الفرق المتخصصة"
  },
  "en": {
    "name": "Team Package",
    "description": "Specialized team services"
  },
  "price": 750.00,
  "orders_count": 10,
  "discount_percentage": 25.00,
  "provider_discount_percentage": 18.00,
  "admin_discount_percentage": 7.00,
  "package_type": "team",
  "client_type": "company",
  "team_ids": [10, 20],
  "service_ids": [1, 4],
  "is_active": true
}
```

**Validation Rules:**
- `ar.name`: required, string, max 255
- `en.name`: required, string, max 255
- `ar.description`: optional, string
- `en.description`: optional, string
- `price`: required, numeric, greater than 0, max 99999999.99
- `orders_count`: required, integer, min 1
- `discount_percentage`: required, numeric, greater than 0, max 100
- `provider_discount_percentage`: required, numeric, min 0, max 100
- `admin_discount_percentage`: required, numeric, min 0, max 100
- `package_type`: required, string, values: individual|team
- `provider_ids`: required if package_type=individual, array, min 1, max 1000
- `provider_ids.*`: integer, must exist in users table
- `team_ids`: required if package_type=team, array, min 1, max 100
- `team_ids.*`: integer, must exist in teams table
- `service_ids`: optional, array, max 200
- `service_ids.*`: integer, must exist in services table
- `is_active`: optional, boolean
- `client_type`: optional, string, values: all|individual|company

**Custom Validation Rules:**

1. **Discount Split Validation:**
   - `provider_discount_percentage + admin_discount_percentage` must equal `discount_percentage`
   - Tolerance: 0.01 difference allowed for rounding

2. **Service Match Validation (PackageServicesMatchRule):**
   
   **For Individual Packages:**
   - Each selected provider must offer at least one of the selected services
   - Each selected service must be offered by at least one of the selected providers
   
   **For Team Packages:**
   - Each selected team must provide one of the selected services
   - Each selected service must be provided by at least one of the selected teams

**Success Response (201):**
```json
{
  "success": true,
  "message": "Package created successfully",
  "data": {
    "id": 2,
    "name": "Home Maintenance Package",
    "price": 500.00,
    "orders_count": 5,
    "discount_percentage": 20.00,
    "provider_discount_percentage": 15.00,
    "admin_discount_percentage": 5.00,
    "package_type": "individual",
    "is_active": true,
    "providers_count": 3,
    "services_count": 3,
    "created_at": 1730102400
  }
}
```

**Error Responses:**

*Discount Split Error (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "discount_percentage": [
      "Provider discount + Admin discount must equal total discount percentage"
    ]
  }
}
```

*Service Coverage Error - Provider Missing Service (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "service_ids": [
      "Each selected provider must offer at least one of the selected package services"
    ]
  }
}
```

*Service Coverage Error - Service Missing Provider (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "service_ids": [
      "Each selected service must be offered by at least one of the selected package providers"
    ]
  }
}
```

*Team Service Mismatch (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "service_ids": [
      "Each selected team must provide one of the selected package services"
    ]
  }
}
```

*Inactive Provider (422):*
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "provider_ids": [
      "One or more selected providers are not active or approved in the system"
    ]
  }
}
```

#### Update Package

**Endpoint:** `PUT /admin/packages/{id}`

**Request Body:**
```json
{
  "price": 550.00,
  "discount_percentage": 22.00,
  "provider_discount_percentage": 17.00,
  "admin_discount_percentage": 5.00,
  "service_ids": [1, 2, 3, 4],
  "is_active": false
}
```

**Validation Rules:**
Same as create, but all fields are optional (use "sometimes" instead of "required")

**Additional Rules:**
- When switching package_type, opposite relationship is cleared
- Service validation re-runs with new configuration

**Success Response (200):**
```json
{
  "success": true,
  "message": "Package updated successfully",
  "data": {
    "id": 2,
    "price": 550.00,
    "discount_percentage": 22.00,
    "is_active": false,
    "updated_at": 1730109600
  }
}
```

#### Delete Package

**Endpoint:** `DELETE /admin/packages/{id}`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Package deleted successfully",
  "data": null
}
```

**Error Response:**

*Active Subscriptions Exist (400):*
```json
{
  "success": false,
  "message": "Cannot delete this package because there are active clients who have not yet consumed all their package benefits."
}
```

#### Toggle Package Status

**Endpoint:** `PATCH /admin/packages/{id}/status`

**Success Response (200):**
```json
{
  "success": true,
  "message": "Package status updated successfully",
  "data": {
    "id": 2,
    "is_active": true
  }
}
```

### 4.2 Service Requirements Enforcement

#### How Service Validation Works

**Database Structure:**
- `package_service`: Links packages to services
- `provider_service`: Tracks which providers offer which services
- `teams.service_id`: Teams have a single service

**Validation Algorithm:**

**For Individual Packages:**
1. Query `provider_service` table for all combinations of selected providers and services
2. Check each provider has at least one service from the package
3. Check each service is offered by at least one provider

**For Team Packages:**
1. Get service_id for each selected team
2. Verify each team's service is in the package's service list
3. Verify each package service has at least one team providing it

### 4.3 Frontend Workflow for Package Creation

#### Step 1: Select Services for Package

Admin selects which services will be included in the package (e.g., Plumbing, Electrical, Carpentry).

#### Step 2: Filter Providers by Selected Services

**Request:**
```
GET /admin/providers?service_id=1,2,3&per_page=100
```
Where:
- 1 = Plumbing
- 2 = Electrical  
- 3 = Carpentry

**Purpose:** This returns all providers who offer **at least one** of these services. The frontend can then allow the admin to select which providers to include in the package.

**Response:**
```json
{
  "success": true,
  "data": [
    {
      "id": 100,
      "full_name": "Provider A",
      "provider_service": [
        {"id": 1, "name": "Plumbing", "price": 100},
        {"id": 2, "name": "Electrical", "price": 120}
      ]
    },
    {
      "id": 101,
      "full_name": "Provider B",
      "provider_service": [
        {"id": 3, "name": "Carpentry", "price": 90}
      ]
    },
    {
      "id": 102,
      "full_name": "Provider C",
      "provider_service": [
        {"id": 1, "name": "Plumbing", "price": 95},
        {"id": 2, "name": "Electrical", "price": 110},
        {"id": 3, "name": "Carpentry", "price": 85}
      ]
    }
  ]
}
```

#### Step 3: Admin Selects Providers

From the filtered list, admin selects which providers to include (e.g., Provider A, Provider B, Provider C).

#### Step 4: Create Package

**Request:**
```json
{
  "service_ids": [1, 2, 3],
  "provider_ids": [100, 101, 102],
  "price": 500,
  "orders_count": 5,
  ...
}
```

#### Step 5: Backend Validation

The `PackageServicesMatchRule` validates:
- ✅ Provider A offers Plumbing (1) and Electrical (2) ← has at least one service
- ✅ Provider B offers Carpentry (3) ← has at least one service
- ✅ Provider C offers all three ← has at least one service
- ✅ Plumbing (1) is covered by Providers A & C
- ✅ Electrical (2) is covered by Providers A & C
- ✅ Carpentry (3) is covered by Providers B & C
- ✅ **All services covered, all providers relevant → Valid!**

### 4.4 Package Profitability Warning

#### ⚠️ Important Financial Consideration

When creating packages, admins should be aware of the relationship between discount percentages and platform fees to ensure profitability.

**Key Formula:**
```
Admin Discount Percentage ≤ App Fee Percentage
```

**Why This Matters:**

The platform earns revenue through the app fee percentage charged on each order. When creating a package, the admin absorbs part of the discount:

```
Example Scenario:
- App Fee: 15% (platform's revenue)
- Package Discount: 20%
  - Provider absorbs: 15%
  - Admin absorbs: 5%

Revenue Analysis:
✅ Admin absorbs 5% < App fee 15% → Platform still profitable
❌ Admin absorbs 18% > App fee 15% → Platform loses money on orders
```

#### When Admin Discount Can Exceed App Fee

**Scenario: Subscription Revenue Offsets Losses**

If the package is sold with a subscription fee that covers the per-order losses:

```
Package Details:
- Subscription price: 500 SAR
- Orders included: 5
- Average order value: 100 SAR
- App fee: 15% = 15 SAR per order
- Admin discount: 18% = 18 SAR per order
- Loss per order: 3 SAR
- Total loss on orders: 15 SAR (3 × 5)

✅ Subscription (500 SAR) covers losses (15 SAR) + provides profit
```

#### Recommendation

**Warning Message in Frontend:**
```
⚠️ Profitability Notice:
Admin discount percentage (18%) exceeds app fee percentage (15%).
This means the platform will lose 3% on each order.

Ensure the package subscription price covers these losses.

Formula: Admin discount should ideally be ≤ App fee for order-level profitability.
```

#### Why Not Enforce as Validation?

We **intentionally do not block** this in validation because:

1. ✅ **Business Flexibility** - Admin may have strategic reasons
2. ✅ **Subscription Model** - Upfront fees can offset per-order losses
3. ✅ **Market Strategy** - Loss leaders to acquire customers
4. ✅ **Promotional Campaigns** - Temporary high discounts for growth
5. ✅ **Bundle Economics** - Overall package profitability matters more

#### Best Practices

**✅ Standard Packages (Profitable Per Order):**
```json
{
  "discount_percentage": 20.00,
  "provider_discount_percentage": 15.00,
  "admin_discount_percentage": 5.00,   // ≤ app_fee (typically 15%)
  "price": 500.00
}
```

**⚠️ Strategic Packages (Subscription Covers Losses):**
```json
{
  "discount_percentage": 30.00,
  "provider_discount_percentage": 15.00,
  "admin_discount_percentage": 15.00,  // May exceed app_fee
  "price": 800.00  // Higher subscription price to compensate
}
```

#### Frontend Implementation

**Display Warning (Non-Blocking):**
```javascript
// Calculate loss per order
const appFeePercentage = 15; // Get from settings
const adminDiscount = formData.admin_discount_percentage;
const lossPerOrder = adminDiscount - appFeePercentage;

if (lossPerOrder > 0) {
  showWarning({
    type: 'warning',
    message: `Admin will absorb ${lossPerOrder}% per order. 
              Ensure subscription price (${formData.price} SAR) 
              covers this loss across ${formData.orders_count} orders.`,
    calculation: `Total potential loss: ${lossPerOrder * formData.orders_count}%`
  });
}
```

**UI Example:**
```
┌─────────────────────────────────────────────────────────────┐
│ Package Discount Split                                       │
├─────────────────────────────────────────────────────────────┤
│ Total Discount: 25%                                          │
│   • Provider absorbs: 10%                                    │
│   • Admin absorbs: 15%          ⚠️ Matches app fee          │
│                                                              │
│ ⚠️ Profitability Check:                                     │
│ Admin discount (15%) = App fee (15%)                        │
│ → Break-even on orders, profit from subscription            │
└─────────────────────────────────────────────────────────────┘
```

```
┌─────────────────────────────────────────────────────────────┐
│ Package Discount Split                                       │
├─────────────────────────────────────────────────────────────┤
│ Total Discount: 30%                                          │
│   • Provider absorbs: 12%                                    │
│   • Admin absorbs: 18%          ⚠️ Exceeds app fee!         │
│                                                              │
│ ⚠️ Loss Warning:                                            │
│ Admin discount (18%) > App fee (15%)                        │
│ Loss per order: 3%                                          │
│ Total loss: 15% across 5 orders                             │
│                                                              │
│ ⚠️ Ensure subscription price (500 SAR) covers this loss    │
└─────────────────────────────────────────────────────────────┘
```

---

### 4.5 Use Case Examples

#### Example 1: Valid Multi-Service Package

**Configuration:**
- Services: [Plumbing, Electrical, Carpentry]
- Providers:
  - Provider A: offers Plumbing, Electrical
  - Provider B: offers Carpentry, Electrical
  - Provider C: offers Plumbing, Carpentry

**Result:** ✅ Valid
- All services covered
- All providers relevant

#### Example 2: Invalid - Service Without Provider

**Configuration:**
- Services: [Plumbing, Electrical, Painting]
- Providers:
  - Provider A: offers Plumbing, Electrical

**Result:** ❌ Invalid
- Error: "Each selected service must be offered by at least one of the selected package providers"
- Painting service has no provider

#### Example 3: Invalid - Provider Without Service

**Configuration:**
- Services: [Plumbing, Electrical]
- Providers:
  - Provider A: offers Plumbing
  - Provider B: offers Carpentry

**Result:** ❌ Invalid
- Error: "Each selected provider must offer at least one of the selected package services"
- Provider B offers Carpentry (not in package)

#### Example 4: Valid Team Package

**Configuration:**
- Services: [Deep Cleaning, Window Cleaning]
- Teams:
  - Team Alpha: service_id = Deep Cleaning
  - Team Beta: service_id = Window Cleaning

**Result:** ✅ Valid
- Complete coverage
- All teams aligned with services

---

## ✅ Implementation Summary

### Providers Filtering

**Endpoint:** `GET /admin/providers?service_id=1,2,3`

**How it works:**
- Providers can offer **MULTIPLE services**
- Filter returns providers who offer **ANY** of the specified services
- Uses `whereHas` on `providerService` relationship
- Returns providers with ALL their services (not just filtered ones)

**Backward Compatible:** ✅ Yes
- Single ID: `?service_id=5` → Works as before
- Multiple IDs: `?service_id=1,2,3` → New functionality
- Array format: `?service_id[]=1&service_id[]=2` → New functionality

**No Conflicts:** ✅ 
- Uses scope `scopeServiceId` in User model
- DashboardCRUDService automatically calls it via `filterableFields`
- Previous single-service filtering removed from `ProviderService::applyScopes()`

---

### Teams Filtering

**Endpoint:** `GET /admin/teams?service_id=1,2,3`

**How it works:**
- Teams provide **ONE service** (stored in `service_id` column)
- Filter returns teams where `service_id` is IN the specified array
- Uses `whereIn` on `service_id` column
- Returns teams that match ANY of the specified service IDs

**Backward Compatible:** ✅ Yes
- Single ID: `?service_id=5` → Works as before (WHERE service_id = 5)
- Multiple IDs: `?service_id=1,2,3` → New functionality (WHERE service_id IN (1,2,3))
- Array format: `?service_id[]=1&service_id[]=2` → New functionality

**No Conflicts:** ✅
- Uses scope `scopeServiceId` in Team model
- DashboardCRUDService automatically calls it via `filterableFields`
- `service_id` already in `filterableFields`

---

## 🎯 For Package Creation

### Individual Packages (Providers)

**Step 1:** Select services for package (e.g., [1, 2, 3])

**Step 2:** Filter providers
```
GET /admin/providers?service_id=1,2,3
```

**Returns:** Providers who offer service 1 OR 2 OR 3 (or any combination)

**Example:**
```
Provider A: offers [Plumbing(1), Electrical(2)]           ← Returned
Provider B: offers [Carpentry(3)]                         ← Returned  
Provider C: offers [Plumbing(1), Electrical(2), HVAC(4)]  ← Returned
Provider D: offers [Painting(5)]                          ← NOT returned
```

---

### Team Packages (Teams)

**Step 1:** Select services for package (e.g., [1, 2, 3])

**Step 2:** Filter teams
```
GET /admin/teams?service_id=1,2,3
```

**Returns:** Teams where service_id is 1 OR 2 OR 3

**Example:**
```
Team Alpha: service_id = 1 (Plumbing)      ← Returned
Team Beta:  service_id = 2 (Electrical)    ← Returned
Team Gamma: service_id = 3 (Carpentry)     ← Returned
Team Delta: service_id = 4 (HVAC)          ← NOT returned
```

---

## ⚠️ Key Differences

| Aspect | Providers | Teams |
|--------|-----------|-------|
| **Services** | Multiple services per provider | ONE service per team |
| **Filtering Logic** | WHERE service_id IN provider's services | WHERE service_id IN (array) |
| **Relationship** | Many-to-Many (`provider_service` pivot) | Direct column (`service_id`) |
| **Response Shows** | ALL services provider offers | Only team's ONE service |

---

## ✅ Validation

Both endpoints work correctly for package creation because:

1. **PackageServicesMatchRule validates:**
   - For individual packages: Each provider offers ≥1 package service
   - For team packages: Each team's service_id is in package services

2. **Filtering ensures:**
   - Only relevant providers/teams are returned
   - Frontend can easily select from filtered list
   - Backend validation will pass if using filtered results

---

## 🧪 Test Cases

### Test 1: Single Service (Backward Compatibility)

**Providers:**
```
GET /admin/providers?service_id=5
✅ Returns providers who offer service 5
```

**Teams:**
```
GET /admin/teams?service_id=5  
✅ Returns teams where service_id = 5
```

### Test 2: Multiple Services (New Functionality)

**Providers:**
```
GET /admin/providers?service_id=1,2,3
✅ Returns providers offering ANY of these services
```

**Teams:**
```
GET /admin/teams?service_id=1,2,3
✅ Returns teams with service_id IN (1,2,3)
```

### Test 3: Combined Filters

**Providers:**
```
GET /admin/providers?service_id=1,2&tag_id=5
✅ Returns providers who:
   - Offer service 1 OR 2 (ANY)
   - AND have tag 5 (must match)
```

**Teams:**
```
GET /admin/teams?service_id=1,2&tag_id=5
✅ Returns teams where:
   - service_id is 1 OR 2 (ANY)
   - AND have tag 5 (must match)
```

---

## 📝 Summary for Frontend

### For Individual Packages:
1. Select services → Get array [1, 2, 3]
2. Call: `GET /admin/providers?service_id=1,2,3`
3. Show providers with ALL their services
4. Highlight which services match the package
5. Admin selects providers
6. Submit package with selected provider IDs and service IDs

### For Team Packages:
1. Select services → Get array [1, 2, 3]
2. Call: `GET /admin/teams?service_id=1,2,3`
3. Show teams (each team has ONE service)
4. Admin selects teams
5. Submit package with selected team IDs and service IDs

### Validation:
- Backend `PackageServicesMatchRule` ensures complete coverage
- No conflicts with existing functionality
- Backward compatible with single service filtering
- **Purpose:** Internal admin tracking for orders
- **Features:** Full audit trail with admin and timestamp
- **Endpoints:** PUT (create/update), DELETE (remove)
- **Max Length:** 5000 characters

### Provider Contracts
- **Master Template:** Single unified template for all contracts
- **Customization:** Per-provider contract customization available
- **Lifecycle:** SENT → ACCEPTED/REJECTED → EXPIRED
- **Duration:** Stored as integer with unit (day/month/year)
- **Restrictions:** Cannot edit/delete accepted contracts

### Provider Tags
- **Translation:** Full multi-language support
- **Assignment:** Can assign to both providers and teams
- **Filtering:** Powerful filtering by tags (single or multiple)
- **Visibility:** Tags appear in all provider/team API responses
- **Permissions:** Granular permission control

### Package Services
- **Validation:** Strict bidirectional coverage validation
- **Types:** Individual packages (providers) or team packages (teams)
- **Discount Split:** Provider + Admin must equal total discount
- **Service Requirements:** Every service must have coverage, every provider/team must be relevant
- **Protection:** Cannot delete packages with active subscriptions

---

## Best Practices

1. **Order Notes:** Use clear, concise language for internal tracking
2. **Contracts:** Test with master template before creating custom contracts
3. **Tags:** Create meaningful tag names that scale with your business
4. **Packages:** Verify provider-service relationships before creating packages
5. **Validation:** Review error messages carefully - they guide proper configuration
