Битрикс. Раздаём заказы нескольким операторам


Пришла задача написать системку распределения заказов для операторов.
Первоначальное задание:
1. Нужна страничка с одной кнопкой. Оператор нажимает эту кнопочку и получает заказ на обработку.
2. Оператор может:
  • Отказаться от заказа (например, не дозвонились пользователю). В этом случае заказ спит некоторое время, а потом возвращается.
  • Отменить заказ штатной кнопочкой Отменить - у нас это равносильно по смыслу удалению заказа (удалять заказы операторам нельзя)
  • Изменить статус заказа, чем снимает с себя обязательства по обработке заказа, закрывает заказ.
  • И пограничный случай - если заказ заблокирован кем-то ещё, то оператор может снять с себя этот заказ. Пример - создание тестовых заказов, которые обрабатываются непосредственно теми, кто тестирует. В этом случае заказ оператору может попасть, но будет перехвачен из реального списка тестировщиком.

После некоторых дебатов, было решено делать это всё отдельным модулем, чтобы удобнее дополнять, внедрять и весь код был в одном месте.

Версия модуля предварительная, доработок там требуется ещё с тонну. Код - жестокая смесь двух стилей - стандартного битриксового и D7. Где получалось - исправила на D7. Вообще, возможностей нового ядра пока чуток не хватает (про документацию лучше помолчу).
Итак, сегодня в статье микс из описаний использованных решений и самой схемы работы модуля.

Использованные решения
Я надеюсь, что создание модуля вам подвластно, а если нет, то подвластно гугление. Поэтому особо задерживаться тут не будем.
Хотя для начала расскажу про использованные возможности платформы и про то, как они были использованы.

Сохранение данных при удалении модуля
Делается это с помощью двух шагов при деинсталляции модуля. Создаются два файла unstep1 и unstep2. В функции DoUninstall делаем вызовы с условиями

$step = IntVal($step);
 
 //шаг первый, спрашиваем, удалять ли данные вместе с файлами модуля
 if ($step < 2)
 {
  $APPLICATION->IncludeAdminFile(
   $this->MODULE_NAME.': удаление, шаг 1',
   $moduleAbsPath."/install/unstep1.php"
  );
 }
 elseif ($step == 2) 
 {
  $this->UnInstallFiles();
  //соответственно функцию удаления данных вызываем с результатом первого шага
  //и внутри неё обрабатываем этот аргумент
  $this->UnInstallDB(array(
   "savedata" => $_REQUEST["savedata"],
  ));
  $this->UnInstallEvents();
   
  $obModule = $this;
  $APPLICATION->IncludeAdminFile(
   $this->MODULE_NAME.': удаление, шаг 2',
   $moduleAbsPath."/install/unstep2.php"
  );
 }

В файлах шагов обычный html-код формы, здесь приводить не буду.

Кастомная форма просмотра заказа

Кто-то в офисе когда-то слышал про такую возможность. Действительно, она есть. И уважаемый Долганин в одном из своих комментов разъяснил, как этим пользоваться. Информации - море, конечно.
Ну ладно, лезем в /bitrix/modules/sale/admin/order_detail.php
Теперь глубоко дышим и смотрим на 5 тыщ строк =)

Находим указанную строку, выкусываем текст между фигурными скобками после else (кстати, у меня ушло некоторое время, чтобы найти где этот блок заканчивается), забрасываем к себе в файлик и добавляем уже нужные нам поля.

ae86ecbadac3916a03eaa5a6a5e5457f.png


Для проверки надо указать путь от корня сайта до нашего кастомного файла: Настройки -- Настройки продукта -- Настройки модулей -- Интернет магазин -- Файл с пользовательской формой просмотра заказа

Создание ORM-класса для таблицы
Это вообще делается очень просто =) Правда нужна версия битрикс, поддерживающая данную возможность.
Идём на страницу Настройки -- Производительность -- Таблицы. Видим список табличек, существующих в системе. Теперь к урлу добавляем &orm=y
Получаем тот же список табличек за одним исключением: в бутерброде около таблиц появился пункт ORM

987152f3802c38c1b129b50505034c80.png

Тыкаем на него и получаем почти полный код класса для работы с нашей табличкой.
В модулях классы D7 лежат в папочке lib, имя файла с классом должно совпадать с именем класса до части Table и должно быть маленькими буковками. То есть в нашем случае получается класс DopordersTable и имя файла doporders.php. Кстати, сгенерированный код потребует добавления функции getFilePath, но там всё просто.
И всё, теперь с нашей таблицей можно работать через стандартные функции, предоставляемые D7.

Общее описание
1. Кроновский скрипт спрашивает необработанные заказы: заказ не залочен, имеет статус Принят (у нас он под айди N) и свойство Отменён не выставлено (то есть не равно Y). Потом эти заказы записываются в нашу распределительную таблицу.
Также этот скрипт возвращает в обработку отказанные заказы.
2. Страничка с кнопочкой "Хочу заказ" и списком залоченных в данный момент на текущего оператора заказов. Как всегда мне лень делать нормальные формы и я всё делаю аджаксом.
3. Настройки модуля включают:
  • время отдыха отказанных заказов
  • группы пользователей, имеющих доступ к странице с кнопочкой
  • статусы заказов, перевод в которые вызывает закрытие заказа
4. Инит модуля с обработчиками двух событий:
  • смена статуса заказа
  • отмена заказа
5. Кастомная форма просмотра заказа включает форму отказа и снятия заказа с текущего оператора. Также действия обрабатываются аджаксом.
6. Возможные статусы заказа в модуле:
  • new - новый, только что пришёл на обработку
  • owned - взят оператором на обработку
  • done - обработан
  • canceled - отменён, больше не будет обрабатываться
  • rejected - отказан, ещё вернётся в распределение
Сценарии работы оператора
1. Нормальный. Оператор нажимает кнопочку, получает заказ, переходит к странице заказа.
1.1 Оператор успешно обрабатывает заказ, выставляя статус "Обработан"
1.2 Оператор отменяет заказ (например фейковый, или пользователь отказался)
1.3 Оператор отказывает заказу в выполнении (например не может дозвониться до пользователя), указывая причину отказа. По истечении определённого времени (указывается в настройках модуля) такой заказ возвращается обратно в схему распределения.

2. Пограничный. Оператору выдан заказ, а он занят другим оператором. Помимо непосредственного вопроса вживую почему занят этот заказ - у оператора есть возможность снять с себя этот заказ.

Обработка сценариев
Выдача заказа осуществляется запросом апдейт, как известно неявно блокирующим строку (если у вас иннодб, конечно), так что кто успел то и съел, то есть получил заказ.

Пункты 1.1 и 1.2 обрабатываются по событиям OnSaleBeforeStatusOrder и OnSaleBeforeCancelOrder в ините модуля

Пункт 1.3 реализуется засчёт кастомизации страницы просмотра заказа, там выводится текстареа и кнопочка. Если заказ залочен за другим оператором, то форма заблокирована.

Пункт 2 реализуется также кастомизацией страницы просмотра заказа. В таком случае оператору даётся возможность снять с себя этот заказ. При этом заказ удаляется из таблицы распределения. Заказ вернётся в распределение как только будет удовлетворять условиям скрипта сбора. Ну или не вернётся, если это действительно тестовый заказ или если его напрямую взяли на обработку.

Пункты на развитие
1. сильно напрашивается логгирование (сейчас не хотят, потом точно захотят)
2. страничка статистики
3. вкладочка в админке, где оператор может увидеть свои выполненные заказы

Ядро D7 в документации


Просмотров: 4451