Как установить основное контактное лицо партнерам и контрагентам в 1С?

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

В процессе работы с базами 1С (ERP, Комплексная автоматизация, Управление торговлей или CRM) часто возникает задача массовой нормализации данных. Одна из типичных задач — заполнить реквизит основного контактного лица у партнеров или контрагентов, если он остался пустым. При этом логика назначения обычно следующая: если у партнера зарегистрирован только один контакт, выбираем его; если несколько — берем первый по алфавиту; если основной контакт уже назначен, объект не изменяем.

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

Анализ типовых ошибок и тонкостей логики

Прежде чем приступать к написанию кода, проанализируем ключевые нюансы работы платформы 1С:

  1. Агрегатные функции над ссылками: Использование конструкций вида МАКСИМУМ(КонтактныеЛица.Ссылка) группирует записи по внутреннему идентификатору (UUID), а не по алфавиту наименований или порядку добавления. Это приводит к назначению случайного контакта при наличии нескольких вариантов.
  2. Повторный запуск запроса: Если в условиях отбора установлено ГДЕ Владелец.CRM_ОсновноеКонтактноеЛицо = ЗНАЧЕНИЕ(...), то после первого успешного выполнения обработки данные перезаписываются. Повторный запуск такого запроса вернет пустую выборку, так как пустых значений в базе уже не осталось.
  3. Помеченные на удаление элементы: В запросе обязательно нужно отсекать контакты и партнеров, помеченных на удаление, чтобы не назначить неактуальный контакт.

Вариант 1. Иерархический обход выборки с итогами

Рассмотрим вариант, в котором запрос получает упорядоченный список контактных лиц с группировкой по владельцу через конструкцию ИТОГИ ПО. В программном коде мы открываем выборку верхнего уровня по партнерам, а для детальных записей вызываем метод Следующий() ровно один раз. Это гарантирует выборку первого контакта по алфавиту.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	КонтактныеЛица.Ссылка КАК КонтактноеЛицо,
|	КонтактныеЛица.Владелец КАК Партнер
|ИЗ
|	Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛица
|ГДЕ
|	НЕ КонтактныеЛица.ПометкаУдаления
|	И НЕ КонтактныеЛица.Владелец.ПометкаУдаления
|	И (КонтактныеЛица.Владелец.CRM_ОсновноеКонтактноеЛицо ЕСТЬ NULL
|		ИЛИ КонтактныеЛица.Владелец.CRM_ОсновноеКонтактноеЛицо = ЗНАЧЕНИЕ(Справочник.КонтактныеЛицаПартнеров.ПустаяСсылка))
|УПОРЯДОЧИТЬ ПО
|	КонтактныеЛица.Владелец,
|	КонтактныеЛица.Наименование
|ИТОГИ ПО
|	Партнер";

РезультатЗапроса = Запрос.Выполнить();
ВыборкаПартнеры = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаПартнеры.Следующий() Цикл
	ВыборкаДетали = ВыборкаПартнеры.Выбрать();
	// Берем только первую запись из детальной выборки
	Если ВыборкаДетали.Следующий() Тогда
		ОбъектПартнер = ВыборкаПартнеры.Партнер.ПолучитьОбъект();
		ОбъектПартнер.CRM_ОсновноеКонтактноеЛицо = ВыборкаДетали.КонтактноеЛицо;
		
		// Отключаем лишние проверки при пакетной обработке
		ОбъектПартнер.ОбменДанными.Загрузка = Истина;
		ОбъектПартнер.Записать();
	КонецЕсли;
КонецЦикла;

Вариант 2. Запрос на временных таблицах для плоской выборки

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	КонтактныеЛица.Владелец КАК Партнер,
|	МИНИМУМ(КонтактныеЛица.Наименование) КАК МинНаименование
|ПОМЕСТИТЬ ВТ_ПервыеКонтакты
|ИЗ
|	Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛица
|ГДЕ
|	НЕ КонтактныеЛица.ПометкаУдаления
|	И НЕ КонтактныеЛица.Владелец.ПометкаУдаления
|	И (КонтактныеЛица.Владелец.CRM_ОсновноеКонтактноеЛицо ЕСТЬ NULL
|		ИЛИ КонтактныеЛица.Владелец.CRM_ОсновноеКонтактноеЛицо = ЗНАЧЕНИЕ(Справочник.КонтактныеЛицаПартнеров.ПустаяСсылка))
|СГРУППИРОВАТЬ ПО
|	КонтактныеЛица.Владелец
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ.Партнер КАК Партнер,
|	МИНИМУМ(КонтактныеЛица.Ссылка) КАК КонтактноеЛицо
|ИЗ
|	ВТ_ПервыеКонтакты КАК ВТ
|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КонтактныеЛицаПартнеров КАК КонтактныеЛица
|		ПО ВТ.Партнер = КонтактныеЛица.Владелец
|		И ВТ.МинНаименование = КонтактныеЛица.Наименование
|ГДЕ
|	НЕ КонтактныеЛица.ПометкаУдаления
|СГРУППИРОВАТЬ ПО
|	ВТ.Партнер";

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
	ОбъектПартнер = Выборка.Партнер.ПолучитьОбъект();
	ОбъектПартнер.CRM_ОсновноеКонтактноеЛицо = Выборка.КонтактноеЛицо;
	ОбъектПартнер.ОбменДанными.Загрузка = Истина;
	ОбъектПартнер.Записать();
КонецЦикла;

Рекомендации по оптимизации пакетной записи

Разберем практические рекомендации, которые помогут избежать блокировок и ускорят выполнение обработки в рабочей базе:

  1. Использование свойства ОбменДанными.Загрузка: Установка флага Объект.ОбменДанными.Загрузка = Истина перед вызовом Записать() отключает прикладные проверки в модуле объекта и подписках на события. Это предотвращает возникновение ошибок валидации других незаполненных реквизитов и существенно ускоряет процесс.
  2. Транзакции порциями: Если партнеров в базе десятки тысяч, рекомендуется оборачивать запись в транзакции пачками по 500–1000 элементов с помощью методов НачатьТранзакцию() и ЗафиксироватьТранзакцию().
  3. Разделение сущностей Партнеры и Контрагенты: Обратите внимание, к какому именно справочнику привязаны контактные лица в вашей конфигурации. В типовых конфигурациях 1С контактные лица подчинены справочнику Партнеры (управленческий учет), а не Контрагенты (регламентированные данные).
← На главную