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

# Автодобавление бесплатного подарка при достижении порога

> Сценарий использования Cart SDK, который добавляет бесплатный подарок, когда сумма корзины превышает порог, и удаляет его, если сумма опускается ниже.

Следите за суммой корзины и синхронизируйте с ней бесплатный подарок: добавьте подарок, когда покупатель превысит порог расходов, и удалите его, если сумма снова опустится ниже.

<Note>
  Для настраиваемой мерчантом версии с уровнями, правилами по рынкам и прогресс-баром используйте блок [Rewards](/ru/aftersell/cart/rewards-block) — без кода. Этот вариант нужен, когда ваше правило не выражается блоком.
</Note>

<div id="the-snippet">
  ## Фрагмент кода
</div>

Вставьте в **Cart settings → Custom script → Initialization** и задайте две константы:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const FREE_GIFT_VARIANT_ID = 1234567890; // the gift product's variant id
const THRESHOLD_CENTS = 5000;            // $50.00, since totals are in cents

function reconcileGift(state) {
  const gift = state.items.find((line) => line.variantId === FREE_GIFT_VARIANT_ID);
  const qualifies = state.totalPrice >= THRESHOLD_CENTS;

  if (qualifies && !gift) {
    window.aftersell.cart.actions.addItem(FREE_GIFT_VARIANT_ID, 1);
  } else if (!qualifies && gift) {
    window.aftersell.cart.actions.removeItem(gift.key);
  }
}

window.aftersell.cart.events.on('cart_loaded', reconcileGift);
window.aftersell.cart.events.on('cart_updated', reconcileGift);
```

<div id="how-it-works">
  ## Как это работает
</div>

**`reconcileGift`** описывает состояние, в котором корзина *должна* находиться, а затем вносит не более одного изменения, чтобы достичь его:

* Условие выполнено, а подарка нет → [`addItem`](/ru/aftersell/cart/sdk-actions#additemvariantid-quantity) добавляет его.
* Условие не выполнено, а подарок есть → [`removeItem`](/ru/aftersell/cart/sdk-actions#removeitemkey) удаляет его по `key` строки.
* Иначе → ничего не делать.

Функция выполняется и на [`cart_loaded`](/ru/aftersell/cart/sdk-events#cart_loaded) (чтобы корзина, уже соответствующая условию при загрузке страницы, получила подарок), и на [`cart_updated`](/ru/aftersell/cart/sdk-events#cart_updated) (чтобы реагировать на каждое последующее изменение). Поскольку `cart_loaded` воспроизводится для поздних подписчиков, это работает независимо от того, когда запускается ваш скрипт.

<Warning>
  **Проверки `!gift` / `gift` — именно то, что предотвращает зацикливание.** Добавление подарка вызывает `cart_updated`, которое снова запускает `reconcileGift`, и второй проход обнаруживает, что подарок уже есть, поэтому ничего не делает. Уберите проверки — и получите бесконечный цикл. См. [два правила](/ru/aftersell/cart/sdk-events#the-two-rules).
</Warning>

Обратите внимание, что добавление подарка **увеличивает сумму корзины**, поэтому порог, близкий к цене товара, может колебаться: добавление подарка за \$0 безопасно, но подарок с ценой сам по себе может поднять корзину выше порога. Делайте подарок бесплатным или сравнивайте с суммой, которая его исключает.

<div id="adapting-it">
  ## Адаптация
</div>

**Условие по количеству товаров вместо суммы:**

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const qualifies = state.itemCount >= 3;
```

**Условие по наличию конкретного товара в корзине:**

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const qualifies = state.items.some((line) => line.productId === TRIGGER_PRODUCT_ID);
```

**Только в определённых странах.** Читайте [`context`](/ru/aftersell/cart/sdk-overview#context), доступный до загрузки корзины:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const ELIGIBLE = ['US', 'CA'];

// Only register the handlers for eligible countries.
if (ELIGIBLE.includes(window.aftersell.cart.context.customer_country)) {
  window.aftersell.cart.events.on('cart_loaded', reconcileGift);
  window.aftersell.cart.events.on('cart_updated', reconcileGift);
}
```

**Исключите подарок из порога**, чтобы он не мог сам себя удерживать:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const subtotalWithoutGift = state.items.reduce(
  (sum, line) => (line.variantId === FREE_GIFT_VARIANT_ID ? sum : sum + line.finalLinePrice),
  0
);
const qualifies = subtotalWithoutGift >= THRESHOLD_CENTS;
```

**Пометьте подарок, чтобы он читался как подарок.** [Line transform](/ru/aftersell/cart/sdk-hooks#registerlinetransform) может изменить заголовок строки, заголовок варианта, состояние скрытия и внутренние свойства — переименуйте его:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.variantId === FREE_GIFT_VARIANT_ID) {
    line.setTitle('Free gift 🎁');
  }
});
```

<Note>
  Line transform **не может скрыть элементы управления количеством строки** — его единственные сеттеры: `setTitle`, `setVariantTitle`, `setHidden` и `setInternalProperties`. Если покупатель изменит количество подарка, корректируйте его из обработчика `cart_updated`.
</Note>

<div id="where-to-go-next">
  ## Что дальше
</div>

* **[Блок Rewards](/ru/aftersell/cart/rewards-block)**: версия без кода, с уровнями.
* **[Действия](/ru/aftersell/cart/sdk-actions)**: `addItem`, `removeItem` и остальные.
* **[События](/ru/aftersell/cart/sdk-events)**: почему важны проверки.
