> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sitestorepro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Discount Service: Pricing & Promotions Engine

> Learn how Site Store Pro's DiscountService calculates item prices, applies BOGO rules, and evaluates order-level coupons across a 3-phase pipeline.

The `DiscountService` (`App\Services\DiscountService`) is the central pricing engine for Site Store Pro. It governs every price adjustment a customer sees — from category-wide markdowns and quantity breaks to BOGO promotions and stacking order coupons. Understanding how it works lets you build integrations, custom rules, and reporting tools that align with the platform's pricing logic.

## Public Methods

These two methods are your primary entry points when working with discounted pricing programmatically.

<CardGroup cols={2}>
  <Card title="applyDiscountsToCart" icon="cart-shopping">
    Runs the full 3-phase discount pipeline against a set of cart line items for a given user. Use this when you need the complete, order-ready price breakdown.
  </Card>

  <Card title="getDiscountedPriceForVariant" icon="tag">
    Calculates the discounted price for a single variant given user context and quantity. Use this for catalog displays and buy-box pricing.
  </Card>
</CardGroup>

### `applyDiscountsToCart`

```php theme={null}
applyDiscountsToCart($items, $user): array
```

Pass the array of cart line items and the authenticated (or guest) user. Both parameters are intentionally untyped — `$items` accepts any iterable collection of cart line objects, and `$user` accepts either an authenticated `User` model or a guest user representation. The method runs all three discount phases in sequence and returns a structured array:

| Key                 | Type    | Description                                    |
| ------------------- | ------- | ---------------------------------------------- |
| `items`             | `array` | Line items with per-item adjusted prices       |
| `subtotal`          | `float` | Raw subtotal before order-level discounts      |
| `discounts`         | `array` | List of applied discount objects with metadata |
| `total_discount`    | `float` | Total currency amount discounted               |
| `adjusted_subtotal` | `float` | Final subtotal after all discounts             |

**Example:**

```php theme={null}
use App\Services\DiscountService;

$discountService = app(DiscountService::class);

$result = $discountService->applyDiscountsToCart($cart->items, auth()->user());

$adjustedSubtotal = $result['adjusted_subtotal'];
$appliedDiscounts = $result['discounts'];
$totalSaved       = $result['total_discount'];
```

### `getDiscountedPriceForVariant`

```php theme={null}
getDiscountedPriceForVariant($variant, $user, $qty): float
```

Use this single-variant helper when you need a discounted price outside of the full cart context — for example, on a product listing page or in a custom buy-box component. `$variant` accepts a `ProductVariant` model instance, `$user` accepts an authenticated user or guest, and `$qty` is the requested quantity as an integer or numeric string.

**Example:**

```php theme={null}
$price = $discountService->getDiscountedPriceForVariant(
    $variant,
    auth()->user(),
    $requestedQty
);
```

***

## The 3-Phase Discount Pipeline

When `applyDiscountsToCart` runs, it evaluates discounts in three sequential phases. Each phase builds on the previous one. Later phases cannot override item-level pricing settled in Phase 1 — they add to or further reduce the running total.

<Steps>
  <Step title="Phase 1 — Item-Level Evaluation">
    For each line item in the cart, the service walks through up to five discount rules in ascending priority order. **The last applicable rule wins** — meaning a higher-priority rule will replace a lower-priority one if both match.

    <Note>
      Because the last applicable rule wins, a more specific discount (like an item-specific price) will always take precedence over a broader one (like a category discount) as long as it is evaluated later in the sequence. Design your discount rules with this ordering in mind to avoid unintended overrides.
    </Note>

    The evaluation order per line item is:

    | Priority    | Rule                      | Discount Type                |
    | ----------- | ------------------------- | ---------------------------- |
    | 1 (lowest)  | Category & Brand Discount | Type 5                       |
    | 2           | Item-Specific Discount    | Type 6                       |
    | 3           | On-Sale Special Price     | `variant->sale_price`        |
    | 4           | Quantity Break Tier       | `variant->quantityDiscounts` |
    | 5 (highest) | Wholesale Price           | `variant->wholesale_price`   |

    After this phase, every line item has its final per-unit adjusted price.
  </Step>

  <Step title="Phase 2 — BOGO Evaluation">
    After item-level pricing is settled, the service evaluates any active Buy X Get Y (BOGO) promotions.

    * **Trigger check:** Validates that the trigger product X is present in the cart at or above the required `free_range1` quantity threshold.
    * **Target discount:** Applies the configured `product_y_percent` discount to the target product Y in the cart.
    * **Slide cart lock:** Marks the BOGO target item's quantity as locked in the slide cart to prevent the customer from editing it to exploit the promotion.
  </Step>

  <Step title="Phase 3 — Order-Level Discounts">
    With item and BOGO pricing settled, the service evaluates discounts that apply to the order as a whole. These are processed in the following priority order (highest priority applied first):

    | Priority | Type                      | Slug           |
    | -------- | ------------------------- | -------------- |
    | 1        | Coupon / Gift Certificate | `code`         |
    | 2        | General Order Value       | `order_value`  |
    | 3        | Preferred Customer        | `preferred`    |
    | 4        | New Customer              | `new_customer` |

    Each candidate discount is validated against:

    * Minimum and maximum order spend
    * Minimum order quantity
    * Minimum order weight
    * Customer role eligibility

    If the `allow_multiple_order_discounts` config flag is enabled, valid discounts at this phase can stack. Otherwise, only the highest-priority applicable discount is applied.
  </Step>
</Steps>

***

## Discount Type Reference

Use these type identifiers and slugs when querying the `discounts` table or building custom tooling around the discount system.

| Type | Slug             | Scope | Description                                              |
| ---- | ---------------- | ----- | -------------------------------------------------------- |
| 1    | `code`           | Order | Coupon code or gift certificate entered at checkout      |
| 2    | `preferred`      | Order | Automatic discount targeted at specific user accounts    |
| 3    | `order_value`    | Order | Automatic discount triggered by order subtotal threshold |
| 4    | `new_customer`   | Order | Automatic discount applied to a customer's first order   |
| 5    | `category_brand` | Item  | Discount applied to all products in a category or brand  |
| 6    | `item_specific`  | Item  | Discount applied to a single specific product            |
| 7    | `bogo`           | Item  | Buy X Get Y promotion                                    |

***

## CurrencyService Integration

`App\Services\CurrencyService` works alongside `DiscountService` to ensure that every price returned is formatted correctly for the active storefront language.

The service calls `LanguageService::currencyOverride()` to check whether the current language has a currency override configured. Based on the result, it dynamically formats:

* **Symbol** — e.g. `$`, `€`, `£`
* **Symbol placement** — prefix or suffix
* **Decimal and thousands separators** — locale-aware formatting

You do not need to call `CurrencyService` directly when using `applyDiscountsToCart` or `getDiscountedPriceForVariant` — prices are returned as raw floats. Apply `CurrencyService` formatting at the display layer when rendering prices to the storefront.

```php theme={null}
use App\Services\CurrencyService;

$currencyService = app(CurrencyService::class);

$formattedPrice = $currencyService->format($result['adjusted_subtotal']);
// e.g. "€ 49,95" for a Dutch locale
```
