Документація

Дані товару

Поля картки в списку та сторінки товару: ціна, наявність, характеристики, варіанти купівлі, упаковки та послуги.

На цій сторінці

Товар приходить у шаблон одним набором полів. Той самий набір — у категорії, пошуку, у бренда, за міткою, у добірках головної, в обраному, у порівнянні, у рекомендаціях і в аксесуарах. На сторінці товару до нього додається те, що потрібно лише картці: опис, галерея, варіанти купівлі, упаковки, послуги.

Картка в списку

Поле Що це
id, slug, name, url товар і адреса його сторінки
sku код показаного варіанта
sku_id номер варіанта, який картка показує зараз — саме він іде в кошик
price ціна у валюті вітрини
compare_price закреслена ціна або порожньо
discount_percent цілий відсоток для мітки «−N%» або порожньо
unit одиниця продажу підписом: «шт», «м²»
pack_only товар продається лише упаковками
rating_avg, rating_count оцінка і число відгуків
summary короткий опис — для вигляду «списком»
in_stock є на складі
is_available можна купити
preorder немає в наявності, але купити можна
count скільки залишилося всього
count_by_warehouse залишок за складами, які видно покупцеві
sku_count залишок показаного варіанта
image, image2, gallery головне фото, друге (для наведення) і до п'яти знімків
matched_sku варіант, підібраний фільтром
features, features_by_code характеристики
variants, variant_skus, variants_json варіанти купівлі, якщо тема їх показує

Мінімальна картка

<article class="card" data-in-cart="{$product.id}">
    <a href="{$product.url}">
        {if $product.image}
            <img src="{thumb src=$product.image size=320}" alt="{$product.name}">
        {else}
            <img src="{$theme.assets}/img/no-photo.svg" alt="">
        {/if}
        <h3>{$product.name}</h3>
    </a>

    <div class="card__price">
        {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}
    </div>

    {if $product.preorder}
        <button data-add="{$product.id}" data-sku="{$product.sku_id}">Передзамовлення</button>
    {elseif $product.is_available}
        <button data-add="{$product.id}" data-sku="{$product.sku_id}">До кошика</button>
    {else}
        <span class="card__out">Немає в наявності</span>
    {/if}
</article>

Товар без фото — звичайна річ, гілку із заглушкою пишіть одразу: інакше сітка розійдеться на першому ж магазині, куди фотографії ще не завантажили.

Наявність: три поля, і плутати їх не можна

Поле Що означає
in_stock товар фізично є на складі
is_available покупець може натиснути «Купити»
preorder купити можна, але товару немає — це передзамовлення

Правильна розмітка кнопки — спочатку передзамовлення, потім звичайна купівля:

{if $product.preorder}Передзамовлення
{elseif $product.is_available}До кошика
{else}Немає в наявності{/if}

Коли товар знову з'явиться

Власник може поставити в товару дату надходження. Магазин віддає її темі готовою — і на плитці в добірці, і на сторінці товару:

Поле Що це
restock_text дата словами: «05 вересня 2026»
restock_at та сама дата числом, «2026-09-05» — якщо потрібен свій формат

Поля заповнені, тільки поки товару немає в наявності, і лише якщо дата ще не минула: перевіряти це в темі не потрібно, достатньо {if $product.restock_text}. Товар з'явився — поля порожні, і рядок зникає сам.

Рядок ставте окремо від гілки «немає в наявності»: при дозволеному передзамовленні кнопка купівлі лишається, ця гілка не малюється, а дата покупцеві потрібна тим більше.

{if $product.restock_text}
    <div class="card__restock">Очікується: {$product.restock_text}</div>
{/if}

Залишки

count і sku_count бувають порожніми, і це не нуль: порожньо означає, що залишок у товару не рахують зовсім — послуги, товари на замовлення. Написати «залишилося 0 шт.» у цьому випадку не можна.

{if $product.count !== null && $product.count > 0}
    Залишилося {qty value=$product.count} {$product.unit}
{/if}

Від'ємних чисел не буває: перепроданий залишок приходить нулем.

Розбивка за складами:

{foreach $product.count_by_warehouse as $w}
    {$w.name}{if $w.city}, {$w.city}{/if}: {qty value=$w.count}
{/foreach}

Поля складу: id, name, city, count, priority. Склади, які власник не позначив як видимі покупцеві, у розбивку не потрапляють.

Характеристики

{foreach $product.features as $f}
    <dt>{$f.name}</dt>
    <dd>
        {if $f.link}<a href="{$f.link}">{$f.value}</a>{else}{$f.value}{/if}
        {if $f.unit} {$f.unit}{/if}
    </dd>
{/foreach}

Поля: id, code, name, value, unit, link (адреса посадкової сторінки, якщо доповнення її дало), sku_id, values.

Один рядок — одна характеристика. У «Кольору» чи «Інтернету» значень буває кілька; магазин зводить їх у value через кому, а не видає три рядки «Колір» поспіль. Так само поводяться списки товарів, тому картка й плитка в добірці показують товар однаково.

Розібрані значення лежать поруч, у values: у кожного id, label, color (колір із довідника, якщо заданий) і link (своя посадкова сторінка). Перебір values дає те, чого рядок через кому не вміє, — кружечки кольорів і посилання на те значення, за яким покупець клікнув:

{foreach $f.values as $v}{if !$v@first}, {/if}
    {if $v.color}<i class="spec-dot" style="background: {$v.color|escape}"></i>{/if}
    {if $v.link}<a href="{$v.link}">{$v.label}</a>{else}{$v.label}{/if}
{/foreach}

Посилання рядка (link) заповнюється, тільки коли значення одне: у характеристики з трьома значеннями незрозуміло, куди вело б одне посилання на весь рядок.

У товару без характеристик це порожній список, а не відсутнє поле{if $product.features} безпечний.

Доступ за кодом — коли потрібна конкретна характеристика:

{if $product.features_by_code.color}
    Колір: {$product.features_by_code.color.value}
{/if}

Сторінка товару

Понад поля картки приходить:

Поле Що це
summary, description, type описи і тип товару
qty_step, qty_min крок і мінімум кількості
gallery знімки: thumb, preview, full, sku_id
variants групи опцій купівлі
packagings упаковки
services додаткові послуги
round_services чи округлювати вартість послуг до цілих
accessories групи аксесуарів
short_features перші п'ять характеристик
default_sku_id варіант, вибраний за замовчуванням
tags, rating мітки і оцінка
json карта варіантів для браузера

Плюс своє: breadcrumbs, rec_cross і rec_up (рекомендації), reviews, questions.

Дробові кількості

qty_step і qty_min — крок і мінімум. Якщо крок не задано, обидва дорівнюють одиниці. Товар продається метрами або кілограмами — лічильник зобов'язаний їх дотримуватися:

<input type="number" data-qty
       value="{$product.qty_min}"
       min="{$product.qty_min}"
       step="{$product.qty_step}">

Варіанти купівлі

{foreach $product.variants as $group}
    <div class="variant" data-variant-group="{$group.feature_id}">
        <span>{$group.name}</span>
        {foreach $group.values as $v}
            <button data-variant-value="{$v.id}"
                    {if $group.type == 'color'}style="background: {$v.color}"{/if}>
                {$v.label}
            </button>
        {/foreach}
    </div>
{/foreach}

Група: feature_id, name, type (text, color, number), values (id, label, color), selected.

Якщо у варіантів немає опцій, але самих варіантів більше одного, магазин робить перемикач сам: за числовою характеристикою, якщо вона є у кожного варіанта і всюди різна, інакше за назвами варіантів.

Карта варіантів для браузера

<script type="application/json" data-product-skus>{$product.json nofilter}</script>

Усередині — тільки дані: skus, default, currency, locale, preorder. Підписи («в наявності», «до кошика») туди не кладуться — вони належать темі і передаються окремо, атрибутами data-l-*.

Варіант у карті: id, sku, price, compare, available, preorder, sellable, stock, gallery, stocks, options, packagings.

sellable зі значенням «ні» означає «знято з продажу», а не «закінчився» — підписку на надходження в цьому випадку пропонувати не треба. У складу в stocks є level (ok, low, critical); пороги задає власник у картці складу, не задав — завжди ok.

Не забудьте nofilter: без нього в розмітку поїде екранований текст, і скрипти не розберуть карту.

Упаковки

{foreach $product.packagings as $p}
    <label>
        <input type="radio" name="packaging" value="{$p.id}" {if $p.is_default}checked{/if}>
        {$p.name} — {$p.factor} {$product.unit},
        {money amount=$p.price currency=$storefront.currency}
    </label>
{/foreach}

Поля: id, name, factor (скільки одиниць в упаковці), is_default, price.

Своєї ціни в упаковки немає — вона рахується як ціна одиниці, помножена на коефіцієнт. Тому розійтися з кошиком їй нізвідки, і своя арифметика в шаблоні тут не потрібна.

У варіанта купівлі буває своя упаковка, і тоді загальні не показуються.

Лічильник пачок із підписом «6 пачок — це 8,4 м²» магазин робить сам, тема дає розмітку — див. Атрибути data-*.

Додаткові послуги

{foreach $product.services as $s}
    <fieldset>
        <legend>{$s.name}{if $s.required} *{/if}</legend>
        {if $s.description}<p>{$s.description}</p>{/if}
        {foreach $s.variants as $v}
            <label>
                <input type="radio" name="service[{$s.id}]" value="{$v.id}"
                       {if $v.is_default}checked{/if}>
                {$v.name} — {money amount=$v.price currency=$storefront.currency}
            </label>
        {/foreach}
    </fieldset>
{/foreach}

Послуга: id, name, description, required, default_variant, variants. Варіант: id, name, price_type (fixed або percent), value, price, is_default. Послуга без варіантів не приходить.

price уже пораховано — для відсоткових послуг це сума, а не відсоток. Якщо round_services увімкнено, вартість послуг округлюється до цілих; клієнтська розмітка зобов'язана округлювати так само, як сервер, інакше покупець побачить одну суму, а заплатить іншу.

Ціна і знижка

Ціну, за якою товар продається, магазин рахує в одному місці і віддає готовою:

  • price — ціна продажу: уже з усіма каталожними акціями і вже у валюті вітрини;
  • compare_price — закреслена: найбільше з «ціни до акції» і «старої ціни, заданої власником». Вийшло не більше за поточну — приходить порожньо;
  • discount_percent — рахується до переведення валюти, тому цифра однакова на будь-якій вітрині.

Один товар коштує однаково в списку, на своїй сторінці, у карті варіантів і в кошику — це правило магазин тримає перевіркою, зокрема для випадків, коли у варіанта своя валюта, а вітрина торгує в третій.

Докладніше — на сторінці Ціни, кількості і валюта.

Оновлено 24 серпня 2026