В процессе разработки и поддержки конфигураций 1С часто возникает задача синхронизации данных. Одной из самых сложных ситуаций является необходимость внесения изменений в документы или другие объекты при удалении записи из регистра сведений, особенно если удаление происходит интерактивно пользователем. На первый взгляд задача кажется простой, но она скрывает в себе риски транзакционных блокировок и потерю данных — поможет журнал изменений и восстановление удаленных объектов. В этой статье мы подробно разберем, как правильно реализовать этот механизм, избегая типичных ошибок.
Прежде чем приступать к кодингу, проанализируем ситуацию. Когда пользователь или программа удаляет запись из регистра, 1С работает не с одной строкой, а с набором записей. Если мы попытаемся изменить документ непосредственно в момент удаления записи регистра, мы можем столкнуться с Deadlock (взаимной блокировкой) — решит задачу инструмент анализа производительности и блокировок. Это происходит потому, что запись в регистр часто сама является частью транзакции проведения документа. Попытка "развернуться" и записать документ из модуля регистра приведет к тому, что два процесса будут бесконечно ждать друг друга.
Рассмотрим самый простой способ определить, что происходит полная очистка данных по определенному фильтру. В модуле набора записей регистра (или в подписке на события) мы можем проанализировать состояние объекта ЭтотОбъект. Выясним причину, по которой вызывается событие ПередЗаписью — для этого подойдёт настройка произвольных проверок и условий контроля.
Если выполняется полное удаление записей по отбору (например, программно вызван метод Записать(Истина) для пустого набора), то количество строк в текущем наборе будет равно нулю. Посмотрим на пример кода:
Процедура ПередЗаписью(Отказ, Замещение)
// Если Замещение = Истина и количество записей в наборе 0 -
// значит, данные по этому отбору будут полностью удалены.
Если Замещение И ЭтотОбъект.Количество() = 0 Тогда
// Здесь мы понимаем, что записи удаляются.
// Однако старые данные в ЭтотОбъект уже недоступны.
КонецЕсли;
КонецПроцедуры
Проанализируем более сложную ситуацию: когда нам нужно знать, какие именно записи были удалены, чтобы обновить соответствующие им документы. Для этого нам понадобится временно сохранить старые данные. Разберем по шагам алгоритм использования ДополнительныеСвойства.
Шаг 1: В событии ПередЗаписью считываем текущие данные из базы, которые еще не успели удалиться.
Процедура ПередЗаписью(Отказ, Замещение)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Считываем то, что сейчас лежит в БД по данному отбору
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрСведений.ИмяВашегоРегистра ГДЕ <ФильтрПоОтборуНабора>";
// ... установка параметров из ЭтотОбъект.Отбор
ТаблицаСтарыхДанных = Запрос.Выполнить().Выгрузить();
ЭтотОбъект.ДополнительныеСвойства.Вставить("СтарыеДанные", ТаблицаСтарыхДанных);
КонецПроцедуры
Шаг 2: В событии ПриЗаписи сравниваем старые данные с новыми. Если в старых данных запись была, а в новом наборе (ЭтотОбъект) её нет — значит, произошло удаление конкретной строки.
Процедура ПриЗаписи(Отказ)
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("СтарыеДанные") Тогда
СтарыеДанные = ЭтотОбъект.ДополнительныеСвойства.СтарыеДанные;
// Логика сравнения ТаблицыСтарыхДанных и текущего набора ЭтотОбъект
// Находим удаленные строки и формируем список документов для обновления
КонецЕсли;
КонецПроцедуры
Как мы уже выяснили, изменять документы прямо внутри ПриЗаписи опасно. Вместо этого рассмотрим архитектурно верный подход: регистрацию задания. Создадим вспомогательный регистр сведений (назовем его ОчередьОбновленияДокументов).
ПриЗаписи), вместо поиска и записи документа, мы записываем только ссылку на этот документ в наш новый регистр-очередь.РегламентноеЗадание, которое будет запускаться раз в минуту, считывать записи из ОчередьОбновленияДокументов, обрабатывать документы и очищать очередь.Этот метод гарантирует, что основная транзакция записи регистра завершится быстро, а тяжелые операции с документами пройдут в фоновом режиме, не вызывая блокировок у пользователей.
В современных версиях платформы (8.3.11+) для регистров сведений можно включить стандартный механизм История данных — для этого подойдёт ведение детальной истории изменений объектов. Проанализируем ситуацию: если в свойствах регистра включено логирование истории, платформа сама записывает все изменения. Мы можем программно обратиться к методам ИсторияДанных.ВыбратьВерсии(), чтобы найти факт удаления. Это избавляет от необходимости писать свои "зеркальные" регистры, но требует настройки состава хранимых данных.
Иногда лучшим техническим решением является запрет самого действия. Если бизнес-логика подразумевает, что записи регистра должны всегда соответствовать документам, логично запретить пользователям удалять их вручную. Рассмотрим настройку прав доступа (ролей):
Интерактивное удаление для данного регистра.Запись только для программного кода, который вызывается при проведении или отмене проведения документов.Важный момент: если база "переписанная" и в ней много ручного ввода в регистры, этот метод может потребовать создания специальной обработки для администратора, но он на порядок повысит чистоту данных.
Если вы работаете в типовой конфигурации и не хотите снимать регистр с поддержки, используйте Подписки на события (поможет анализ зависимостей между объектами и расширениями). Создайте новую подписку на источник РегистрСведенийНаборЗаписей.ИмяВашегоРегистра для событий ПередЗаписью и ПриЗаписи. Это позволит вынести всю логику отслеживания в отдельный общий модуль, сохраняя типовую конфигурацию обновляемой.
Подведем итог: наиболее надежным и современным способом является сочетание Подписки на события для фиксации изменений и Очереди задач (через вспомогательный регистр или регламентное задание) для последующего обновления документов. Это обеспечит стабильность системы и отсутствие конфликтов блокировок.