Кейсы

07 марта 2023, 11:30

Акция "Купи два - получи третий в подарок"

При заказе больше двух штук одного товара, третий будет бесплатным, но добавить его покупатель должен сам.

Акция

Акция "Черная пятница"

Скидка на все товары в течение одних суток в определённый день недели.

Акция

Акция "Техника Bosch со скидкой"

Скидка на все товары производителя

Акция

Акция "Мебель за полцены"

Скидка на определённую категорию товаров

Акция

Акция "Ликвидация остатков"

Скидка на определённые товары

Акция

Акция "Зарегистрируйся и купи дешевле"

Скидка только для зарегистрированных и авторизованных пользователей

Акция

Акция "Скидка при покупке от 1000 рублей"

Скидка с ограничением по минимальной сумме заказа

Акция

Акция "Летняя распродажа"

Скидка на все товары в течение определенного периода

Акция

Акция "Попробуй новое и сэкономь"

Скидка на товары с активным свойством "Новый"

Акция

Промокоды

Встроенного функционала нет, но его можно создать под ваши нужды. Ниже приведён один из вариантов реализации.

  1. Создадим новую опцию promocode

    Создадим новую опцию

  2. Создадим новую скидку-промокод. Здесь важно добавить опцию promocode со значением промокода.

    Создадим новую скидку-промокод

  3. Вероятно, Вам захочется сделать ограниченное число промокодов. Установите компонент Migx. Создайте конфигурацию promocodes_count (название может быть любым) с полями promocode и count (название полей строго такие).

    Создайте конфигурацию

  4. Создайте TV с именем promocodes_count (название может быть любым) и присвойте любому удобному шаблону, я присвоил шаблону 1 (это нам понадобится дальше).
  5. Заполните созданное TV тем значением, которое указали при создании скидки в п.2 и укажите доступное количество промокодов.
  6. Теперь нам нужна форма для ввода промокода. Я использовал компонент AjaxFormitLogin и вызвал форму на странице корзины. Вызов выглядит так
{'!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 имеет следующее ссодержимое


<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>
  1. Также потребуется проверка введённого промокода. Проверять будем применён ли уже этот промокод текущим пользователем, существовует и активна ли скидка связанная с промокодом, остаток промокодов. Для этого создадим свой сниппет-валидатор promocode со следующим содержимым
require_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;
  1. Кроме того после применения проммокода, нужно изменять остаток и обновлять корзину. Это мы сделаем в сниппите-хуке applyPromocode с таким кодом
require_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;
  1. Чтобы сниппеты работали нужно создать класс Promocodes в файле elements/promocodes.class.php с кодом

    class 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);
        }
    }
    }
  2. Чтобы после применения промокода скидка применялась ко вновь добавляемым в корзину товарам, нам понадобится ещё плагин
    switch ($modx->event->name) {
    case 'msOnAddToCart':
        $tmp = $cart->get();
        $tmp[$key]['options']['promocode'] = $_SESSION['promocode'];
        $cart->set($tmp);
        break;
    }

Теперь Вы можете создавать какие угодно промокоды в каком угодно количестве и гибко настраивать к каким товарам их применять.



Следующий документ
Перед началом работы