Как массово сделать договоры контрагентов основными в 1С:Управление торговлей 10.3?

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

При переносе данных из устаревших конфигураций (например, «Торговля и Склад 7.7») в «1С:Управление торговлей 10.3» пользователи часто сталкиваются с технической проблемой: справочники контрагентов и их договоров успешно перенесены, но ни один договор не назначен в качестве основного. В интерфейсе программы такие договоры не выделяются жирным шрифтом, а при создании новых документов (например, «Реализация товаров и услуг» или «Поступление товаров и услуг») поле «Договор» остается пустым, требуя от пользователя ручного выбора.

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

Почему стандартная групповая обработка справочников не решает задачу?

В меню программы «Сервис — Универсальные обработки — Групповая обработка справочников и документов» действительно можно выбрать справочник Контрагенты и попытаться изменить реквизит ОсновнойДоговорКонтрагента. Однако стандартный инструмент позволяет установить только одно конкретное значение для всех выбранных записей.

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

Способ 1. Оптимизированный программный код для внешней обработки

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


Для Каждого ВыборкаСтрока Из ТабличнаяЧасть Цикл
    КонтрагентОбъект = ВыборкаСтрока.Контрагент.ПолучитьОбъект();
    ТекстЗапроса =
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    ДоговорыКонтрагентов.Ссылка
    |ИЗ
    |    Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
    |ГДЕ
    |    ДоговорыКонтрагентов.Владелец = &ВладелецКонтрагент";
    
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ВладелецКонтрагент", ВыборкаСтрока.Контрагент);
    Запрос.Текст = ТекстЗапроса;
    
    ВыборкаЗапрос = Запрос.Выполнить().Выбрать();
    Пока ВыборкаЗапрос.Следующий() Цикл
        КонтрагентОбъект.ОсновнойДоговорКонтрагента = ВыборкаЗапрос.Ссылка;
    КонецЦикла;        
    КонтрагентОбъект.Записать();
КонецЦикла;

В чем заключается критическая ошибка данного кода? Здесь запрос к базе данных выполняется внутри цикла Для Каждого. Если у вас в базе 10 000 контрагентов, программа сделает 10 000 отдельных запросов к СУБД. Это приведет к колоссальной нагрузке на сервер, зависанию сеанса 1С и долгому ожиданию завершения операции.

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


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

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

// Начало транзакции для ускорения записи и обеспечения целостности
НачатьТранзакцию();

Попытка
	// 2. В цикле выполняем только запись измененных объектов
	Пока Выборка.Следующий() Цикл
		КонтрагентОбъект = Выборка.Контрагент.ПолучитьОбъект();
		
		Если КонтрагентОбъект <> Неопределено Тогда
			КонтрагентОбъект.ОсновнойДоговорКонтрагента = Выборка.Договор;
			
			// Свойство ОбменДанными.Загрузка = Истина позволяет обойти тяжелые 
			// встроенные проверки при записи (например, проверку заполнения ИНН/КПП), 
			// что ускоряет процесс в десятки раз
			КонтрагентОбъект.ОбменДанными.Загрузка = Истина; 
			
			КонтрагентОбъект.Записать();
		КонецЕсли;
	КонецЦикла;
	
	ЗафиксироватьТранзакцию();
	Сообщить("Обработка успешно завершена! Все договоры установлены как основные.");
Исключение
	ОтменитьТранзакцию();
	Сообщить("Произошла ошибка при записи данных: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;

В этом коде функция МИНИМУМ(ДоговорыКонтрагентов.Ссылка) гарантирует, что если у контрагента создано несколько договоров, программа выберет первый созданный договор (с наименьшим внутренним идентификатором) и назначит его в качестве основного.

Способ 2. Автоматическое назначение при загрузке договоров

Если вы прямо сейчас занимаетесь переносом данных и используете стандартную обработку ЗагрузкаДанныхИзТабличногоДокумента.epf для импорта договоров из Excel или MXL, вы можете решить проблему превентивно. Нет необходимости писать внешние файлы обработок — достаточно использовать встроенные события загрузчика.

Разберем по шагам, как настроить автоматическое назначение договора основным непосредственно при его загрузке:

  1. Откройте обработку «Загрузка данных из табличного документа» и настройте сопоставление полей для справочника ДоговорыКонтрагентов.
  2. В нижней части формы обработки нажмите на кнопку «События» (в некоторых версиях — «Настройка событий»).
  3. Найдите обработчик события «При записи» (оно выполняется сразу после того, как новый договор успешно записан в базу данных).
  4. Вставьте в открывшееся текстовое поле безопасный и оптимизированный код:

// Проверяем заполненность владельца (контрагента) загружаемого договора
Если ЗначениеЗаполнено(Объект.Владелец) Тогда
    КонтрагентОбъект = Объект.Владелец.ПолучитьОбъект();
    
    Если КонтрагентОбъект <> Неопределено Тогда
        // Назначаем только что созданный договор основным для его владельца
        КонтрагентОбъект.ОсновнойДоговорКонтрагента = Объект.Ссылка;
        
        // Отключаем лишние подписки на события для ускорения работы
        КонтрагентОбъект.ОбменДанными.Загрузка = Истина;
        
        Попытка
            КонтрагентОбъект.Записать();
        Исключение
            // Записываем информацию об ошибке в журнал регистрации, чтобы загрузка не прерывалась
            ЗаписьЖурналаРегистрации("ЗагрузкаДоговоров", УровеньЖурналаРегистрации.Предупреждение, , , 
                "Не удалось обновить контрагента: " + КонтрагентОбъект.Наименование + ". Ошибка: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
КонецЕсли;

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

Способ 3. Повторная загрузка через сопоставление реквизитов

Если договоры уже загружены, но вы не хотите писать программный код, можно воспользоваться альтернативным методом повторного импорта через ту же обработку ЗагрузкаДанныхИзТабличногоДокумента.epf.

Для этого выполните следующие действия:

  1. Подготовьте таблицу в Excel, содержащую всего две колонки: «Наименование контрагента» (или его ИНН/Код для точной идентификации) и «Наименование договора» (например, «Основной договор»).
  2. В обработке загрузки выберите режим загрузки в справочник Контрагенты.
  3. В таблице настроек полей отметьте только два реквизита: Наименование (или ИНН, установив для него флаг «Искать по этому полю») и ОсновнойДоговорКонтрагента. Напротив остальных полей снимите флаги активности.
  4. Для поля ОсновнойДоговорКонтрагента настройте поиск договора по наименованию с учетом владельца.
  5. Обязательно установите в настройках загрузки флаг «Не создавать новых контрагентов» (или «Заменять существующие объекты», отключив создание новых записей), чтобы избежать дублирования информации.
  6. Запустите процесс загрузки. Программа найдет существующих контрагентов по ключевому полю и обновит у них только один реквизит — основной договор.

Важное предупреждение: перед выполнением любых массовых групповых операций с базой данных 1С обязательно создавайте резервную копию вашей информационной базы (файл 1Cv8.1CD или выгрузку .dt)!

Важная информация на будущее: архитектурные изменения в 1С 8.3

Если вы планируете в обозримом будущем совершить переход с устаревшей платформы «1С:Предприятие 8.2» и конфигурации «Управление торговлей 10.3» на современные решения (такие как «Управление торговлей 11», «Комплексная автоматизация 2» или «ERP Управление предприятием»), вам необходимо учитывать критическое изменение в архитектуре метаданных 1С:

Для программной установки основного договора в современных версиях программ 1С на управляемых формах используется создание записи в регистре сведений либо вызов стандартных процедур из Библиотеки стандартных подсистем (БСП):


// Пример установки основного договора для современных конфигураций на платформе 1С 8.3
МенеджерЗаписи = РегистрыСведений.ОсновныеДоговорыКонтрагентов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Организация = СсылкаНаНашуОрганизацию;
МенеджерЗаписи.Контрагент  = СсылкаНаКонтрагента;
МенеджерЗаписи.Договор     = СсылкаНаДоговор;
МенеджерЗаписи.Записать(Истина); // Запись с замещением старого значения

Понимание этих принципов позволит вам не только эффективно решить текущую задачу в «УТ 10.3», но и подготовить вашу информационную систему к беспрепятственному переходу на актуальные версии платформы «1С:Предприятие».

← На главную