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