# API Contract: Create Customer

**Endpoint**: `POST /api/v1/customers`
**Permission**: `customers.create` (Spatie, `admin` guard)
**Auth**: `Authorization: Bearer {token}` (Sanctum)

## Request

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer {sanctum_token}` |
| `Accept` | Yes | `application/json` |
| `Content-Type` | Yes (for file upload) | `multipart/form-data` |

### Body (multipart/form-data)

| Field | Type | Required | Constraints |
|---|---|---|---|
| `name` | string | **Yes** | 1–150 characters |
| `phone` | string | **Yes** | Syrian local format: `^0?9\d{8}$` (e.g., `0912345678` or `912345678`); unique across `clients` |
| `description` | string | No | Up to 1000 characters; nullable |
| `avatar` | file (image) | No | One of `jpg`, `jpeg`, `png`, `webp`; ≤ 5 MB; nullable |

### Example Request

```http
POST /api/v1/customers HTTP/1.1
Authorization: Bearer {token}
Content-Type: multipart/form-data; boundary=----FormBoundary

------FormBoundary
Content-Disposition: form-data; name="name"

أحمد محمد
------FormBoundary
Content-Disposition: form-data; name="phone"

0912345678
------FormBoundary
Content-Disposition: form-data; name="description"

عميل مميز
------FormBoundary
Content-Disposition: form-data; name="avatar"; filename="ahmad.jpg"
Content-Type: image/jpeg

<binary data>
------FormBoundary--
```

## Success Response

### 201 Created

```json
{
  "success": true,
  "message": "تم إنشاء العميل بنجاح.",
  "data": {
    "id": 42,
    "name": "أحمد محمد",
    "phone": "0912345678",
    "description": "عميل مميز",
    "avatar": "avatars/abc123.jpg",
    "avatar_url": "http://host/storage/avatars/abc123.jpg",
    "client_type_flags": ["customer"],
    "created_at": "2026-06-07T10:30:00+03:00",
    "updated_at": "2026-06-07T10:30:00+03:00"
  }
}
```

## Error Responses

### 401 Unauthorized

```json
{
  "success": false,
  "message": "Unauthenticated."
}
```

### 403 Forbidden (missing `customers.create` permission)

```json
{
  "success": false,
  "message": "You do not have permission to perform this action."
}
```

### 422 Unprocessable Entity (validation error)

```json
{
  "success": false,
  "message": {
    "phone": ["رقم الهاتف يجب أن يكون بالصيغة السورية المحلية (10 أرقام)"],
    "avatar": ["يجب أن يكون الملف صورة بصيغة مدعومة (jpg, jpeg, png, webp)"]
  }
}
```

### 422 Unprocessable Entity (phone duplicate)

```json
{
  "success": false,
  "message": {
    "phone": ["رقم الهاتف مستخدم مسبقاً."]
  }
}
```

### 500 Server Error

```json
{
  "success": false,
  "message": "حدث خطأ غير متوقع. يرجى المحاولة لاحقاً."
}
```

## Server-Side Behavior

1. Validate input via `StoreCustomerRequest` (rules in `data-model.md`).
2. Open a DB transaction.
3. `Client::create([... 'client_type_flags' => ['customer']])`.
4. If `avatar` provided: `Storage::disk('public')->putFile('avatars', $file)`; update `client->avatar`; `$client->save()`.
5. Commit transaction.
6. Return `resourceCreatedResponse('تم إنشاء العميل بنجاح.', $clientResource->toArray($client))`.

## Idempotency

Not idempotent. Two identical POSTs will create two clients (the second may fail with 422 if `phone` is duplicated).

## Side Effects

- Inserts one row into `clients` with `client_type_flags = ["customer"]`.
- If avatar provided: writes one file to `storage/app/public/avatars/`.
- No `customer_listing_mv` write — the MV is refreshed every 5 minutes by the scheduler.

## Test Cases (Feature Tests)

| # | Scenario | Expected |
|---|---|---|
| 1 | POST with valid name, phone, description, avatar | 201, response has client data, `client_type_flags` includes `"customer"`, avatar file exists on disk |
| 2 | POST without avatar | 201, `avatar` field is null in response, no file created |
| 3 | POST with phone missing | 422, `phone` field error |
| 4 | POST with phone = "12345" (bad format) | 422, `phone` field error |
| 5 | POST with phone = existing client's phone | 422, `phone` field error "مستخدم مسبقاً" |
| 6 | POST with avatar larger than 5 MB | 422, `avatar` field error |
| 7 | POST with avatar = `.gif` file | 422, `avatar` field error |
| 8 | POST with name longer than 150 chars | 422, `name` field error |
| 9 | POST as a user without `customers.create` permission | 403 |
| 10 | POST without `Authorization` header | 401 |
| 11 | After successful POST, the new client appears in `GET /api/v1/customers` after MV refresh | The list includes the new client within 5 minutes (or immediately after `REFRESH MATERIALIZED VIEW` in tests) |
