msOptionsPrice2 и склады


Долго ломал голову, как связать msOptionsPrice2 и наличие товара на складах. В модификациях товара у меня хранятся размеры и кол-во. Искал решение или компонент, который бы позволил сделать что-то подобное. Как оказалось, искал не там… В итоге сделал всё на базе msOptionsPrice2 и костыля, как это часто бывает.

1. Опция со списком складов


Создаём miniShop2 опцию — stocks с типом Список с автодополнением. В ней у каждого товара будет храниться список складов, на которых он предположительно присутствует.




2. Отображение пустых складов


Если вам нужно отображать склад с нулевым кол-вом товара, указываем системную настройку msoptionsprice_allow_zero_count = true

Лирическое отступление


Не тратьте время, если не хотите погружаться в детали, сразу переходите к пункту 3.

В момент выбора размера на фронте, компонент msOptionsPrice2 обращается в процессор web/modification/get, в котором он получает список модификаций путём цикличного вызова метода getModificationByOptions, который в свою очередь возвращает модификацию по переданным в него опциям. К примеру, если мы передадим в этот метод 1 опцию (size), а у нашего товара все модификации с 2-мя опциями (size и stocks), то метод нам ничего не вернёт. Это нам создаёт большие проблемы на фронте, т.к. если у модификации 2 опции, а при выборе размера в процессор передаётся одна, то метод getModificationByOptions ничего не возвращает.



Чтобы решить это, у нас есть системная настройка msoptionsprice_exclude_modification_options. В ней можно указать опцию, которая не будет участвовать в запросе.
Но! При создании/обновлении модификации, компонент в процессоре вызывает тот-же самый метод getModificationByOptions. Если мы укажем опцию stocks в этой настройке, то при создании/обновлении модификации с одним и тем-же размером, но разными складами, компонент будет выдавать ошибку, т.к. для него модификация с размером уже будет существовать, ибо в этом случае он не проверяет опцию stocks.

Вот тут был основной «затык», ибо мне на миг показалось, что ничего придумать уже нельзя...
Благо в методе есть вызов события плагина msopOnBeforeGetModification, в которое мы можем «вклиниться» и временно присвоить настройке msoptionsprice_exclude_modification_options нужную опцию, когда вызов метода происходит с фронтенда, и ничего не присваивать, если метод вызывается из бекенда!

3. Плагин


$sp = &$scriptProperties;
switch ($modx->event->name) {
    case 'msopOnBeforeGetModification':
        if (preg_match('/^mgr.*/i', $_POST['action'])) {
            break;
        }
        $modx->setOption('msoptionsprice_exclude_modification_options', 'stocks');
        $modx->event->returnedValues['rid'] = $sp['rid'];
        $modx->event->returnedValues['options'] = $sp['options'];
        $modx->event->returnedValues['excludeIds'] = $sp['excludeIds'];
        $modx->event->returnedValues['excludeType'] = $sp['excludeType'];
        break;
}
Вешаем на событие msopOnBeforeGetModification.

4.1. Фронт-энд, HTML


В шаблоне товара делаем что-то вроде этого:
<form class="[ ms2_form msoptionsprice-product ]" method="post">
    <input type="hidden" name="id" value="{$_modx->resource['id']}">
    <input type="hidden" name="options[stocks]" value="">
    ...
    <div class="product-stocks [ js-stocks ]">
        <table class="product-stocks__table">
            {if $_modx->resource['stocks'] && $_modx->resource['stocks'][0]}
                {foreach $_modx->resource['stocks'] as $v}
                    <tbody>
                        <tr data-stocks-name="{$v}" style="display:none">
                            <td>
                                <div class="product-stocks__name">{$v}</div>
                            </td>
                            <td class="product-stocks__count [ js-stocks-value ]">
                                -
                            </td>
                        </tr>
                    </tbody>
                {/foreach}
            {/if}
        </table>
    </div>
    ...
</form>
Важно сохранить скрытое поле options[stocks], аттрибут data-stocks-name, класс msoptionsprice-product у формы и все классы с префиксом js!

4.2. Фронт-энд, JS


После подключения jQuery прописываем примерно такой JS:
$(document).on('msoptionsprice_product_action', function (e, action, form, response) {
    if (action != 'modification/get') {
        return;
    }
    if (response['success'] && response['data']) {
        var $stocks = $(document).find('.js-stocks');
        var modifications = response.data['modifications'];
        $stocks.find('[data-stocks-name]').hide();
        if (modifications['length']) {
            modifications.forEach(function (modification) {
                var stock = ('stocks' in modification['options']) ? modification['options']['stocks'] : '';
                var $stock = $stocks.find('[data-stocks-name="' + stock + '"]');
                if ($stock['length']) {
                    var $value = $stock.find('.js-stocks-value');
                    if ($value['length']) {
                        $value.html(modification['count']);
                        $stock.show();
                    }
                }
            });
        }
    }
});

P.S. Благодарность Володе за то, что он предусмотрел и событие плагина в нужном месте, и триггер jQuery! Если он оптимизирует компонент на работу со складами из коробки, будет очень круто!
Павел Гвоздь
04 сентября 2018, 14:55
modx.pro
10
2 164
+15
Поблагодарить автора Отправить деньги

Комментарии: 2

Алексей Шумаев
04 сентября 2018, 18:54
+2
Павел, спасибо.
msOptionsPrice2 — классный компонент. Из моего опыта — я его сильно модифицировал (оптовые цены, совместимость с msDiscount) под проект и заложенный функционал сильно порадовал.
    Павел Гвоздь
    04 сентября 2018, 19:23
    +1
    Да, Володя написал хороший продукт. Я уже ни раз пользуюсь нативными фишками, расширяя его функционал — modx.pro/howto/13043
    Благодарю за отзыв!
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    2