Как правильно создать элемент справочника при записи или проведении документа в 1С

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

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

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

Анализ ограничений платформы при проведении и записи

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

  1. Запрет изменения реквизитов документа в процедуре проведения: В обработчике ОбработкаПроведения реквизиты текущего объекта документа модифицировать нельзя. Любые изменения реквизитов объекта не попадут в базу данных автоматически, а явный вызов метода Записать() для текущего документа внутри его же проведения запрещен платформой и приводит к ошибкам или неконтролируемой рекурсии.
  2. Особенности транзакций: Все события записи и проведения объекта выполняются в рамках одной неделимой транзакции. Если элемент справочника создается и записывается внутри транзакции документа, платформа автоматически объединяет эти действия. При возникновении ошибки или установке флага Отказ = Истина откатятся как изменения документа, так и созданный элемент справочника.
  3. Событие ПриЗаписи: Если создавать справочник в событии ПриЗаписи модуля документа, то для сохранения полученной ссылки в реквизит документа потребуется повторный вызов записи документа, что спровоцирует зацикливание.

Исходя из этого, наиболее корректным местом для создания элемента справочника и записи ссылки в реквизит текущего документа является процедура-обработчик ПередЗаписью в модуле объекта документа.

Пошаговая реализация в событии ПередЗаписью

Рассмотрим по шагам логику работы алгоритма:

  1. Проверяем, заполнен ли реквизит документа, хранящий ссылку на связанный справочник.
  2. Если реквизит пустой (документ новый или справочник еще не создавался), создаем новый элемент справочника через метод Справочники.ИмяСправочника.СоздатьЭлемент(), заполняем его реквизиты данными из документа, вызываем метод Записать() и сохраняем ссылку в реквизите документа.
  3. Если реквизит уже заполнен, получаем объект существующего справочника через ПолучитьОбъект(), обновляем реквизиты (синхронизируем данные) и перезаписываем элемент справочника.

Посмотрим на пример реализации данного механизма в модуле объекта документа:


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

Так как присвоение ссылки ЭтотОбъект.ДоговорКонтрагента = НовыйДоговор.Ссылка происходит в момент ПередЗаписью, сам документ сохранит это значение в базу данных автоматически в штатном цикле без необходимости повторных вызовов метода Записать().

Связывание с новым, еще не записанным документом

Разберем ситуацию, когда элементу справочника требуется обратная ссылка на текущий документ (например, реквизит ДокументОснование в справочнике). Если документ еще ни разу не записывался в базу, его ссылка пуста (ЭтотОбъект.Ссылка.Пустая()).

Для решения этой проблемы выясним, как получить и зафиксировать ссылку нового объекта заранее:


// Если ссылка у документа еще не сформирована в базе данных
Если ЭтотОбъект.ЭтоНовый() Тогда
	НоваяСсылкаДокумента = Документы.ДоговорЗайма.ПолучитьСсылку(Новый УникальныйИдентификатор());
	ЭтотОбъект.УстановитьСсылкуНового(НоваяСсылкаДокумента);
Иначе
	НоваяСсылкаДокумента = ЭтотОбъект.Ссылка;
КонецЕсли;

// Теперь переменную НоваяСсылкаДокумента можно передать в реквизит справочника
НовыйЭлемент = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
НовыйЭлемент.ДокументОснование = НоваяСсылкаДокумента;
НовыйЭлемент.Записать();

ЭтотОбъект.ДоговорКонтрагента = НовыйЭлемент.Ссылка;

Альтернативные подходы

Рассмотрим другие варианты архитектурного решения в зависимости от пользовательского сценария:

  1. Интерактивное создание по кнопке на форме: Если пользователю требуется предварительно отредактировать параметры создаваемого элемента справочника перед сохранением документа, создание выносится в команду формы. На сервере создается элемент справочника, ссылка сразу возвращается в реквизит формы, и пользователь видит результат до проведения документа.
  2. Подписка на событие: Если нежелательно вносить изменения в типовой модуль документа, логику создания справочника переносят в подписку на событие ПередЗаписью для нужного типа документа.
  3. Использование механизма ввода на основании: Если справочник является самостоятельным объектом, более правильным с точки зрения типовой архитектуры может быть ввод справочника на основании документа или наоборот, с заполнением через обработчик ОбработкаЗаполнения.

Управление отменой проведения и блокировками

Важно помнить о побочных эффектах:

  1. Отмена проведения: При интерактивной отмене проведения созданный ранее элемент справочника не удаляется автоматически, так как он существует независимо. Если логика системы требует обязательной очистки или пометки на удаление, обработку таких ситуаций необходимо реализовывать в процедурах ОбработкаУдаленияПроведения или ПередУдалением.
  2. Блокировки: Метод ПолучитьОбъект() устанавливает исключительную блокировку на элемент справочника. При высокой интенсивности одновременной записи документов, ссылающихся на один и тот же справочник, следует минимизировать время нахождения в транзакции.
← На главную