При разработке учетных механизмов в платформе 1С разработчики нередко сталкиваются с ситуацией, когда в процессе проведения одного документа (документа-источника) требуется сформировать связанный документ для фиксации дополнительной аналитики. Однако стандартный вызов создания нового объекта в процедуре ОбработкаПроведения приводит к серьезной проблеме: при повторном проведении документа-источника каждый раз генерируется новый экземпляр подчиненного документа, в результате чего база данных засоряется дублями, а движения и итоги искажаются. Рассмотрим подробнее, почему возникает эта сложность и какими способами можно организовать связь между документами, особенно в условиях, когда документ аналитики является типовым и менять его структуру нежелательно.
Прежде чем перейти к программным решениям, проанализируем поведение платформы. Процедура ОбработкаПроведения выполняется внутри транзакции записи документа. Платформа 1С по умолчанию управляет движениями документа через свойство конфигурации Удаление движений (например, со значением Удалять автоматически). Это означает, что в самом начале процедуры проведения все сформированные ранее движения очищаются. Если ссылка на подчиненный аналитический документ хранилась исключительно в записях регистров документа-источника, в момент старта повторного проведения эта ссылка стирается, и алгоритм теряет информацию о том, какой документ был создан ранее.
Кроме того, создание и тем более проведение сторонних документов внутри обработчика проведения не рекомендуется стандартами разработки фирмы «1С». Это увеличивает время транзакции и может провоцировать взаимоблокировки (deadlocks). Тем не менее, если такая архитектура уже заложена или является обязательным требованием, необходимо обеспечить однозначную идентификацию ранее созданного объекта.
Разберем основные варианты решения поставленной задачи.
Этот вариант является наиболее универсальным и чистым с точки зрения архитектуры, если типовой документ аналитики запрещено снимать с поддержки или изменять. Создадим отдельный независимый непериодический регистр сведений, например, СвязиДокументовАналитики.
Структура регистра может быть следующей:
ДокументОснование (тип: ДокументСсылка ведущего документа).ДокументАналитики (тип: ДокументСсылка создаваемого аналитического документа).В алгоритме проведения перед созданием аналитического документа выполним проверку наличия связи через запрос к регистру сведений. Если связь найдена, получим существующий документ по ссылке через метод ПолучитьОбъект() и обновим его данные. Если запись отсутствует, создадим новый документ и зафиксируем пару ссылок в регистре.
Посмотрим на пример программной реализации в модуле документа-источника:
// Проверяем наличие уже созданного документа через регистр сведений
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Связи.ДокументАналитики КАК ДокументАналитики
|ИЗ
| РегистрСведений.СвязиДокументовАналитики КАК Связи
|ГДЕ
| Связи.ДокументОснование = &ДокументОснование";
Запрос.УстановитьПараметр("ДокументОснование", ЭтотОбъект.Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.ДокументАналитики) Тогда
// Получаем существующий документ аналитики для перезаписи
ДокументАналитикиОбъект = Выборка.ДокументАналитики.ПолучитьОбъект();
Иначе
// Создаем новый аналитический документ
ДокументАналитикиОбъект = Документы.АналитическийДокумент.СоздатьДокумент();
КонецЕсли;
// Заполняем реквизиты аналитического документа
ДокументАналитикиОбъект.Дата = ЭтотОбъект.Дата;
ДокументАналитикиОбъект.Организация = ЭтотОбъект.Организация;
// Заполнение прочих данных...
ДокументАналитикиОбъект.Записать(РежимЗаписиДокумента.Проведение);
// Если документ был создан впервые, фиксируем связь в регистре сведений
Если Не Выборка.Количество() > 0 Тогда
МенеджерЗаписи = РегистрыСведений.СвязиДокументовАналитики.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ДокументОснование = ЭтотОбъект.Ссылка;
МенеджерЗаписи.ДокументАналитики = ДокументАналитикиОбъект.Ссылка;
МенеджерЗаписи.Записать(Истина);
КонецЕсли;
Такой подход полностью изолирует логику, не требует модификации типовых метаданных и гарантирует, что при повторных проведениях будет обновляться строго один и тот же объект.
Если конфигурация допускает использование механизма расширений (в режиме совместимости 8.3.11 и выше), мы можем добавить реквизит ДокументОснование в аналитический документ через расширение конфигурации. Это не затрагивает основную типовую конфигурацию и сохраняет её на полной поддержке.
В этом случае при повторном проведении выполняется поиск ранее созданного документа с помощью простого запроса к таблице подчиненного документа:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Аналитика.Ссылка КАК Ссылка
|ИЗ
| Документ.АналитическийДокумент КАК Аналитика
|ГДЕ
| Аналитика.ДокументОснование = &ДокументОснование";
Запрос.УстановитьПараметр("ДокументОснование", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ДокументАналитикиОбъект = Выборка.Ссылка.ПолучитьОбъект();
Иначе
ДокументАналитикиОбъект = Документы.АналитическийДокумент.СоздатьДокумент();
ДокументАналитикиОбъект.ДокументОснование = ЭтотОбъект.Ссылка;
КонецЕсли;
// Обновление и проведение документа
ДокументАналитикиОбъект.Записать(РежимЗаписиДокумента.Проведение);
Если прикладное решение построено на базе Библиотеки стандартных подсистем (БСП) и внесение изменений даже через расширения ограничено регламентом, можно задействовать подсистему свойств. В пользовательском режиме для аналитического документа регистрируется дополнительное сведение, например, Документ-основание, хранящееся в регистре сведений ДополнительныеСведения.
Поиск и запись в таком сценарии выполняются через стандартные процедуры и функции БСП (модуль УправлениеСвойствами). Перед генерацией документа аналитики считывается значение дополнительного сведения, и если ссылка на документ-источник совпадает, платформа перезаписывает найденный объект вместо создания дубликата.
Если дорабатывать разрешено именно ведущий документ (документ-источник), то наиболее простой способ — добавить в него реквизит ДокументАналитики. В таком случае алгоритм предельно упрощается:
ОбработкаПроведения проверяется условие: ЗначениеЗаполнено(ЭтотОбъект.ДокументАналитики).ЭтотОбъект.ДокументАналитики.ПолучитьОбъект().ДокументАналитики текущего документа.Однако следует учитывать, что при изменении реквизитов ведущего документа в процедуре проведения необходимо корректно обработать сохранение ссылки, чтобы не вызвать конфликт блокировок при одновременной записи объекта.
При реализации любого из предложенных вариантов нельзя забывать о жизненном цикле зависимых данных. Если ведущий документ снимается с проведения или помечается на удаление, документ аналитики не очистится платформой автоматически. Обязательно добавьте обработку удаления зависимого документа или отмену его проведения в процедуру ОбработкаУдаленияПроведения модуля документа-источника, найдя зависимый документ через созданный регистр или связь.