# Promo Codes

Promo codes let owners offer discounts on unit bookings.

## PromoCode model

`App\Models\PromoCode` stores:

- `owner_id` (nullable; `null` is reserved for platform-wide codes managed by super admins)
- `code` (encrypted)
- `code_hash` (unique, used for lookups)
- `usage_limit`
- `per_customer_limit`
- `uses_count`
- `discount_value`
- `discount_type` — `percentage` or `fixed`
- `starts_at`, `expires_at`, `used_at`

The actual code is encrypted; a hash is used for validation.

## Bulk generation

`POST /api/v1/promo-codes` creates one or more codes at once via `PromoCodeService::generateBulk`:

1. Accepts `count` (default 1, max 100), discount type/value, usage limits, and validity window.
2. Hashes each generated code.
3. Persists them under the authenticated owner's account.

## Validation

A promo code is valid when:

- It has started and not expired.
- It has not exceeded `usage_limit`.
- The customer has not exceeded `per_customer_limit`.
- It applies to the selected unit/owner.

## Redemption

During reservation creation or recalculation, the system:

1. Looks up the code by hash.
2. Validates it against the unit and customer.
3. Applies the discount to the total price.
4. Increments `uses_count`.

## Public preview

Unauthenticated users can preview the discount a promo code would apply to a unit and date range without actually redeeming it (`POST /api/v1/promo-codes/preview`).

## Owner management

Owners and their employees can list, update, and delete promo codes scoped to their account. Super admins bypass all policy checks.
