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

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

При разработке расширений или доработке типовых конфигураций, таких как Зарплата и Управление Персоналом 3.1, программисты часто сталкиваются с необходимостью записывать дополнительные данные в собственные регистры сведений при проведении типовых документов (например, «Исполнительный лист»). Однако стандартный подход с созданием набора записей вручную часто приводит к странному поведению: при первом проведении запись появляется, а при повторном — бесследно исчезает. Разберем подробно, почему так происходит, и как реализовать этот механизм правильно.

Анализируем причину проблемы: механизм очистки движений

В платформе «1С:Предприятие 8.3» для большинства документов установлено свойство «Удаление движений» в значение «Удалять автоматически при перепроведении». Это фундаментальный механизм, который гарантирует актуальность данных в регистрах. Когда пользователь нажимает кнопку «Провести», система выполняет следующие действия:

  1. Открывает транзакцию.
  2. Автоматически удаляет все старые записи по всем регистрам, где данный документ является регистратором.
  3. Запускает код модуля документа (или подписки на события) для формирования новых записей.
  4. Фиксирует транзакцию.

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

Решение №1: Использование коллекции «Движения» (Рекомендуемый способ)

Самый надежный и архитектурно правильный способ — использовать штатную коллекцию документа. Проанализируем, как изменить код, чтобы он работал стабильно. Вместо создания нового объекта РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей(), мы должны обратиться к свойству Движения документа-источника.

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

Рассмотрим пример правильного кода:


// В обработчике проведения или подписке на событие ОбработкаПроведения
// Источник — это ссылка на документ (или сам объект)

// Получаем набор записей из коллекции Движения
НаборЗаписей = Источник.Движения.ИНФ_РеквизитыИсполЛистовДляВыгрузкиБП;

// Указывать отбор по регистратору здесь не нужно, он устанавливается автоматически
// Также не нужно вызывать методы Прочитать() или Очистить()

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... "; // Ваш запрос для получения данных
РезультатЗапроса = Запрос.Выполнить();

// Загружаем данные из таблицы значений напрямую в набор
НаборЗаписей.Загрузить(РезультатЗапроса.Выгрузить());

// Устанавливаем флаг необходимости записи
НаборЗаписей.Записывать = Истина;

Почему это работает: Устанавливая свойство Записывать = Истина, мы сообщаем платформе, что в конце процесса проведения этот набор нужно зафиксировать в базе данных вместе со всеми остальными движениями документа. Это гарантирует транзакционность: если проведение документа прервется ошибкой, записи в вашем регистре также не будут созданы.

Решение №2: Проверка результатов запроса

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

Когда вы выполняете НаборЗаписей.Загрузить(ПустаяТЗ) и затем Записать(), платформа воспринимает это как команду на удаление существующих записей. Проанализируем, как добавить защитное условие:


ТаблицаДанных = Запрос.Выполнить().Выгрузить();

Если ТаблицаДанных.Количество() > 0 Тогда
    НаборЗаписей = Источник.Движения.ИНФ_РеквизитыИсполЛистовДляВыгрузкиБП;
    НаборЗаписей.Загрузить(ТаблицаДанных);
    НаборЗаписей.Записывать = Истина;
Иначе
    // Если данных нет, можно либо явно очистить движения, 
    // либо оставить флаг Записывать = Ложь (по умолчанию), тогда старые движения просто удалятся системой
КонецЕсли;

Особенности работы в ЗУП 3.1

В современных конфигурациях на базе БСП (включая ЗУП 3.1) процесс проведения часто выносится в общие модули, такие как ПроведениеСервер. Рассмотрим несколько нюансов, специфичных для этой программы:

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

Итоги и рекомендации

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

Использование встроенных механизмов платформы не только решает проблему «исчезающих записей», но и делает ваш код более производительным и безопасным с точки зрения целостности данных в базе 1С.

← На главную