Своё поле в оформлении заказа - свойство, группа, свой тип
Добавляем на оформление заказа своё поле: от выбора типа плательщика до собственного типа поля с проверкой значения и выводом в шаблоне компонента.
Механика
Свойства заказа принадлежат конкретному типу плательщика, а не заказу вообще. Физическое и юридическое лицо в магазине заполняют разные наборы полей, и это разделение задаётся именно типом плательщика.
Свойства собираются в группы, и группы тоже привязаны к типу плательщика. Группа определяет, в каком блоке формы оформления окажется поле и в каком порядке оно там встанет.
Порядок настройки магазина обязателен и хорошо проверяется на практике. Сначала заводят типы плательщиков, затем группы свойств, затем сами свойства, и только после этого настраивают доставки и оплаты.
У каждого свойства есть тип, который определяет разметку поля. Строка, число, флажок, список, множественный список и местоположение - разные типы с разным поведением при выводе и сохранении.
Списочные типы требуют отдельно заведённых вариантов возможных значений. Само свойство хранит только выбранный вариант, а список вариантов живёт отдельной таблицей и правится отдельно.
Свойство местоположения обслуживает расчёт доставки и налогов. У каждого типа плательщика должно быть ровно одно такое свойство, иначе расчёт доставки не находит город покупателя.
Обязательность поля задаётся отдельным признаком у самого свойства. В старом API имя этого признака написано с опечаткой, и это приходится учитывать при работе с устаревшими методами.
Свой тип поля заводят наследником базового класса и регистрируют менеджером типов. После регистрации тип появляется в списке типов свойства при создании нового свойства заказа.
Поддержку своего типа поля в штатном компоненте оформления добавляют руками. Компонент собирает разметку по известным ему типам, и новый тип он рисовать сам не научится.
Значение свойства сохраняется вместе с заказом и живёт в его свойствах. Читают и пишут его через коллекцию свойств заказа, а не через отдельную таблицу напрямую.
Отдельный вопрос - где проверять введённое значение. Проверка в браузере помогает покупателю заполнить форму правильно, но заказ приходит и прямым запросом мимо неё, поэтому серверная проверка обязательна.
Ставят её либо в классе своего типа поля, либо в обработчике события оформления. Первое подходит для правил самого поля, второе - для правил, связывающих несколько полей заказа между собой.
Шаги
- Определить, у какого именно типа плательщика должно появиться новое поле.
- Завести или выбрать подходящую группу свойств для этого типа плательщика.
- Создать свойство нужного типа с кодом, сортировкой и нужным признаком обязательности.
- Для списочного типа завести варианты значений нужными записями.
- При необходимости завести свой тип поля и зарегистрировать его менеджером типов.
- Научить шаблон компонента оформления выводить это поле правильно и полностью.
- Проверить сохранение значения и его отображение в административной части сайта.
Код
Смотрим типы плательщиков и группы свойств:
\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());}// значения живут в коллекции свойств заказа, а не в отдельной своей таблицеКоллекция свойств заказа - штатный способ добраться до значений. Прямые запросы к таблицам мимо неё ломаются при изменениях платформы и не поднимают нужных событий.
Ограничения
Свойство заказа принадлежит своему типу плательщика навсегда. Перенести его к другому типу нельзя: заводят новое свойство и переносят значения отдельным скриптом.
Штатный компонент оформления знает только свои собственные типы полей. Собственный тип требует правки шаблона компонента, и эту работу учитывают в оценке задачи сразу.
Обязательность поля проверяется на сервере, а не только в браузере. Проверка в разметке помогает покупателю, но заказ создаётся и через прямой запрос мимо формы.
Массовая правка свойств у уже существующих заказов - отдельная задача. Новое свойство не появляется у старых заказов само, и заполнять его приходится скриптом.
Типичные проблемы
Новое свойство не показывается на оформлении.
Оно заведено у другого типа плательщика или у него не задана группа. Форма оформления собирается по типу плательщика и его группам свойств.
Списочное поле выводится пустым.
У этого свойства не заведены варианты возможных значений. Само свойство хранит только выбранный вариант, а список вариантов ведётся отдельно.
Расчёт доставки не находит город покупателя.
У этого типа плательщика нет свойства с признаком местоположения. Именно оно связывает форму оформления заказа с расчётом доставки и налогов.
Свой тип поля не появляется в списке типов свойства.
Класс типа не зарегистрирован менеджером типов или регистрация выполняется слишком поздно. Регистрацию типа вешают на событие подключения модуля продаж.
Поле выводится, но значение не сохраняется.
Имя поля в шаблоне не совпадает с ожидаемым именем этого свойства. Компонент собирает значения полей по именам, которые сам же и формирует.
Частые вопросы
Чем свойство заказа отличается от пользовательского поля?
Свойство заказа принадлежит типу плательщика и участвует в оформлении, а пользовательское поле - механизм ядра для произвольных сущностей. В оформлении заказа работают именно свойства.
Можно ли обойтись без своего типа поля?
Обычно да: строки, списка и флажка хватает большинству задач. Свой тип заводят ради проверки значения и особой разметки, а не ради названия поля.
Где хранятся значения свойств заказа?
В служебных таблицах модуля продаж, привязанных к заказу. Читать и писать их следует через коллекцию свойств заказа, а не запросами к таблицам.
Как сделать поле обязательным только для доставки курьером?
Через связь свойства со службой доставки или проверкой в обработчике события оформления. Признак обязательности сам по себе условий не понимает.
Что будет со старыми заказами после добавления свойства?
У них значение останется пустым: свойство появляется только у новых заказов. Заполнение старых заказов делают отдельным скриптом, если оно вообще нужно.
Смежное
-
Оформление заказа - оглавление подтемы
-
Оформление заказа: настройка компонента и правка шаблона - как устроен сам компонент
-
События оформления заказа: свои проверки, запрет и допданные - проверка значений на сервере
-
Свойства заказа: чтение, запись и фильтрация по значению - работа со значениями из кода
-
Настройка магазина по шагам: что за чем включать - обязательный порядок настройки
-
Местоположения: импорт, свойство заказа, доставки и индекс - свойство местоположения подробнее
-
Интернет-магазин на 1С-Битрикс - устройство магазина целиком
-
Заказ для юрлица: тип плательщика, реквизиты, счёт - поля, привязанные к типу плательщика