У більшості рушіїв інтернет-магазинів ціна товару — це одне число в одному полі бази даних. Працює бездоганно, поки магазин має один склад і продає всім за однією ціною. Проблема виникає, щойно з'являється другий склад із власною закупівельною вартістю або гуртовий покупець, якому потрібна інша ціна за той самий товар.
Ця стаття про те, чому «один товар — одна ціна» ламається саме в цій точці, і що довелося порахувати в ядрі власного рушія LiteShop, щоб цього уникнути.
Де саме ламається модель з однією ціною
Уявімо магазин з двома складами: один у Києві, товар туди завезли за старою закупівельною ціною, другий у Львові — та сама позиція, але завезена пізніше й дорожче. Класичний рушій, у якому ціна прив'язана до товару, а не до пари «товар + склад», змушує вибирати: або тримати два різні товарні записи для однієї й тієї ж позиції (і плутати покупця вибором «однакового» товару двічі), або продавати з одного складу собі в збиток, поки не вирівняється середня закупівельна.
Та сама проблема з гуртовими рівнями: постійний клієнт із замовленнями на суму понад певний поріг має отримувати іншу ціну за той самий артикул, ніж роздрібний покупець. Якщо ціна — одне поле, це потребує або ручної системи знижок поверх стандартного рушія, або окремого прайсу, який синхронізується вручну і розходиться з основним каталогом за перший же тиждень.
Що це означає для архітектури, а не лише для інтерфейсу
Правильне рішення — не косметичне (додати поле «знижка» в картку товару), а структурне: ціна має належати парі «товар + склад» і додатково модифікуватися рівнем покупця. Це означає окрему таблицю цін, а не одне поле, і логіку вибору правильного ряду цін під час кожного перегляду картки товару й кожного розрахунку кошика — без сповільнення сторінки, бо цей розрахунок відбувається на кожному запиті.
Ми будували цю логіку в ядрі власного рушія LiteShop: кілька складів із різною закупівельною ціною одного товару, рівні гуртових цін і складський облік — не надбудова поверх типового рушія, а частина того, як побудована модель товару з самого початку. 42 модулі ядра (доставка, оплата, склад, лояльність, SEO) вмикаються перемикачем в адмінці, а не встановлюються окремими плагінами, які потім конфліктують одне з одним.
Чи не сповільнює це магазин
Додаткова логіка розрахунку ціни на кожен перегляд — природне питання про швидкість. На проді одного з магазинів, побудованих на цьому рушії (Hetzner cx43), перша відповідь головної сторінки холодна дорівнює теплій — 167 мс, замір 19.05.2026. Це можливо саме тому, що розрахунок ціни за складом і рівнем клієнта кешується в Redis: заміри 12.06.2026 показали 98,3% влучань у кеш при нулі витіснених ключів — тобто кеш не встигає переповнюватися й видаляти актуальні дані навіть під живим навантаженням.
Чого рушій поки не робить — чесно
Резервування товару з таймером у кошику відсутнє: залишок списується під час оформлення замовлення, не в момент додавання в кошик. Імпорту каталогу напряму з OpenCart немає. Інтеграції з Rozetka Delivery і Meest поки заглушки, не робоча функція. Ці обмеження — не приховані дрібним шрифтом, а прямо озвучені: рушій молодий, і множинні склади з гуртовими рівнями — це та частина, яку зробили міцною з першого дня, а решту добудовують послідовно.
Кому це взагалі потрібно
Якщо у вас один склад, кілька сотень товарів і немає планів на гуртові рівні чи доробки — типовий рушій чи оренда конструктора обійдуться дешевше, і чесний розбір задачі скаже це першим листом, а не після оплати. Потреба у структурному рішенні виникає від двох складів чи постачальників, від необхідності в гуртових рівнях цін, або коли типовий конструктор уже впирається у власні обмеження.