# Offers & Rewards Engine

This document describes how discounts and reward points are calculated in the current implementation.

## Discount Offers

### Rule Shape

A discount rule contains:

- **shop_id**: which shop the rule belongs to
- **applies_to**: what the rule targets
- **target_id**: which category/item the rule targets (nullable)
- **discount_type**: `PERCENT` or fixed amount
- **value**: percent value or fixed amount
- **min_amount**: optional minimum subtotal required for the rule to apply
- **start_at / end_at**: optional active window
- **status**: active (1) or inactive (0)

### Applies-To Values

The engine matches a rule based on `applies_to`:

- `ALL`: applies to any purchase at the shop
- `PRODUCT_CATEGORY`: matches the purchased product’s `category_id`
- `PRODUCT`: matches the purchased product `id`
- `SERVICE_CATEGORY`: matches the purchased service’s `category_id`
- `SERVICE`: matches the purchased service `id`

### Selection Strategy (“Best Discount Wins”)

When an order is created, the system:

1. Loads all **active** discount rules for the shop within the active window.
2. Filters rules that do not meet `min_amount` (if set).
3. Filters rules that do not match the item type and target.
4. Computes discount amount:
   - If `PERCENT`: `subtotal × value / 100`
   - Else fixed: `value`
5. Caps the discount: `0 ≤ discount ≤ subtotal`
6. Selects the **maximum** discount amount across all matching rules.

Only the single best discount amount is applied (no stacking).

## Reward Rules (Points Earn)

### Rule Shape

A reward rule contains:

- **shop_id**
- **applies_to**
- **target_id**
- **points_type**: `PERCENT` or fixed-per-unit
- **value**
- **start_at / end_at**
- **status**

### Selection Strategy (“Best Points Wins”)

When an order is created, the system:

1. Loads all **active** reward rules for the shop within the active window.
2. Filters rules that do not match the item type and target.
3. Computes earned points:
   - If `PERCENT`: `round(total × value / 100)`
   - Else fixed-per-unit: `round(value × quantity)`
4. Selects the **maximum** points value across matching rules.

Only the single best points result is used (no stacking).

## Wallet and Ledger

When points are earned:

- The system updates the customer’s wallet (`rewarder_wallets.points_balance`).
- The system inserts a ledger row (`rewarder_ledger`) with:
  - `user_id`, `shop_id`, `order_id`, `points_delta`, and a short description.

## Points Redemption (Current State)

The schema includes `points_redeemed` on orders, but the order creation flow currently:

- Records `points_redeemed = 0`
- Does not subtract points from the wallet
- Does not create a “redeem” ledger entry

If you want “use points to get discount on second purchase”, redemption needs to be implemented as part of the order creation/payment flow.
