В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда необходимо реализовать дополнительную логику при проведении документа, не внося изменения в сам объект (в модуль документа). Для этого идеально подходят подписки на события. Однако разработчики нередко сталкиваются с проблемой: программный код записи в независимый регистр сведений внутри подписки отрабатывает без ошибок, но после завершения транзакции регистр остается пустым.
В данной статье мы подробно разберем, почему это происходит, как диагностировать проблему и какие методы записи данных в регистр являются наиболее надежными. Проанализируем ситуацию на примере работы с регистрами расчета прав на отпуска, где корректность данных критически важна.
Рассмотрим типичный сценарий. Разработчик создает подписку на событие ОбработкаПроведения. Внутри обработчика размещен код, который формирует таблицу значений с данными и загружает ее в набор записей независимого регистра сведений. При запуске этого же кода через внешнюю обработку записи успешно появляются в базе. При проведении документа — нет.
Первым делом выясним причину такого поведения. Разберем основные факторы:
ПриЗаписи движения другого регистра) очищает этот же регистр или записывает пустой набор по тому же отбору, данные первой подписки будут потеряны.ОбработкаПроведения выполняется внутри одной транзакции с документом. Если после выполнения вашего кода где-то в системе возникнет ошибка или установится Отказ = Истина, вся транзакция откатится, включая ваши записи в независимый регистр.Рассмотрим пример кода, который часто используется для записи данных. Проанализируем его структуру и выявим потенциальные слабые места.
ОписаниеТипаДата = Новый ОписаниеТипов("Дата", , , , , Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя));
ОписаниеТипаСотрудник = Новый ОписаниеТипов("СправочникСсылка.Сотрудники");
ОписаниеЧисловогоТипа = Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(15,2));
ТаблицаНабора = Новый ТаблицаЗначений;
ТаблицаНабора.Колонки.Добавить("Период", ОписаниеТипаДата);
ТаблицаНабора.Колонки.Добавить("Сотрудник", ОписаниеТипаСотрудник);
ТаблицаНабора.Колонки.Добавить("КоличествоДней", ОписаниеЧисловогоТипа);
СтрокаТаблицы = ТаблицаНабора.Добавить();
СтрокаТаблицы.Период = ТекущаяДата();
СтрокаТаблицы.Сотрудник = СсылкаНаСотрудника;
СтрокаТаблицы.КоличествоДней = 5;
// Работа с набором записей
НаборЗаписей = РегистрыСведений.ЗаработанныеПраваНаОтпуска.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Сотрудник.Установить(СсылкаНаСотрудника);
// Читать существующие записи не всегда обязательно, если мы хотим их полностью заменить
// НаборЗаписей.Прочитать();
НаборЗаписей.Загрузить(ТаблицаНабора);
НаборЗаписей.Записать();
Важный момент: при использовании метода Записать() без параметров, по умолчанию применяется режим Замещать = Истина. Это означает, что система сначала удаляет из базы все записи, которые соответствуют установленному фильтру (в нашем примере — по сотруднику), а затем записывает новые из набора. Если ваша ТаблицаНабора по какой-то причине оказалась пустой (например, не сработал поиск сотрудника или условие заполнения), вы просто очистите регистр по данному сотруднику.
Если нам нужно записать одну конкретную строку, иногда удобнее использовать МенеджерЗаписи. Разберем, как это выглядит в коде подписки:
ЗаписьРег = РегистрыСведений.ЗаработанныеПраваНаОтпуска.СоздатьМенеджерЗаписи();
ЗаписьРег.Период = ТекущаяДата();
ЗаписьРег.Сотрудник = СотрудникСсылка;
ЗаписьРег.ВидЕжегодногоОтпуска = Справочники.ВидыОтпусков.Основной;
ЗаписьРег.КоличествоДней = 5;
Попытка
ЗаписьРег.Записать(Истина); // Истина означает замещение существующей записи с такими же измерениями
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Ошибка записи регистра", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(Инфо));
КонецПопытки;
Преимущество этого метода в его простоте, однако он менее производителен при записи большого объема данных по сравнению с НаборомЗаписей.
Если код отрабатывает, но данных нет, проанализируем ситуацию с помощью отладчика и системных механизмов. Пройдем по шагам:
ПриЗаписи, ОбработкаПроведения и ПередЗаписью.Чтобы избежать "мистического" исчезновения данных, придерживаемся следующих правил:
Во-первых, проверяйте заполнение данных перед записью. Если поиск ссылки (например, НайтиПоНаименованию) вернул пустую ссылку, запись набора с таким отбором может привести к удалению данных.
Во-вторых, используйте свойство ДополнительныеСвойства объекта документа. Вместо прямой записи в регистр внутри ОбработкаПроведения, можно поместить туда таблицу данных. А саму запись произвести в событии ПриЗаписи (которое срабатывает в самом конце). Это позволит гарантировать, что стандартные механизмы проведения уже отработали и не затрут ваши данные.
В-третьих, всегда обрабатывайте исключения. Запись в регистр может блокироваться другими пользователями или процессами. Использование конструкции Попытка ... Исключение с выводом сообщения в журнал регистрации поможет понять причину сбоя в реальных условиях эксплуатации.
Проанализировав ситуацию, мы видим, что разработка в подписках требует комплексного подхода. Проблема "пустого регистра" — это чаще всего результат конфликта бизнес-логики разных модулей, а не ошибка в коде записи. Внимательный аудит смежных подписок и понимание механизмов транзакционности 1С позволяют эффективно решать такие задачи.