# Discount API

Generic per-tenant discount rules and their evaluation for a patient session.
Owner is resolved from the authenticated user (`ROLE_DOCTOR` → doctor, `ROLE_CLINIC` → clinic). All responses use the shared envelope; single/object payloads are double-nested (`data.data`).

## Rule types (`type`)

| type | target field(s) | meaning |
|------|-----------------|---------|
| `patient_tag` | `target_tag_uuid` (TenantTag uuid) | patient carries the tag |
| `invoice_amount` | `min_amount_rials` | session `final_price_rials` ≥ threshold |
| `specific_patient` | `target_record_uuid` (PatientRecord uuid) | a specific patient's record |
| `occasion` | `valid_from`/`valid_to`, optional `occasion_kind: birthday` | date window; `birthday` also requires today == patient birthday (month/day) |
| `service` | `target_service_item_uuid` (ServiceItem uuid) | session contains that service (discount base = that service's line total) |
| `visit_count` | `min_visit_count` | patient's session count ≥ threshold |

Shared fields: `discount_type` (`percent`|`fixed`), `value` (percent 0..100 or rials), `priority` (int, higher first), `combinable` (bool), `active` (bool), `valid_from`/`valid_to` (unix, nullable).

---

## GET `/api/v1/admin/discount-rules`
List the owner's rules (array hydration, priority desc). **Auth:** doctor/clinic.

### Response `200`
```json
{ "success": true, "data": { "data": [ { "uuid": "…", "name": "بالای ۱۰۰ هزار → ۱۰٪", "type": "invoice_amount", "discount_type": "percent", "value": 10, "priority": 5, "combinable": false, "active": true, "min_amount_rials": 1000000 } ] } }
```

## POST `/api/v1/admin/discount-rules`
Create a rule. **Auth:** doctor/clinic.

### Request Body
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | ✅ | |
| `type` | string | ✅ | one of the rule types above |
| `discount_type` | string | ❌ | `percent` (default) or `fixed` |
| `value` | int | ❌ | percent or rials |
| `priority` | int | ❌ | default 0 |
| `combinable` | bool | ❌ | default false |
| `active` | bool | ❌ | default true |
| `valid_from` / `valid_to` | int (unix) | ❌ | validity window |
| `target_tag_uuid` / `target_record_uuid` / `target_service_item_uuid` | string (uuid) | ❌ | per-type target |
| `min_amount_rials` / `min_visit_count` | int | ❌ | per-type threshold |
| `occasion_kind` | string | ❌ | `birthday` or null |

### Response `201`
Created rule object (double-nested `data.data`).

### Errors
| Code | HTTP | Description |
|------|------|-------------|
| `ERR_VALIDATION_001` | 422 | missing name / invalid type |
| `ERR_AUTH_006` | 403 | user is not a doctor/clinic owner |

## PATCH `/api/v1/admin/discount-rules/{uuid}`
Update a rule (owner-scoped). Same body fields (all optional). **Auth:** doctor/clinic.
`404 ERR_VALIDATION_002` if the rule is not found for this owner.

## DELETE `/api/v1/admin/discount-rules/{uuid}`
Delete a rule (owner-scoped). **Auth:** doctor/clinic. Response `200` → `{ data: { data: { deleted: true } } }`.

---

## GET `/api/v1/session/{uuid}/discount-suggestions`
Evaluate all active owner rules against a session and return applicable discounts. **Auth:** doctor/clinic; the session's record must belong to the caller's owner.

### Response `200`
```json
{
  "success": true,
  "data": { "data": [
    {
      "rule_uuid": "740bcfc4-…",
      "rule_name": "بالای ۱۰۰ هزار → ۱۰٪",
      "type": "invoice_amount",
      "discount_type": "percent",
      "value": 10,
      "discount_rials": 700000,
      "combinable": false,
      "priority": 5
    }
  ] }
}
```
Ordered by priority desc. Each `discount_rials` is capped at the session's unpaid remainder. Rules that don't apply (or compute to 0) are omitted.

### Errors
| Code | HTTP | Description |
|------|------|-------------|
| `ERR_VALIDATION_002` | 404 | session not found |
| `ERR_AUTH_006` | 403 | not the owner / not a doctor-clinic user |

---

> Applying a suggested discount goes through `PATCH /api/v1/session/{uuid}` with `discount_rule_uuid` (see `patient.md`); the applied rule is recorded on the session as `applied_discount_rule_id` / `applied_discount_rule_label` for audit and reporting.
