Почему не работает скидка в день рождения для программно созданных партнеров в УТ 11 и как это исправить

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При массовом переносе данных или интеграции 1С с внешними системами (например, сайтами или мобильными приложениями) разработчики часто сталкиваются с ситуацией, когда созданные программным путем клиенты выглядят абсолютно идентично созданным вручную. Однако при оформлении продаж типовые механизмы ведут себя непредсказуемо. Один из ярких примеров — скидка в день рождения в конфигурации «1С:Управление торговлей, редакция 11» (УТ 11.4 / 11.5). Программно созданный клиент с заполненной датой рождения и привязанной картой лояльности не получает автоматическую скидку, в то время как созданный вручную клиент при аналогичных условиях обслуживается корректно.

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

В чем кроется причина проблемы?

Когда мы сравниваем двух партнеров в консоли запросов, мы видим плоскую таблицу реквизитов справочника Справочник.Партнеры. На первый взгляд, все поля (включая дату рождения, статус, тип и текстовое представление) совпадают. Но в платформе 1С бизнес-логика сложнее простого хранения данных в одной таблице. Причиной сбоя механизма скидок при программном создании клиентов являются следующие факторы:

  1. Обход обработчиков событий (режим загрузки). При импорте больших массивов данных (например, 2000 клиентов) разработчики почти всегда используют свойство ОбменДанными.Загрузка = Истина. Это отключает стандартные проверки в модулях объектов и менеджеров справочников. В результате объект записывается как «пустая оболочка» без запуска сопутствующих бизнес-процессов системы.
  2. Разделение сущностей «Партнеры», «Контрагенты» и «Физические лица». В архитектуре УТ 11 справочник Партнеры отвечает за управленческий учет, а Контрагенты — за регламентированный (юридический). Более того, для частных лиц критически важен справочник ФизическиеЛица. Дата рождения, используемая в расчетах автоматических скидок, хранится не только (и не столько) в самом партнере, сколько в связанном с ним элементе справочника ФизическиеЛица. Если эта связь нарушена или карточка физлица не создана, скидка работать не будет.
  3. Отсутствие записей в служебных регистрах сведений. При интерактивном создании партнера система автоматически делает записи в регистры сведений, такие как СоставПартнеров, АналитикаУчетаПоПартнерам и другие. Программная запись в режиме загрузки эти регистры игнорирует.
  4. Неактуальность сегментов. Если скидка ко дню рождения привязана к определенному сегменту клиентов (например, динамическому сегменту «Именинники»), программно созданные партнеры не попадут туда автоматически до тех пор, пока не будет выполнено принудительное обновление состава сегмента.

---

Шаг 1. Анализируем архитектуру связей в базе данных

Рассмотрим, как именно типовой механизм расчета скидок определяет именинника. При расчете скидки система обращается к связанному физическому лицу. Давайте проверим наши данные с помощью запроса. Нам нужно выяснить, у каких партнеров (с типом «Частное лицо») отсутствует привязанное физическое лицо или не совпадает дата рождения.

Выполним следующий запрос в консоли запросов:


ВЫБРАТЬ
	Партнеры.Ссылка КАК Партнер,
	Партнеры.Наименование КАК ПартнерНаименование,
	Партнеры.ФизическоеЛицо КАК ФизЛицо,
	Партнеры.ДатаРождения КАК ДатаРожденияПартнера,
	Партнеры.ФизическоеЛицо.ДатаРождения КАК ДатаРожденияФизЛица
ИЗ
	Справочник.Партнеры КАК Партнеры
ГДЕ
	Партнеры.ЮрФизЛицо = ЗНАЧЕНИЕ(Перечисление.КомпанияЧастноеЛицо.ЧастноеЛицо)
	И (Партнеры.ФизическоеЛицо = ЗНАЧЕНИЕ(Справочник.ФизическиеЛица.ПустаяСсылка)
		ИЛИ Партнеры.ФизическоеЛицо.ДатаРождения <> Партнеры.ДатаРождения)

Если этот запрос вернул список партнеров, то мы нашли первопричину. У программно созданных клиентов либо вообще не заполнено поле ФизическоеЛицо, либо внутри карточки физического лица отсутствует (или отличается) дата рождения. Из-за этого алгоритм расчета скидок, анализируя объект ФизическиеЛица, считает, что у клиента нет дня рождения.

---

Шаг 2. Разрабатываем алгоритм исправления данных

Для того чтобы исправить ситуацию для уже перенесенных клиентов (в нашем примере их около 2000), напишем внешнюю обработку. Нам необходимо решить три задачи:

  1. Найти или создать для каждого партнера-физлица соответствующий элемент в справочнике ФизическиеЛица.
  2. Заполнить в найденном/созданном физлице дату рождения, соответствующую дате рождения партнера.
  3. Связать партнера с этим физлицом и корректно перезаписать объекты без флага загрузки, чтобы отработали системные механизмы.

Рассмотрим пример кода, который выполняет эту процедуру:


Процедура ИсправитьДанныхПартнеров() Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	Партнеры.Ссылка КАК Партнер,
		|	Партнеры.Наименование КАК Наименование,
		|	Партнеры.ДатаРождения КАК ДатаРождения,
		|	Партнеры.ФизическоеЛицо КАК ФизическоеЛицо
		|ИЗ
		|	Справочник.Партнеры КАК Партнеры
		|ГДЕ
		|	Партнеры.ЮрФизЛицо = ЗНАЧЕНИЕ(Перечисление.КомпанияЧастноеЛицо.ЧастноеЛицо)
		|	И НЕ Партнеры.ПометкаУдаления";
	
	РезультатЗапроса = Запрос.Выполнить();
	Выборка = РезультатЗапроса.Выбрать();
	
	Пока Выборка.Следующий() Цикл
		
		МодифицированПартнер = Ложь;
		ТекущееФизЛицо = Выборка.ФизическоеЛицо;
		
		// Если физлицо не привязано к партнеру
		Если НЕ ЗначениеЗаполнено(ТекущееФизЛицо) Тогда
			
			// Попробуем найти физлицо по наименованию (ФИО)
			НайденноеФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(Выборка.Наименование, Истина);
			
			Если ЗначениеЗаполнено(НайденноеФизЛицо) Тогда
				ТекущееФизЛицо = НайденноеФизЛицо;
			Иначе
				// Создаем новое физическое лицо
				ФизЛицоОбъект = Справочники.ФизическиеЛица.СоздатьЭлемент();
				ФизЛицоОбъект.Наименование = Выборка.Наименование;
				ФизЛицоОбъект.ДатаРождения = Выборка.ДатаРождения;
				Попытка
					ФизЛицоОбъект.Записать();
					ТекущееФизЛицо = ФизЛицоОбъект.Ссылка;
					Сообщить("Создано физлицо для: " + Выборка.Наименование);
				Исключение
					Предупреждение("Не удалось записать физлицо для " + Выборка.Наименование + ". Ошибка: " + ОписаниеОшибки());
					Продолжить;
				КонецПопытки;
			КонецЕсли;
			
			МодифицированПартнер = Истина;
			
		КонецЕсли;
		
		// Проверяем актуальность даты рождения в самом физлице
		ФизЛицоОбъект = ТекущееФизЛицо.ПолучитьОбъект();
		Если ФизЛицоОбъект <> Неопределено Тогда
			Если ФизЛицоОбъект.ДатаРождения <> Выборка.ДатаРождения Тогда
				ФизЛицоОбъект.ДатаРождения = Выборка.ДатаРождения;
				Попытка
					ФизЛицоОбъект.Записать();
					Сообщить("Обновлена дата рождения физлица для: " + Выборка.Наименование);
				Исключение
					Сообщить("Ошибка записи даты рождения физлица: " + ОписаниеОшибки());
				КонецПопытки;
			КонецЕсли;
		КонецЕсли;
		
		// Привязываем физлицо к партнеру и записываем партнера
		Если МодифицированПартнер Или (Выборка.ФизическоеЛицо <> ТекущееФизЛицо) Тогда
			ПартнерОбъект = Выборка.Partner.ПолучитьОбъект();
			Если ПартнерОбъект <> Неопределено Тогда
				ПартнерОбъект.ФизическоеЛицо = ТекущееФизЛицо;
				
				// Важно: записываем БЕЗ ОбменДанными.Загрузка = Истина,
				// чтобы сработали стандартные подписки на события и заполнились регистры.
				Попытка
					ПартнерОбъект.Записать();
					Сообщить("Партнер связывается с физлицом: " + Выборка.Наименование);
				Исключение
					Сообщить("Не удалось перезаписать партнера: " + ОписаниеОшибки());
				КонецПопытки;
			КонецЕсли;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

---

Шаг 3. Работа с сегментами клиентов

Часто в УТ 11 скидки ко дню рождения настраиваются не напрямую, а через членство в сегменте. Например, создается сегмент «Именинники этой недели» с динамическим или периодическим обновлением. Если у вас используется именно этот подход, то даже после исправления связей с физическими лицами скидка не заработает мгновенно.

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

  1. Интерактивно: Перейти в раздел CRM и маркетинг -> Настройки и справочники -> Сегменты клиентов. Найти соответствующий сегмент и нажать кнопку «Сформировать».
  2. Программно: Вызвать метод формирования сегмента прямо из нашей обработки после завершения цикла исправления партнеров. Рассмотрим, как это сделать программно:

// Пример программного обновления сегмента партнеров
СегментСсылка = Справочники.СегментыПартнеров.НайтиПоРеквизиту("ИмяИменинники", "Именинники");
Если ЗначениеЗаполнено(СегментСсылка) Тогда
	// Вызов процедуры фонового или прямого формирования сегмента (зависит от версии УТ)
	СегментыСервер.АктуализироватьСоставСегмента(СегментСсылка);
	Сообщить("Состав сегмента клиентов успешно обновлен.");
КонецЕсли;

---

Шаг 4. Проверка карт лояльности

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

Проверить корректность привязки карт лояльности к партнерам можно простым запросом:


ВЫБРАТЬ
	Карты.Ссылка КАК Карта,
	Карты.Партнер КАК Партнер,
	Карты.Партнер.ФизическоеЛицо КАК ФизЛицо,
	Карты.Партнер.ФизическоеЛицо.ДатаРождения КАК ДатаРождения
ИЗ
	Справочник.КартыЛояльности КАК Карты
ГДЕ
	Карты.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыКартЛояльности.Действует)
	И Карты.Партнер.ФизическоеЛицо.ДатаРождения ЕСТЬ NULL

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

---

Выводы и лучшие практики при импорте данных

Мы выяснили главную причину проблемы: при массовой программной загрузке данных были созданы плоские карточки партнеров, но проигнорирована связанная сущность — справочник ФизическиеЛица, данные которого критичны для расчета скидок.

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

  1. При создании партнера с типом «Частное лицо» всегда параллельно создавайте элемент в справочнике ФизическиеЛица, заполняйте в нем дату рождения и записывайте ссылку на него в реквизит ФизическоеЛицо карточки партнера.
  2. Если объем данных позволяет, старайтесь производить финализирующую запись объектов без флага ОбменДанными.Загрузка = Истина, чтобы система могла выполнить транзакционные действия по заполнению регистров сведений и аналитики учета.
  3. После любого массового импорта клиентов обязательно запускайте регламентные задания по обновлению сегментов и очистке/актуализации состава дублей и партнеров.
← На главную