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