При разработке обработок по массовому созданию документов программисты часто сталкиваются с ситуацией: код выполняется без ошибок, отладчик показывает присвоение номеров новым объектам, но по завершении процесса в базе данных обнаруживается только один документ (обычно последний из итерации). Рассмотрим подробно, почему это происходит, проанализируем типичные ошибки в логике и разберем стандарты работы с транзакциями в 1С:Предприятии.
Рассмотрим пример кода, где в цикле обхода таблицы значений создаются документы ЗаказНаПеремещение и фиксируются связи в регистре сведений. На первый взгляд, логика кажется верной: для каждой строки таблицы вызывается либо создание нового документа, либо получение объекта существующего для перезаписи.
Для Каждого СтрТЗ Из тзДокументовПерезаписи Цикл
ЗаказДляперезаписи = НайдемЗаказНаПеремещениеДляПерезаписи(СтрТЗ);
Если ЗаказДляперезаписи = Неопределено Тогда
ДокЗП = Документы.ЗаказНаПеремещение.СоздатьДокумент();
Иначе
ДокЗП = ЗаказДляперезаписи.ПолучитьОбъект();
КонецЕСли;
// Заполнение реквизитов и табличных частей
ЗаполнимЗаказНаПеремещениеДляОрганизации(ДокЗП, СтруктураЗаказа, мСтрокиНоменклатуры);
Попытка
НачатьТранзакцию();
ДокЗП.Записать();
// Работа с регистром сведений
мз = РегистрыСведений.sСвязиОбъектов.СоздатьМенеджерЗаписи();
мз.Источник = ДокЗП.Ссылка;
// ... установка других измерений
мз.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
КонецПопытки;
КонецЦикла;
Проанализируем, почему при таком подходе документы могут "исчезать".
Как выяснилось в ходе обсуждения, наиболее частой причиной является некорректная работа функции НайдемЗаказНаПеремещениеДляПерезаписи(). Если алгоритм поиска внутри функции при каждой итерации возвращает одну и ту же ссылку на уже созданный документ (или на документ, созданный в первой итерации), то метод Записать() будет просто обновлять данные в этом объекте. В итоге в базе останется только один документ с данными последней строки таблицы значений.
Рекомендация: Тщательно проверьте параметры отбора в поисковых запросах. Если поиск идет по комбинации полей (например, Организация, Склад, Вид), убедитесь, что эти данные уникальны для каждого требуемого документа. Если поиск возвращает пустую ссылку или Неопределено там, где должен быть найден объект, система создаст дубли, а если находит не тот объект — затрет существующие данные.
В коде используется СоздатьМенеджерЗаписи(). При работе с ним внутри цикла важно помнить:
Прочитать() заполняет менеджер данными из БД на основании установленных значений измерений. Если перед вызовом Записать() вы не очистили менеджер или не переопределили ключевые измерения, вы можете случайно перезаписать одну и ту же запись регистра.Источник (ссылка на документ) передается в менеджер записи до того, как документ физически записан в первый раз (для нового документа), ссылка может быть пустой. Хотя метод Записать() у документа обычно инициализирует ссылку, в сложных транзакциях лучше явно вызывать ДокЗП.УстановитьНовыйНомер() или проверять наличие ссылки перед записью в регистр.Разберем важный методологический момент, возникший в дискуссии: где должен располагаться вызов НачатьТранзакцию() — внутри блока Попытка или снаружи?
Согласно стандартам разработки 1С, правильная структура выглядит так:
НачатьТранзакцию();
Попытка
// Основная логика записи
Объект.Записать();
ЗафиксироватьТранзакцию();
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
// Логирование ошибки
КонецПопытки;
Почему это важно:
НачатьТранзакцию() находится внутри блока Попытка, и при старте транзакции произойдет исключительная ситуация (например, разрыв соединения с СУБД), управление перейдет в блок Исключение, где вызов ОтменитьТранзакцию() породит новую ошибку (так как транзакция фактически не была начата).ПередЗаписью), транзакция помечается как "битая" (обреченная на откат). В этом случае любые последующие попытки записи в рамках этой же транзакции будут вызывать ошибку "В данной транзакции уже происходили ошибки".Проанализируем ситуацию, когда документ "создается", но не виден в базе. В типовых конфигурациях, таких как УПП или ERP, часто используются сложные проверки в процедурах ПередЗаписью или ПриЗаписи модуля документа. Если какой-то реквизит заполнен неверно, система может установить Отказ = Истина.
Если при программном создании вы не проверяете результат записи и не выводите сообщения об ошибках из модуля объекта, документ просто не будет сохранен. Для обхода бизнес-логик (если это оправдано задачей) используйте свойство ОбменДанными.Загрузка:
ДокЗП = Документы.ЗаказНаПеремещение.СоздатьДокумент();
ДокЗП.ОбменДанными.Загрузка = Истина; // Отключает большинство проверок в модуле объекта
// ... заполнение ...
ДокЗП.Записать();
Если вы столкнулись с подобной проблемой, выполните следующие шаги:
Заказ на перемещение и событию Данные.Добавление. Если в журнале есть запись о создании, но документа нет в списке — значит, произошел откат транзакции после момента записи.Записать()). Это поможет локализовать ошибку: если документы начнут появляться, значит, проблема была во вложенности или ошибочном откате общей транзакции.Сообщить() или запись в лог сразу после строки получения ЗаказДляперезаписи. Выводите ГУИД или номер найденного документа, чтобы убедиться, что на каждой итерации обрабатывается разный объект.Подводя итог, можно сказать, что "магическое" исчезновение документов чаще всего связано либо с логической ошибкой (перезапись одного и того же объекта), либо с некорректной обработкой транзакций, когда одна ошибка в цикле приводит к откату всех предыдущих успешных действий, если они были объединены в одну транзакцию.