Выбор даты доставки при оформлении заказа в 1С-Битиркс

Родион  Абдурахимов

Веб-разработчик

Выбор даты доставки при оформлении заказа в 1С-Битиркс

Рассмотрим ситуацию, когда при оформлении заказа, пользователь должен иметь возможность выбрать желаемую дату доставки этого заказа. В этом посте я расскажу как вывести на страницу оформления заказа календарь для выбора даты пользователем. Также покажу как можно настроить такой календарь, чтоб он давал сделать выбор даты только по конкретным дням недели.

Постановка задачи

Часто бывает нужно дать пользователю выбрать желаемую дату доставки своего заказа. Эта информация полезна для менеджеров по заказам, так как они могут назначить доставку на то время, которое будет удобно покупателю. Также этот функционал можно настроить так, чтоб пользователь мог выбирать только те даты, которые позволит ему выбрать система. Например, магазин доставляет заказы только по четвергам и пользователь, делая заказ, может указать в какой четверг он готов принять доставку и не сможет выбрать другой день недели.

Нам нужно вывести на страницу оформления заказа календарь, в котором пользователь может выбирать даты и эта дата должна сохраняться в заказе после оформления. Также календарь не должен давать выбрать даты, в которые магазин не сможет произвести доставку. Нам нужна страница, на которой менеджеры магазина смогут настраивать какие дни недели пользователь может выбирать для доставки.

Реализация

Приступим к реализации. Нам понадобится создать новое свойство заказа для сохранения указанной даты доставки в нем, вывести календарь в шаблон компонента оформления заказа, добавить новую административную страницу с настройками дней недели и сконфигурировать календарь, чтоб он не давал выбирать запрещенные даты.

Добавление свойства для сохранения даты в заказе

Добавим свойство типа "Строка" для сохранения выбранной пользователем даты в заказе. Это можно сделать на странице Магазин - Настройки - Свойства заказа - Свойства. Назовем его "Желаемая дата доставки" и дадим ему код DELIVERY_DATE. Остальные настройки свойства оставим стандартными:

Вывод календаря в шаблон оформления заказа

Для вывода календаря в публичную часть мы будем использовать jQuery UI плагин Datepicker. Скачать его можно на официальном сайте http://jqueryui.com/ в разделе загрузок. Перед тем как начать скачивание, вы должны будете указать какие компоненты и виджеты нужны вам в вашей сборке. Для нашего примера были использованы компонент UI Core и один виджет Datepicker. Вот скриншот опций сборки:

После загрузки у вас будет архив с набором файлов CSS и JS для работы с плагином Datepicker. Загрузите их на ваш сервер и подключите в ваш шаблон только для страницы оформления заказа, так как в нашем случае - они нужны только там. Вам также понадобится скачать JS-файл для русификации календаря, так как по умолчанию он на английском языке. Ссылка на этот файл размещена в конце поста.


    Файл home/путь_к_шаблону_сайта/header.php
    ...
    <?if($APPLICATION->GetCurPage(false) == '/personal/order/make/'):?>
        <?$APPLICATION->SetAdditionalCSS(SITE_TEMPLATE_PATH."/js/jquery-ui/jquery-ui.min.css");?>
        <?$APPLICATION->AddHeadScript(SITE_TEMPLATE_PATH."/js/jquery-ui/jquery-ui.min.js");?>
        <?$APPLICATION->AddHeadScript(SITE_TEMPLATE_PATH."/js/jquery-ui/datepicker-ru.js");?>
    <?endif?>
    ...

Нужные библиотеки подключены и теперь мы можем вывести календарь в шаблон оформления заказа. Для этого нам нужно перейти в файл props_format.php шаблона sale.order.ajax. В функции PrintPropsForm() найдем место, где обрабатываются свойства типа "Строка" и заменим вывод input для нового свойства, для последующего вывода календаря рядом с ним:


    Файл home/путь_к_шаблонам_компонентов/sale.order.ajax/props_format.php
    ...
    <?if($arProperties["CODE"] == "DELIVERY_DATE"):?>
        <input
            type="text"
            maxlength="250"
            size="<?=$arProperties["SIZE1"]?>"
            value="<?=$arProperties["VALUE"]?>"
            name="<?=$arProperties["FIELD_NAME"]?>"
            id="datepicker-result"
            readonly="readonly"
            />
        <div id="<?=$arProperties["FIELD_NAME"]?>"></div>
    <?else:?>
        <input
            type="text"
            maxlength="250"
            size="<?=$arProperties["SIZE1"]?>"
            value="<?=$arProperties["VALUE"]?>"
            name="<?=$arProperties["FIELD_NAME"]?>"
            id="<?=$arProperties["FIELD_NAME"]?>"
            />
    <?endif?>
    ...

Следующим шагом будет добавление JavaScript кода для вывода календаря рядом с полем свойства. Перейдем в файл template.php шаблона sale.order.ajax и под JS-функцией SetContact() добавим такой код, показанный ниже. Замените #ORDER_PROP_20 на id вашего поля свойства.


    Файл home/путь_к_шаблонам_компонентов/sale.order.ajax/template.php
    ...
    function makeDatepicker() {
        $('#ORDER_PROP_20').datepicker({
            altField: '#datepicker-result'
        });
    }
    $(document).ready(function () {
        makeDatepicker();
        BX.addCustomEvent('onAjaxSuccess', makeDatepicker);
    });
    ...

Здесь мы добавили функцию makeDatePicker() и вызвали ее при загрузке страницы, а также после ajax-запросов компонента. В результате мы видим такую картину в шаблоне оформления заказа:

Сейчас пользователь может выбрать любую желаемую дату доставки и сохранить эту дату в заказе. Нам же нужно настроить календарь так, чтоб можно было выбрать только конкретные дни недели. Перейдем к добавлению такой страницы настроек.

Создание административной страницы для настройки доступных дней доставки

Добавим новый php-файл в папке /bitrix/admin/. Назовем его order_delivery_settings.php и добавим в него такой код:


    Файл home/bitrix/admin/order_delivery_settings.php
    ...
    <?php
    define("LANG", "ru");
    define("NO_KEEP_STATISTIC", true);
    define("NOT_CHECK_PERMISSIONS", true);

    require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_before.php");

    $saleModulePermissions = $APPLICATION->GetGroupRight("sale");
    if ($saleModulePermissions == "D")
        $APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
    ?>

    <?/** GET|POST */?>
    <?

    //on submit form
    if(isset($_REQUEST["set_filter"]) && strlen($_REQUEST["set_filter"])>0)
    {
        //set order delivery days of week
        if(isset($_REQUEST['DELIVERY_DAYS']) && count($_REQUEST['DELIVERY_DAYS']) > 0)
        {
            COption::SetOptionString("main", 'DELIVERY_DAYS', serialize($_REQUEST['DELIVERY_DAYS']));
        }
        else
        {
            COption::SetOptionString("main", 'DELIVERY_DAYS', "");
        }
    }
    ?>
    <?/** HTML */?>
    <?
    $APPLICATION->SetTitle("Дни доставок заказов");
    require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_after.php");
    ?>
        <form name="find_form" method="POST" action="<?echo $APPLICATION->GetCurUri()?>">
            <table class="adm-filter-main-table">
                <tbody>
                <tr>
                    <td class="adm-filter-main-table-cell">
                        <div class="adm-filter-content" id="tbl_sale_stat_filter_content">
                            <div class="adm-filter-content-table-wrap">
                                <table cellspacing="0" class="adm-filter-content-table" id="tbl_sale_stat_filter" style="display: table;">
                                    <tbody>
                                    <tr id="tbl_sale_stat_filter_row_0_delim" style="display: table-row;"><td colspan="3" class="delimiter"><div class="empty"></div></td></tr><tr id="tbl_sale_stat_filter_row_1" style="display: table-row;">
                                        <td valign="top" class="adm-filter-item-left">Дни выдачи заказов:</td>
                                        <td class="adm-filter-item-center">
                                            <div class="adm-filter-alignment">
                                                <div class="adm-filter-box-sizing">
                                                    <span class="adm-select-wrap-multiple">
                                                        <?
                                                        $arDeliveryDays = unserialize(COption::GetOptionString("main", "DELIVERY_DAYS"));
                                                        ?>
                                                        <select class="adm-select-multiple" name="DELIVERY_DAYS[]" multiple="multiple" title="Выбор периода" size="8">
                                                            <option value="Monday" <?= in_array("Monday", $arDeliveryDays) ? "selected='selected'" : ""?>>Понедельник</option>
                                                            <option value="Tuesday" <?= in_array("Tuesday", $arDeliveryDays) ? "selected='selected'" : ""?>>Вторник</option>
                                                            <option value="Wednesday" <?= in_array("Wednesday", $arDeliveryDays) ? "selected='selected'" : ""?>>Среда</option>
                                                            <option value="Thursday" <?= in_array("Thursday", $arDeliveryDays) ? "selected='selected'" : ""?>>Четверг</option>
                                                            <option value="Friday" <?= in_array("Friday", $arDeliveryDays) ? "selected='selected'" : ""?>>Пятница</option>
                                                            <option value="Saturday" <?= in_array("Saturday", $arDeliveryDays) ? "selected='selected'" : ""?>>Суббота</option>
                                                            <option value="Sunday" <?= in_array("Sunday", $arDeliveryDays) ? "selected='selected'" : ""?>>Вокресенье</option>
                                                        </select>
                                                    </span>
                                                    <label>
                                                </div>
                                            </div>
                                        </td>
                                    </tr>
                                    <tr id="tbl_sale_stat_filter_row_1_delim" style="display: table-row;">
                                        <td colspan="3" class="delimiter"><div class="empty"></div></td>
                                    </tr>
                                    </tbody>
                                </table>
                            </div>
                            <div class="adm-filter-bottom-separate" id="tbl_sale_stat_filter_bottom_separator" style="display: block;"></div>
                            <div class="adm-filter-bottom">
                                <input type="submit" class="adm-btn" id="tbl_sale_stat_filterset_filter" name="set_filter" title="Выполнить" value="Выполнить">
                            </div>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </form>
    <?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_admin.php");?>
    ...

Этот код выводит в административной странице форму с множественным списком выбора дней недели. Можно выбрать какие дни недели будут доступны для выбора в календаре. Вот как выглядит эта страница:

Страница доступна по прямой ссылке http://example.com/bitrix/admin/order_delivery_settings.php, но можно еще добавить ее в меню подраздела "Магазин". Это можно сделать зарегистрировав в файле init.php такой обработчик:


    Файл home/bitrix/php_interface/init.php
    ...
    AddEventHandler("main", "OnBuildGlobalMenu", "OnBuildGlobalMenuHandler");
    function OnBuildGlobalMenuHandler(&$adminMenu, &$moduleMenu)
    {
        $moduleMenu[] = array(
            "parent_menu" => "global_menu_store",
            "sort" => 101,
            "text" => 'Дни доставок заказов',
            "url" => "order_delivery_settings.php?lang=ru"
        );
    }
    ...

Для решения задачи нам осталось лишь запретить виджету календаря делать выбор тех дат, дни недели которых, не выбраны в настройках этой страницы.

Отключение в календаре недоступных дат

Для определения доступных дат в календаре нам нужно реализовать PHP-функцию, которая будет возвращать массив доступных дат. Вот код этой функции:


    Файл home/bitrix/php_interface/init.php
    ...
    function getAllowedDays()
    {
        $arResult = array();
        $arDeliveryDays = unserialize(COption::GetOptionString("main", "DELIVERY_DAYS"));

        if(count($arDeliveryDays) > 0)
        {
            $begin = new DateTime('next '.$arDeliveryDays[0]);
        }
        else
        {
            $begin = new DateTime();
        }

        //set period end for 4 week ahead
        $end = new DateTime();
        $end = $end->modify('+4 weeks');

        //set interval for 1 day in step
        $interval = new DateInterval('P1D');
        $dateRange = new DatePeriod($begin, $interval, $end);

        //from first allowed delivery day to 6 week ahead for 1 day step
        foreach($dateRange as $date)
        {
            $deliveryDayTimestamp = $date->getTimestamp();

            $printDate = $date->format("j.n.Y");
            $dayOfWeek = $date->format('l');

            //include date in result if it is delivery day and not manually excepted by admin day
            if(in_array($dayOfWeek, $arDeliveryDays))
            {
                $arResult[$deliveryDayTimestamp] = $printDate;
            }
        }

        return $arResult;
    }
    ...

Эта функция извлекает из БД наши сохраненные на административной странице разрешенные дни недели и строит массив дат на 1 месяц вперед, которые пользователь сможет выбрать в календаре. Последним шагом будет внесение изменений в JS-функцию makeDatepicker(), чтоб она исключила из календаря запрещенные даты. Вот как нам нужно исправить функцию makeDatepicker():


    Файл home/путь_к_шаблонам_компонентов/sale.order.ajax/template.php
    ...
    function makeDatepicker() {
        <?
        $arDeliveryDates = getAllowedDays();
        ?>
        var availableDates = [
            <?foreach($arDeliveryDates as $date):?>
            "<?=$date?>",
            <?endforeach?>
        ];

        function unavailable(date) {
            dmy = date.getDate() + "." + (date.getMonth()+1) + "." +date.getFullYear();
            if ($.inArray(dmy, availableDates) >= 0) {
                return [true,"","День выдачи доступен"];
            } else {
                return [false,"","День выдачи недоступен"];
            }
        }

        $('#ORDER_PROP_20').datepicker({
            beforeShowDay: unavailable,
            altField: '#datepicker-result',
            defaultDate: availableDates[0]
        });
    }
    ...

Что здесь происходит: мы с помощью PHP-вставки добываем с помощью функции getAllowedDays() массив доступных дат и строим из него JS-массив. Далее с помощью API календаря мы передаем этот массив и сообщаем ему, что только эти даты из массива можно выбирать в календаре. В результате мы видим в календаре только даты, которые есть четвергами на месяц вперед, другие выбрать нельзя:

Если мы на административной странице добавим еще какой-то день недели, то календарь позволит выбрать новые доступные даты. На скриншоте ниже доступные дни выбраны понедельник и пятница:

Ссылки

Похожие посты

Мы подобрали посты, которые могут быть вам интересны

Комментарии

Тут без вас никак. Поделитесь с нами вашими мыслями

Горячие вакансии