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

Программист 1С v8.3 (Обычные формы) Торговля и дистрибуция
← На главную

При разработке механизмов логистики или складского учета часто возникает задача: на основании документа (например, Маршрутного задания) вывести список накладных, проставить в таблице флажки (статусы) и сохранить эти изменения в регистр сведений. Однако начинающие разработчики часто сталкиваются с тем, что данные записываются некорректно: либо сохраняется только одна строка, либо флажки (тип Булево) «исчезают» при записи.

В этой статье мы подробно разберем, почему так происходит, изучим механизмы работы Набора записей и предложим оптимальные варианты решения задачи.

Причина 1: Нарушение уникальности ключа записи

Проанализируем архитектуру регистра сведений. Если ваш регистр является независимым и периодическим, то уникальность каждой строки определяется набором полей, называемых «ключом записи». В этот ключ входят: Период + Измерения.

Разберем ситуацию: если в коде для записи нескольких строк используется ТекущаяДата(), а измерения (например, «Документ» и «Пользователь») остаются одинаковыми, система 1С будет воспринимать каждую новую строку как попытку изменить уже существующую запись за эту секунду. В результате в базе останется только та строка, которая была обработана последней в цикле.

Важный момент: если вы пытаетесь записать два разных статуса (флажка) для одной и той же накладной в одну и ту же секунду, и эти статусы являются ресурсами, а не измерениями, то сохранится только последний вариант. Чтобы хранить историю или разные наборы данных одновременно, необходимо либо добавлять новое измерение (например, тип статуса), либо гарантировать уникальность периода.

Причина 2: Неправильное использование объекта НаборЗаписей

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


Пока Выборка.Следующий() Цикл
    Для Каждого Строка Из ТаблицаСтатуса Цикл
        НаборЗаписей = РегистрыСведений.СтатусыДокументов.СоздатьНаборЗаписей();
        НоваяЗапись = НаборЗаписей.Добавить();
        // Заполнение полей...
        НаборЗаписей.Записать(Ложь);
    КонецЦикла;
КонецЦикла;

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

Решение проблемы через правильную работу с набором записей

Для корректной и быстрой работы нам нужно вынести создание объекта НаборЗаписей за пределы циклов или использовать отборы. Рассмотрим правильный алгоритм:

  1. Создаем НаборЗаписей один раз.
  2. Устанавливаем Отбор (например, по конкретному документу или накладной), чтобы не перезаписывать весь регистр целиком.
  3. В цикле добавляем записи в этот набор через метод Добавить().
  4. Вызываем Записать() один раз после заполнения всего набора.

Пример исправленного кода:


// Создаем набор один раз для всех документов или внутри цикла по документам
НаборЗаписей = РегистрыСведений.СтатусыДокументов.СоздатьНаборЗаписей();

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

// Записываем все накопленные данные одним вызовом
НаборЗаписей.Записать(); 

Особенности метода ЗаполнитьЗначенияСвойств

Выясним причину, по которой флажки могут «пропадать», даже если код кажется верным. Метод ЗаполнитьЗначенияСвойств копирует значения из источника в приемник по совпадению имен полей.

Если вы сначала вручную установили НоваяЗапись.Вернул = Истина, а затем вызвали ЗаполнитьЗначенияСвойств(НоваяЗапись, Выборка), и в вашей выборке (результате запроса) тоже есть поле Вернул, но оно пустое или равно Ложь, то ручная установка будет затерта.

Правило: Всегда вызывайте ЗаполнитьЗначенияСвойств для массового копирования в самом начале, а специфические флажки и индивидуальные поля присваивайте после этого вручную.

Оптимизация: Использование временных таблиц в запросе

Рассмотрим более продвинутый и эффективный способ. Вместо вложенных циклов (перебор таблицы значений внутри выборки запроса), что создает огромную нагрузку при больших объемах данных, лучше объединить данные на уровне SQL-запроса.

Проанализируем шаги оптимизации:

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

Пример структуры такого запроса:


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Статусы ИЗ &ТаблицаСтатуса КАК Таблица";
Запрос.УстановитьПараметр("ТаблицаСтатуса", ТаблицаСтатуса);
Запрос.Выполнить();

Запрос.Текст = 
"ВЫБРАТЬ
|   МЗ.Реализация КАК Документ,
|   ВТ.Вернул КАК Вернул,
|   ВТ.Закрыто КАК Закрыто
|ИЗ
|   Документ.МаршрутноеЗадание.ДокументыРеализации КАК МЗ
|   ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Статусы КАК ВТ ПО МЗ.Реализация = ВТ.Документ
|ГДЕ
|   МЗ.Ссылка = &СсылкаМЗ";

Такой подход гарантирует, что вы получите ровно те данные, которые видел пользователь на форме, и избавит от сложной логики сопоставления строк в коде 1С.

Бизнес-логика и выбор типа регистра

В завершение проанализируем ситуацию с точки зрения архитектуры. Если вам нужно, чтобы в регистре всегда хранилось только актуальное состояние (без истории «кто и когда менял флажок»), рекомендуем сделать регистр непериодическим.

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

← На главную