Перейти к содержимому

Своё поле в оформлении заказа - свойство, группа, свой тип

Добавляем на оформление заказа своё поле: от выбора типа плательщика до собственного типа поля с проверкой значения и выводом в шаблоне компонента.

Механика

Свойства заказа принадлежат конкретному типу плательщика, а не заказу вообще. Физическое и юридическое лицо в магазине заполняют разные наборы полей, и это разделение задаётся именно типом плательщика.

Свойства собираются в группы, и группы тоже привязаны к типу плательщика. Группа определяет, в каком блоке формы оформления окажется поле и в каком порядке оно там встанет.

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

У каждого свойства есть тип, который определяет разметку поля. Строка, число, флажок, список, множественный список и местоположение - разные типы с разным поведением при выводе и сохранении.

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

Свойство местоположения обслуживает расчёт доставки и налогов. У каждого типа плательщика должно быть ровно одно такое свойство, иначе расчёт доставки не находит город покупателя.

Обязательность поля задаётся отдельным признаком у самого свойства. В старом API имя этого признака написано с опечаткой, и это приходится учитывать при работе с устаревшими методами.

Свой тип поля заводят наследником базового класса и регистрируют менеджером типов. После регистрации тип появляется в списке типов свойства при создании нового свойства заказа.

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

Значение свойства сохраняется вместе с заказом и живёт в его свойствах. Читают и пишут его через коллекцию свойств заказа, а не через отдельную таблицу напрямую.

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

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

Шаги

  1. Определить, у какого именно типа плательщика должно появиться новое поле.
  2. Завести или выбрать подходящую группу свойств для этого типа плательщика.
  3. Создать свойство нужного типа с кодом, сортировкой и нужным признаком обязательности.
  4. Для списочного типа завести варианты значений нужными записями.
  5. При необходимости завести свой тип поля и зарегистрировать его менеджером типов.
  6. Научить шаблон компонента оформления выводить это поле правильно и полностью.
  7. Проверить сохранение значения и его отображение в административной части сайта.

Код

Смотрим типы плательщиков и группы свойств:

\Bitrix\Main\Loader::includeModule('sale');
$persons = \Bitrix\Sale\PersonTypeTable::getList([
'select' => ['ID', 'NAME', 'ACTIVE'],
])->fetchAll();
print_r($persons); // свойства заводятся под конкретный тип плательщика

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

Читаем свойства выбранного типа:

$props = \Bitrix\Sale\Internals\OrderPropsTable::getList([
'select' => ['ID', 'CODE', 'NAME', 'TYPE', 'REQUIED', 'IS_LOCATION', 'SORT'],
'filter' => ['=PERSON_TYPE_ID' => $personTypeId],
'order' => ['SORT' => 'ASC'],
])->fetchAll();
print_r($props); // признак обязательности в старом API назван с опечаткой
// IS_LOCATION отмечает свойство, по которому считается доставка

Список свойств показывает и порядок вывода, и признаки, влияющие на поведение формы. Отсюда же видно, есть ли у типа плательщика свойство местоположения, нужное для расчёта доставки.

Заводим варианты для списочного свойства:

\CSaleOrderPropsVariant::Add([
'ORDER_PROPS_ID' => $propertyId,
'NAME' => 'Заберу сам',
'VALUE' => 'SELF', // значение попадёт в свойство заказа
'SORT' => 100,
]);
// варианты нужны типам «список», «множественный список» и «переключатель»
// сортировка определяет порядок вариантов в выпадающем списке

Списочное свойство без вариантов выводится пустым выпадающим списком. Значение варианта уезжает в заказ, а название видит покупатель, и путать их при разборе заказа не стоит.

Регистрируем свой тип поля:

use Bitrix\Sale\Internals\Input\Manager;
Manager::register('vendor_inn', [
'CLASS' => \Vendor\Module\Sale\InnInput::class, // наследник базового класса
'NAME' => 'ИНН с проверкой',
]);
// регистрацию выполняют на обработчике события при подключении модуля продаж
// после регистрации тип виден при создании нового свойства заказа

Свой тип нужен там, где штатной строки недостаточно: поле с маской, проверкой контрольной суммы или подсказками. Класс типа отвечает и за разметку поля, и за проверку введённого значения.

Реализуем класс своего типа:

class InnInput extends \Bitrix\Sale\Internals\Input\Base
{
public static function getEditHtmlSingle($name, array $input, $value)
{
return '<input name="' . $name . '" value="' . htmlspecialcharsbx($value)
. '" inputmode="numeric" maxlength="12">';
}
public static function getErrorSingle(array $input, $value): array
{
return preg_match('/^\d{10}(\d{2})?$/', (string)$value)
? [] : ['Проверьте ИНН: ожидается 10 или 12 цифр'];
}
}

Класс типа отдаёт разметку поля и список ошибок проверки. Ошибка возвращается списком строк, и покупатель видит её рядом с полем, а не общим сообщением формы.

Читаем значение свойства у сохранённого заказа:

$order = \Bitrix\Sale\Order::load($orderId);
foreach ($order->getPropertyCollection() as $property) {
printf("%s = %s\n", $property->getField('CODE'), $property->getValue());
}
// значения живут в коллекции свойств заказа, а не в отдельной своей таблице

Коллекция свойств заказа - штатный способ добраться до значений. Прямые запросы к таблицам мимо неё ломаются при изменениях платформы и не поднимают нужных событий.

Ограничения

Свойство заказа принадлежит своему типу плательщика навсегда. Перенести его к другому типу нельзя: заводят новое свойство и переносят значения отдельным скриптом.

Штатный компонент оформления знает только свои собственные типы полей. Собственный тип требует правки шаблона компонента, и эту работу учитывают в оценке задачи сразу.

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

Массовая правка свойств у уже существующих заказов - отдельная задача. Новое свойство не появляется у старых заказов само, и заполнять его приходится скриптом.

Типичные проблемы

Новое свойство не показывается на оформлении.

Оно заведено у другого типа плательщика или у него не задана группа. Форма оформления собирается по типу плательщика и его группам свойств.

Списочное поле выводится пустым.

У этого свойства не заведены варианты возможных значений. Само свойство хранит только выбранный вариант, а список вариантов ведётся отдельно.

Расчёт доставки не находит город покупателя.

У этого типа плательщика нет свойства с признаком местоположения. Именно оно связывает форму оформления заказа с расчётом доставки и налогов.

Свой тип поля не появляется в списке типов свойства.

Класс типа не зарегистрирован менеджером типов или регистрация выполняется слишком поздно. Регистрацию типа вешают на событие подключения модуля продаж.

Поле выводится, но значение не сохраняется.

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

Частые вопросы

Чем свойство заказа отличается от пользовательского поля?

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

Можно ли обойтись без своего типа поля?

Обычно да: строки, списка и флажка хватает большинству задач. Свой тип заводят ради проверки значения и особой разметки, а не ради названия поля.

Где хранятся значения свойств заказа?

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

Как сделать поле обязательным только для доставки курьером?

Через связь свойства со службой доставки или проверкой в обработчике события оформления. Признак обязательности сам по себе условий не понимает.

Что будет со старыми заказами после добавления свойства?

У них значение останется пустым: свойство появляется только у новых заказов. Заполнение старых заказов делают отдельным скриптом, если оно вообще нужно.

Смежное

Первоисточник