Конфигурируемая система промо-цен для WooCommerce
Задача
Интернет-магазину требовалось запускать разные механики скидок без переписывания расчёта под каждую кампанию. В одной акции скидку получает каждый второй товар, в другой цена зависит от полной группы из трёх позиций, в третьей первый товар в паре продаётся со скидкой, а второй — по фиксированной цене. Для общей распродажи нужно было снизить цену всего каталога на заданный процент.
Нужно было согласовать промо-условия витрины с расчётом корзины, заказа и данными для SAP. Для акций на пары и тройки итоговая цена каждой единицы определяется составом корзины и распределением скидки. Процентный купон применяется к сумме после промо. Расчёт должен быть воспроизводимым: одинаковый состав корзины даёт одинаковое распределение независимо от порядка строк.
Роль и объём
Спроектировал и реализовал отдельный pricing-модуль для WooCommerce: общий контракт стратегии, фабрику выбора, четыре алгоритма промо, нормализацию корзины до отдельных товарных единиц и единый результат расчёта. Подключил модуль к витрине, корзине, checkout и заказу через адаптеры WooCommerce, согласовал его с процентными купонами и подготовил данные для SAP.
Настройки кампании вынес в ACF Options. Бизнес-пользователь выбирает одну активную механику и задаёт её параметры, а код интерфейсов и интеграций продолжает работать с одним и тем же контрактом результата.
Ограничения
- Одновременно активна одна кампания и один набор её параметров. Сочетание нескольких промо-механик в одном расчёте не входило в задачу.
- Параметры и момент переключения кампании задаются вручную ответственным сотрудником через ACF Options.
- Процентный купон применяется после промо к уже уменьшенной базе. Остальные типы купонов сохраняют штатные правила WooCommerce.
- Штатная или полученная из SAP акционная цена не ухудшается глобальной распродажей: покупателю остаётся более выгодная цена.
- Распределение выполняется по товарным единицам, поэтому несколько экземпляров одной позиции могут получить разные условия одной акции.
- Система реализует заранее определённые бизнес-правила. Персонализация цены, прогнозирование спроса и dynamic pricing на основе ML в неё не входят.
Архитектура
ACF Options: активная кампания + версия + параметры
│
▼
Pricing Configuration
├── валидация процентов и фиксированных цен
├── нормализация и версионирование настроек
└── некорректная конфигурация → базовая цена
│
▼
Discount Strategy Factory
├── steps_2 — каждый второй товар
├── steps_3 — группы по три
├── constant_value — процент + фиксированная цена
└── global_sale — общая процентная распродажа
│
▼
Pricing Service
├── строка × quantity → отдельные товарные единицы
├── сортировка по цене + стабильный tie-break
├── Money и единая политика округления
└── неизменяемые DiscountResult и allocations
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Woo adapters Order snapshot SAP adapter
каталог стратегия SKU + скидка
карточка версия цена + количество
мини-корзина параметры срок поставки
cart/checkout allocations
Strategy здесь решает бизнес-задачу смены промо-механик. Маркетинг может переключить кампанию и её параметры, а витрина, корзина, заказ и SAP не получают отдельные ветки условий для каждой новой формулы. Каждая стратегия отвечает только за распределение цены между товарными единицами; правила денег, округления, сохранения результата и интеграции остаются общими.
Расчёт и распределение скидки
Перед расчётом pricing-сервис разворачивает количество каждой строки в отдельные единицы. Например, строка с количеством 3 участвует в акции как три самостоятельных экземпляра. Допустимые позиции сортируются по исходной цене по убыванию. Для одинаковой цены используется стабильный tie-break по идентификатору товара, ключу строки и порядковому номеру экземпляра, поэтому одинаковая корзина всегда даёт одинаковое распределение.
Фабрика принимает только whitelist известных идентификаторов и создаёт одну из четырёх стратегий:
steps_2распределяет условия попарно: каждый второй экземпляр получает настроенную скидку; для нечётного остатка действует отдельное правило первого товара.steps_3собирает полные тройки и применяет к ним условие для трёх товаров. Остаток из одной или двух единиц получает соответствующие настройки своей группы.constant_valueобрабатывает пары: первый экземпляр получает процентную скидку, второй — заданную фиксированную цену; для одиночного остатка предусмотрен собственный процент.global_saleприменяет один процент ко всем подходящим товарам и вариациям каталога.
Любая стратегия возвращает неизменяемый DiscountResult: исходную и итоговую сумму, размер скидки и список allocation для каждой товарной единицы. В allocation записаны товар, строка корзины, порядковый номер, исходная цена, применённое правило, процент или фиксированная цена и итог. Из этого объекта строятся представление цены, корректировка корзины, итог заказа и пакет для SAP — повторных независимых вычислений в разных шаблонах нет.
Денежные значения проходят через единый Money-слой на базе WooCommerce precision helpers. Промежуточные операции выполняются в целых единицах повышенной точности, а пользовательская цена и скидка округляются по единой политике до целой единицы валюты магазина. Общая политика округления обеспечивает согласованность денежных значений при расчёте корзины, формировании заказа и подготовке данных для SAP.
Интеграция с WooCommerce
Адаптеры WooCommerce используют общий pricing-сервис. Глобальная процентная распродажа учитывается в ценах каталога и карточки товара. Для акций на пары и тройки итоговое распределение скидки рассчитывается по составу корзины. При изменении количества или применении купона выполняется новый расчёт. Ключ расчёта предотвращает накопление скидки при повторном вызове WooCommerce в одном цикле.
Процентные купоны считаются построчно от суммы после промо. Сначала сервис распределяет скидочную цену между товарными единицами и собирает уменьшенную сумму строки, затем к ней применяется процент купона с той же политикой округления. Этот же порядок используется при формировании заказа.
Для global_sale адаптер покрывает обычные товары, вариации и диапазоны цен вариативного товара. Активная кампания и её версия входят в variation price hash, поэтому WooCommerce не возвращает устаревшую цену из кэша после изменения настроек. Перед подстановкой глобальной скидки сервис сравнивает её со штатной sale price и ценой, пришедшей из SAP, и выбирает минимальную допустимую цену.
Заказ и SAP
При создании заказа сохраняется snapshot расчёта: идентификатор стратегии, версия конфигурации, нормализованные параметры, политика округления и allocations по товарным единицам. Поэтому последующее изменение ACF-настроек не меняет экономику уже оформленного заказа. Повторный пересчёт и отправка в SAP используют snapshot, а не текущую активную кампанию.
SAP-адаптер группирует allocations по SKU и фактически применённому условию. В одну позицию попадают только экземпляры с одинаковой ценой и скидкой; для каждой группы передаются количество, исходная и итоговая цена, тип скидки и рассчитанный срок поставки. Идемпотентный ключ строится из заказа и версии snapshot, поэтому повторная отправка воспроизводит тот же пакет и не создаёт новую интерпретацию промо.
Надёжность и проверка
Конфигурация валидируется до создания стратегии: идентификатор должен входить в whitelist, проценты — находиться в разрешённом диапазоне, фиксированная цена — быть неотрицательной, а обязательные поля выбранной схемы — присутствовать. При ошибке модуль работает fail-closed: возвращает базовую цену, не создаёт частичную скидку и фиксирует код причины для диагностики.
В журнал попадают идентификатор стратегии и версии, хеш нормализованной конфигурации, идентификатор расчёта, состав правил и код ошибки. Персональные данные покупателя, содержимое адресов и платёжные данные не записываются.
Результат
Бизнес переключает одну из четырёх промо-механик через настройки. Корзина и заказ используют согласованный расчёт, а SAP получает сохранённое распределение цен и скидок по товарным единицам. Снимок расчёта сохраняет условия оформленной покупки, стабильная сортировка обеспечивает воспроизводимость, а некорректная конфигурация возвращает базовую цену.
Стек
PHP · WordPress · WooCommerce · ACF · Strategy · SAP API
Есть похожая задача?
Расскажите о текущей системе и нужных изменениях — обсудим следующий шаг.