Как программно записать данные в подчиненный регистр сведений из формы документа?

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

В процессе разработки конфигураций на платформе 1С:Предприятие 8 часто возникает необходимость логировать действия пользователя или фиксировать изменения реквизитов еще до того, как документ будет проведен. Типичный пример — запись истории изменения количества в строках табличной части. Однако при попытке реализовать это программно многие сталкиваются с тем, что данные в регистр просто не попадают, хотя код выполняется без ошибок. Разберем подробно, почему так происходит и как правильно организовать запись в регистр сведений.

Анализ проблемы: почему флаг «Записывать» не работает в форме

Рассмотрим ситуацию, когда разработчик пытается записать движение в регистр сведений ЛогРедактированияДокументов прямо в обработчике события ПриИзменении. В коде часто встречается следующая конструкция:


Движения.ЛогРедактированияДокументов.Записывать = Истина;
Движение = Движения.ЛогРедактированияДокументов.Добавить();
// Заполнение полей движения...

Проанализируем этот подход. Свойство Записывать у коллекции движений — это специальный флаг-признак для платформы. Он эффективно работает только внутри системной процедуры ОбработкаПроведения, которая располагается в модуле объекта документа. Когда управление выходит из ОбработкаПроведения, платформа проверяет этот флаг: если он установлен в значение Истина, система автоматически выполняет транзакционную запись накопленных движений в базу данных.

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

Решение №1: Явный вызов метода Записать()

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


// Внутри процедуры ПриИзменении
Движения.ЛогРедактированияДокументов.Записывать = Истина; // Флаг можно оставить, но он не обязателен здесь
Движение = Движения.ЛогРедактированияДокументов.Добавить();
Движение.Пользователь = ПараметрыСеанса.ТекущийПользователь;
Движение.Было = СтароеЗначение; // Нужно заранее сохранить старое значение
Движение.Документ = ЭтотОбъект.Ссылка;
Движение.Период = ТекущаяДатаСеанса();
Движение.Регистратор = ЭтотОбъект.Ссылка;

// Самый важный этап:
Движения.ЛогРедактированияДокументов.Записать(); 

Рассмотрим подробнее, что происходит при вызове Движения.ЛогРедактированияДокументов.Записать(). По умолчанию этот метод вызывается с параметром Замещать = Истина. Это критически важный момент для логирования. Если документ уже имел записи в этом регистре, то при вызове Записать() все старые записи этого регистратора будут удалены и заменены текущим содержимым коллекции Движения.

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

Решение №2: Использование Менеджера записи (для независимых регистров)

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

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


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

Важные технические нюансы при работе из формы

При реализации записи из формы документа следует учитывать несколько «подводных камней», которые могут привести к ошибкам в работе программы:

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

Рекомендации по правильной архитектуре логирования

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

Также не забывайте о возможности использования стандартного механизма Версионирования объектов (входит в БСП). Этот инструмент позволяет автоматически отслеживать, кто, когда и какие поля изменил, не написав при этом ни одной строки кода вручную.

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

← На главную