Кейсы использования
Акция "Купи два - получи третий в подарок"
При заказе больше двух штук одного товара, третий будет бесплатным, но добавить его покупатель должен сам.
Акция "Черная пятница"
Скидка на все товары в течение одних суток в определённый день недели.
Акция "Техника Bosch со скидкой"
Скидка на все товары производителя.
Акция "Мебель за полцены"
Скидка на определённую категорию товаров.
Акция "Ликвидация остатков"
Скидка на определённые товары.
Акция "Зарегистрируйся и купи дешевле"
Скидка только для зарегистрированных и авторизованных пользователей.
Акция "Скидка при покупке от 1000 рублей"
Скидка с ограничением по минимальной сумме заказа.
Акция "Летняя распродажа"
Скидка на все товары в течение определенного периода.
Акция "Попробуй новое и сэкономь"
Скидка на товары с активным свойством "Новый".
Промокоды
Встроенного функционала нет, но его можно создать под ваши нужды. Ниже приведён один из вариантов реализации.
Создадим новую скидку-промокод. Здесь важно добавить опцию
promocode
со значением промокода.Вероятно, Вам захочется сделать ограниченное число промокодов. Установите компонент
Migx
. Создайте конфигурациюpromocodes_count
(название может быть любым) с полямиpromocode
иcount
(название полей строго такие).Создайте TV с именем
promocodes_count
(название может быть любым) и присвойте любому удобному шаблону, я присвоил шаблону1
(это нам понадобится дальше).Заполните созданное TV тем значением, которое указали при создании скидки в п.2 и укажите доступное количество промокодов.
Теперь нам нужна форма для ввода промокода. Я использовал компонент
AjaxFormitLogin
и вызвал форму на странице корзины. Вызов выглядит такfenom{'!AjaxFormitLogin' | snippet:[ 'form' => '@FILE promocodeForm.tpl', 'hooks' => 'applyPromocode', 'customValidators' => 'promocode', 'validate' => 'promocode:required:promocode=^1|promocodes_count^', 'aliases' => 'promocode==Промокод', 'promocode.vTextRequired' => 'Не передан.', 'successMessage' => 'Промокод применён', 'transmittedParams' => '{"success" : "", "error" : "aliases"}' ]}
Чанк формы
promocodeForm.tpl
имеет следующее содержимоеhtml<form class=""> <div class="input-group mb-3"> <input type="text" class="form-control" placeholder="Введите промокод" name="promocode"> <button class="btn btn-success" type="submit">Применить</button> </div> </form>
Также потребуется проверка введённого промокода. Проверять будем применён ли уже этот промокод текущим пользователем, существует и активна ли скидка связанная с промокодом, остаток промокодов. Для этого создадим свой сниппет-валидатор
promocode
со следующим содержимымphprequire_once MODX_CORE_PATH . 'elements/promocodes.class.php'; $params = explode('|', $param); $promocodeHandler = new Promocodes($modx, $value, $params[0], $params[1]); $msg = $promocodeHandler->validate(); if(is_string($msg)){ $validator->addError($key, $msg); return false; } return true;
Кроме того после применения проммокода, нужно изменять остаток и обновлять корзину. Это мы сделаем в сниппите-хуке
applyPromocode
с таким кодомphprequire_once MODX_CORE_PATH . 'elements/promocodes.class.php'; preg_match('/promocode=\^(.*?)\^/', $hook->formit->config['validate'], $matches); $params = explode('|', $matches[1]); $promocodeHandler = new Promocodes($modx, $_POST['promocode'], $params[0], $params[1]); $promocodeHandler->process(); return true;
Чтобы сниппеты работали нужно создать класс
Promocodes
в файлеelements/promocodes.class.php
с кодомphpclass Promocodes { public function __construct($modx, $promocode, $rid, $tvname) { $this->modx = $modx; $this->modx->getService('mspdDiscounts', 'mspdDiscounts', MODX_CORE_PATH . 'components/msproductdiscounts/model/'); $this->promocode = $promocode; $this->rid = $rid; $this->tvname = $tvname; $this->init(); } private function init() { $tableName = $this->modx->getTableName('mspdOption'); $q = $this->modx->newQuery('mspdDiscount'); $q->where("active = 1 AND id = (SELECT did FROM $tableName WHERE option_value = '$this->promocode')"); $this->query = $q; if ($this->resource = $this->modx->getObject('modResource', $this->rid)) { if ($codes = $this->resource->getTVValue($this->tvname)) { $this->codes = $this->reformatMIGX(json_decode($codes, 1)); } } } private function reformatMIGX($migx) { $output = array(); foreach ($migx as $item) { $output[$item['promocode']] = $item['count']; } return $output; } public function validate() { if ($this->isApplied()) return ' Уже применён Вами.'; if (!$this->isActive()) return ' Не найден.'; if (!$this->validateCount()) return ' Количество исчерпано.'; return true; } private function isApplied() { return $_SESSION['promocode'] === $this->promocode; } private function isActive() { return (bool)$this->modx->getCount('mspdOption', $this->query); } private function validateCount() { if (empty($this->codes)) return false; return $this->codes[$this->promocode] > 0; } public function process(){ $_SESSION['promocode'] = $this->promocode; $this->updateCount(); $this->updateCart(); } private function updateCount(){ $this->codes[$this->promocode] = ($this->codes[$this->promocode] - 1 >= 0) ? $this->codes[$this->promocode] - 1 : 0; if($codes = $this->resource->getTVValue($this->tvname)){ $codes = json_decode($codes,1); foreach($codes as $k => $item){ if($item['promocode'] === $this->promocode){ $codes[$k]['count'] = $this->codes[$this->promocode]; } } $this->resource->setTVValue($this->tvname, json_encode($codes)); $this->resource->save(); } } private function updateCart(){ $ms2 = $this->modx->getService('minishop2'); $ms2->initialize($this->modx->context->get('key')); if($tmp = $ms2->cart->get()){ foreach($tmp as $k => $item){ $tmp[$k]['options']['promocode'] = $this->promocode; } uasort($tmp, function ($a, $b) { $aprice = $_SESSION['start_cart_state'][$a['key']]['price'] ?: $a['price']; $bprice = $_SESSION['start_cart_state'][$b['key']]['price'] ?: $b['price']; if ($aprice == $bprice) { return 0; } return ($aprice > $bprice) ? -1 : 1; }); $tmp = $this->modx->mspdDiscounts->prepareProductDiscounts($tmp); $ms2->cart->set($tmp); } } }
Чтобы после применения промокода скидка применялась ко вновь добавляемым в корзину товарам, нам понадобится ещё плагин
phpswitch ($modx->event->name) { case 'msOnAddToCart': $tmp = $cart->get(); $tmp[$key]['options']['promocode'] = $_SESSION['promocode']; $cart->set($tmp); break; }
Теперь Вы можете создавать какие угодно промокоды в каком угодно количестве и гибко настраивать к каким товарам их применять.