Почему при программном создании документов в цикле сохраняется только последний объект?

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке обработок по массовому созданию документов программисты часто сталкиваются с ситуацией: код выполняется без ошибок, отладчик показывает присвоение номеров новым объектам, но по завершении процесса в базе данных обнаруживается только один документ (обычно последний из итерации). Рассмотрим подробно, почему это происходит, проанализируем типичные ошибки в логике и разберем стандарты работы с транзакциями в 1С:Предприятии.

Анализ исходной ситуации

Рассмотрим пример кода, где в цикле обхода таблицы значений создаются документы ЗаказНаПеремещение и фиксируются связи в регистре сведений. На первый взгляд, логика кажется верной: для каждой строки таблицы вызывается либо создание нового документа, либо получение объекта существующего для перезаписи.


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

Проанализируем, почему при таком подходе документы могут "исчезать".

Причина 1: Логическая ошибка в алгоритме поиска

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

Рекомендация: Тщательно проверьте параметры отбора в поисковых запросах. Если поиск идет по комбинации полей (например, Организация, Склад, Вид), убедитесь, что эти данные уникальны для каждого требуемого документа. Если поиск возвращает пустую ссылку или Неопределено там, где должен быть найден объект, система создаст дубли, а если находит не тот объект — затрет существующие данные.

Причина 2: Особенности работы с Менеджером записи регистра

В коде используется СоздатьМенеджерЗаписи(). При работе с ним внутри цикла важно помнить:

  1. Метод Прочитать() заполняет менеджер данными из БД на основании установленных значений измерений. Если перед вызовом Записать() вы не очистили менеджер или не переопределили ключевые измерения, вы можете случайно перезаписать одну и ту же запись регистра.
  2. Если Источник (ссылка на документ) передается в менеджер записи до того, как документ физически записан в первый раз (для нового документа), ссылка может быть пустой. Хотя метод Записать() у документа обычно инициализирует ссылку, в сложных транзакциях лучше явно вызывать ДокЗП.УстановитьНовыйНомер() или проверять наличие ссылки перед записью в регистр.

Причина 3: Правильное использование транзакций и блоков исключений

Разберем важный методологический момент, возникший в дискуссии: где должен располагаться вызов НачатьТранзакцию() — внутри блока Попытка или снаружи?

Согласно стандартам разработки 1С, правильная структура выглядит так:


НачатьТранзакцию();
Попытка
    // Основная логика записи
    Объект.Записать();
    
    ЗафиксироватьТранзакцию();
Исключение
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    // Логирование ошибки
КонецПопытки;

Почему это важно:

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

Причина 4: Тихие откаты в модуле объекта

Проанализируем ситуацию, когда документ "создается", но не виден в базе. В типовых конфигурациях, таких как УПП или ERP, часто используются сложные проверки в процедурах ПередЗаписью или ПриЗаписи модуля документа. Если какой-то реквизит заполнен неверно, система может установить Отказ = Истина.

Если при программном создании вы не проверяете результат записи и не выводите сообщения об ошибках из модуля объекта, документ просто не будет сохранен. Для обхода бизнес-логик (если это оправдано задачей) используйте свойство ОбменДанными.Загрузка:


ДокЗП = Документы.ЗаказНаПеремещение.СоздатьДокумент();
ДокЗП.ОбменДанными.Загрузка = Истина; // Отключает большинство проверок в модуле объекта
// ... заполнение ...
ДокЗП.Записать();

Методика диагностики "исчезновения" документов

Если вы столкнулись с подобной проблемой, выполните следующие шаги:

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

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

← На главную