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