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

Профиль покупателя - типы плательщика, повторный заказ, правка из кода

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

Решение

Смотрим профили одного покупателя:

\Bitrix\Main\Loader::includeModule('sale');
$rs = \CSaleOrderUserProps::GetList(['DATE_UPDATE' => 'DESC'],
['USER_ID' => $userId], // профили одного покупателя
false, false, ['ID', 'NAME', 'PERSON_TYPE_ID']);
while ($profile = $rs->Fetch()) {
printf("%-4d %-24s тип плательщика %d\n",
$profile['ID'], $profile['NAME'], $profile['PERSON_TYPE_ID']);
}

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

Читаем значения выбранного профиля:

$rs = \CSaleOrderUserPropsValue::GetList([], ['USER_PROPS_ID' => $profileId],
false, false, ['ID', 'ORDER_PROPS_ID', 'NAME', 'VALUE']);
while ($value = $rs->Fetch()) {
printf("%-20s = %s\n", $value['NAME'], $value['VALUE']);
}

Значение связывается с профилем через USER_PROPS_ID, а с полем формы заказа - через ORDER_PROPS_ID. Поэтому один и тот же телефон в двух профилях - это две разные записи.

Смотрим, какие поля ждёт тип плательщика:

$rs = \CSaleOrderProps::GetList([], ['PERSON_TYPE_ID' => $personTypeId],
false, false, ['ID', 'CODE', 'NAME', 'REQUIED']); // да, поле пишется REQUIED
while ($prop = $rs->Fetch()) {
printf("%-4d %-14s обязательное=%s\n",
$prop['ID'], $prop['CODE'], $prop['REQUIED']);
}

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

Добавляем в профиль недостающее поле:

\CSaleOrderUserPropsValue::Add([
'USER_PROPS_ID' => $profileId, // к какому профилю относится значение
'ORDER_PROPS_ID' => $propId, // какое поле формы заказа заполняем
'NAME' => 'Телефон',
'VALUE' => '+7 900 123-45-67',
]);

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

Правим значение в профиле:

\CSaleOrderUserPropsValue::Update($valueId, [
'ID' => $valueId, // ID дублируется внутри массива полей
'VALUE' => '+7 900 765-43-21',
]);

Дублирование идентификатора внутри массива полей - это требование легаси-API, а не описка в примере. Без второй копии ID правка молча не доезжает до базы, и ошибки при этом нет.

Меняем тип плательщика в готовом заказе:

$order = \Bitrix\Sale\Order::load($orderId);
$order->setPersonTypeId($companyTypeId); // коллекция свойств собирается заново
$props = $order->getPropertyCollection();
$props->getItemByOrderPropertyId($innPropId)->setValue('7701234567');
$order->save();

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

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

Д7-замены у этого API нет: профили покупателя остаются в старом ядре и правятся классами CSaleOrderUserProps и CSaleOrderUserPropsValue. Сам заказ при этом собирают объектами D7, и смешение двух ядер в одном обработчике здесь нормально.

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

Заказ создан своей формой, а профиля покупателя нет.

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

В кабинете профиль можно править и удалять, но не добавлять.

У штатного компонента профилей кнопки добавления нет, и это поведение не менялось годами. Новый профиль заводится при оформлении заказа либо своим компонентом на основе типового.

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

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

Тип плательщика не удаляется: в заказах он используется.

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

Покупатель не находит свой профиль в списке при оформлении.

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

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

Может ли покупатель сам добавить новый профиль?

Штатно - нет: типовой компонент профилей умеет только править и удалять. Новый профиль появляется при оформлении очередного заказа, а кнопку добавления делают своим компонентом на основе типового.

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

Напрямую никак: поля профиля у заказа нет, есть только USER_ID. Берут значения нужного профиля и переписывают ими свойства заказа через коллекцию свойств.

Сколько профилей бывает у одного пользователя?

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

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

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

Почему обмен с 1С путает контрагентов при нескольких профилях?

Штатный обмен строит идентификатор контрагента по данным пользователя, а не по профилю. Несколько профилей на один логин в такой схеме схлопываются в одного контрагента.

Смежное

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