Ціни, кількості та валюта
Чому в шаблоні нічого не рахують, звідки береться валюта і як правильно вивести суму та дробову кількість.
На цій сторінці
Правило одне, і воно коротке: у шаблоні нічого не рахують. Числа приходять
готовими, тег {money} тільки форматує.
Чому не можна рахувати в шаблоні
Підсумок замовлення рахує одне місце магазину — те саме, що друкує рахунок, надсилає лист покупцеві і передає суму платіжному шлюзу. У цю суму входять знижки, промокод, бонусні бали, надбавка за спосіб оплати, вартість доставки, додаткові послуги і податок.
Своя формула в шаблоні повторює частину цього списку і рано чи пізно розходиться зі справжнім підсумком: покупець бачить у кошику одну суму, на сторінці оформлення — іншу, а в листі — третю. Таке вже траплялося, причому розходилося не одразу, а після того, як власник вмикав ще одну можливість.
Тому додавати, множити і віднімати суми в шаблоні не можна. Усе потрібне вже пораховано:
{$cart.items_total} сума позицій
{$cart.discount_total} знижки
{$cart.catalog_savings} вигода за каталожними акціями
{$cart.total} підсумок
{$item.total} сума позиції
{$packaging.price} ціна упаковки
{$service.price} вартість послуги
Валюта
Каталог зберігає ціни в основній валюті магазину, а вітрина показує їх у своїй: той самий товар продається і в гривневому магазині, і в доларовому. Переведення робиться на межі видачі, до того, як дані потраплять у шаблон.
Перемикача валют у покупця немає — валюту задає вітрина. Тому ціна на сторінці визначена однозначно, і гадати, у чому вона, не потрібно.
Що це означає на практиці:
- ціни товару, варіантів, упаковок, послуг і аксесуарів — уже у валюті вітрини;
- відсоток знижки рахується до переведення, тому від валюти не залежить;
- суми кошика і оформлення — у валюті кошика, вона ж валюта вітрини;
- суми замовлення — у валюті замовлення, зафіксованій під час оформлення.
Останнє важливе для кабінету покупця і сторінки «замовлення прийнято». Замовлення ніколи не переоцінюється: у нього свій курс і свої ціни. Замовлення, зроблене місяць тому, показується в тих самих грошах, у яких його оформили, навіть якщо курс відтоді змінився.
Як виводити суми
Валюту беруть з даних, а не з налаштувань теми:
{money amount=$product.price currency=$storefront.currency}
{money amount=$cart.total currency=$cart.currency}
{money amount=$order.total currency=$order.currency}
⚠️ Валюту передавайте завжди. За замовчуванням {money} форматує суму
доларами. На тестовому магазині з валютою за замовчуванням помилки не видно — а в
гривневому магазині ціна поїде з доларовим знаком, і помітить це покупець.
Де копійки ламають верстку — сума кошика в шапці, підпис на картці — приберіть їх:
{money amount=$shop->cartTotal() currency=$storefront.currency precision=0}
Закреслена ціна і відсоток знижки
{money amount=$product.price currency=$storefront.currency}
{if $product.compare_price}
<s>{money amount=$product.compare_price currency=$storefront.currency}</s>
<span class="badge">−{$product.discount_percent}%</span>
{/if}
compare_price приходить порожнім, коли закреслювати нічого: стару ціну не задано
або вона не більша за поточну. Рахувати відсоток самому не потрібно — discount_percent
уже ціле число.
Кількості
Товари бувають дробовими: метри, кілограми, площа, літри. Кількості виводять
тегом {qty}:
{qty value=$item.qty} {$item.unit} → 30,8 м²
Тег пише число по-місцевому («30,8», а не «30.8»), відкидає хвостові нулі («2», а не «2,000») і не ставить роздільник розрядів. Останнє не дрібниця: це саме число підставляється в поле введення кількості, і «1 200» браузер прочитав би як «1».
Крок і мінімум кількості приходять разом із товаром:
<input type="number" data-qty
value="{$product.qty_min}"
min="{$product.qty_min}"
step="{$product.qty_step}">
Якщо крок не задано, обидва дорівнюють одиниці — звичайний штучний товар.
Порожній кошик віддає неповний набір
⚠️ Коли кошика ще немає, у $cart приходять лише count, qty, total,
discount_total, discounts, currency, items, in_cart — без
items_total і catalog_savings.
Тому весь блок підсумків обгортають перевіркою:
{if $items}
<div class="cart-totals">
<div>Товари: {money amount=$cart.items_total currency=$cart.currency}</div>
{if $cart.discount_total}
<div>Знижка: −{money amount=$cart.discount_total currency=$cart.currency}</div>
{/if}
<div class="total">Разом: {money amount=$cart.total currency=$cart.currency}</div>
</div>
{else}
<p>У кошику поки порожньо</p>
{/if}
Без цієї перевірки шаблон на порожньому кошику полізе за неіснуючим ключем.
Знижки: звідки взялася ціна
У позиції кошика є discount_reasons — чому ціна нижча за звичайну:
{if $item.compare_price}
<s>{money amount=$item.compare_price currency=$cart.currency}</s>
{foreach $item.discount_reasons as $reason}
<span class="reason">{$reason}</span>
{/foreach}
{/if}
Без пояснення позиція зі знижкою виглядає як помилка в ціні, і покупець пише в підтримку.
У кошика цілком те саме лежить у $cart.discounts — список застосованих
знижок із назвами і сумами.
Позиції, які не можна чіпати
У позиції є ознака locked — її поклав магазин, а не покупець: подарунок,
приз, товар із зафіксованою ціною. У такої позиції не показують лічильник
кількості і кнопку видалення, а підпис note пояснює, звідки вона взялася:
{if $item.locked}
<span class="cart-item__note">{$item.note}</span>
{else}
<input type="number" data-cart-qty="{$item.id}" value="{$item.qty}">
<button data-cart-remove-item="{$item.id}">Видалити</button>
{/if}
Мінімальна сума і безкоштовна доставка
{if $below_min}
<p class="warn">Мінімальна сума замовлення —
{money amount=$min_order currency=$cart.currency}</p>
{/if}
{if $free_shipping_left}
<p>До безкоштовної доставки залишилося
{money amount=$free_shipping_left currency=$cart.currency}</p>
{/if}
free_shipping_left приходить порожнім, якщо додавати вже не потрібно або порога у
магазину немає.