В практике разработки на платформе 1С часто возникает задача логирования или ведения истории изменений конкретных объектов. Рассмотрим ситуацию, когда при записи элемента справочника Кластеры необходимо автоматически заносить текущие значения его реквизитов в периодический регистр сведений ИзмененияОбъектов. Это позволяет отслеживать, кто, когда и какие параметры изменял в системе.
Прежде чем переходить к кодированию, разберем структуру объектов, с которыми мы работаем. Согласно условию задачи, у нас есть справочник Кластеры со следующими ключевыми полями:
Данные должны попадать в периодический регистр сведений ИзмененияОбъектов. Периодичность регистра важна, так как она позволяет автоматически фиксировать момент изменения (поле Период). В регистре созданы измерения и реквизиты: Наименование, Пользователь, Строка1 (для хранения имени кластера), Число1 (для порта) и Спр1 (для ссылки на версию платформы).
Разберем самый распространенный способ решения для начинающих разработчиков — написание кода непосредственно в модуле формы справочника. Мы будем использовать обработчик события ПриЗаписи. Важно помнить, что в управляемых формах этот обработчик выполняется на сервере, что позволяет нам напрямую работать с базой данных.
Проанализируем программный код, который создает запись в регистре:
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Создаем набор записей для регистра сведений
НаборЗаписей = РегистрыСведений.ИзмененияОбъектов.СоздатьНаборЗаписей();
// Если регистр периодический, важно понимать, что мы добавляем новую запись,
// а не замещаем старые за весь период.
// Для этого мы можем установить отбор по периоду и объекту, если это требуется.
// В данной задаче мы просто добавляем новую точку в историю.
НоваяЗапись = НаборЗаписей.Добавить();
// Заполняем стандартные и пользовательские поля
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Наименование = ТекущийОбъект.Наименование;
// Получаем текущего пользователя системы
НоваяЗапись.Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь().Наименование;
// Заполняем специфические реквизиты из объекта справочника
НоваяЗапись.Строка1 = ТекущийОбъект.Кластер;
НоваяЗапись.Число1 = ТекущийОбъект.Порт;
НоваяЗапись.Спр1 = ТекущийОбъект.ВерсияПлатформы;
// Фиксируем изменения в базе данных
Попытка
НаборЗаписей.Записать(Ложь); // Параметр Ложь означает, что мы не замещаем существующие записи
Исключение
ТекстОшибки = ОписаниеОшибки();
Сообщить("Не удалось записать историю изменений: " + ТекстОшибки);
КонецПопытки;
КонецПроцедуры
Рассмотрим важный нюанс: использование метода Записать(Ложь). По умолчанию метод Записать() набора записей удаляет все существующие записи, подходящие под установленный отбор, и записывает новые. Если отбор не установлен, использование метода без параметра или с параметром Истина может очистить весь регистр! Поэтому для добавления записи в историю мы используем Ложь.
Если названия реквизитов в справочнике и названия полей в регистре совпадают, мы можем значительно упростить наш код. Вместо ручного присваивания каждой переменной, воспользуемся мощным инструментом платформы — методом ЗаполнитьЗначенияСвойств. Выясним, как это сокращает трудозатраты.
// Вместо множества строк:
// ЗаписьРегистра.Строка1 = Объект.Строка1;
// ЗаписьРегистра.Число1 = Объект.Число1;
// Используем одну команду:
ЗаполнитьЗначенияСвойств(НоваяЗапись, ТекущийОбъект);
Этот метод проанализирует оба объекта, найдет поля с одинаковыми именами и типами, и автоматически перенесет данные. Это не только делает код чище, но и снижает риск возникновения ошибок при опечатках в именах переменных.
Чтобы пользователь мог просматривать накопленные данные, в регистре сведений ИзмененияОбъектов необходимо создать форму списка. Выполним следующие шаги в конфигураторе:
Помните, что если форма списка не создана явно, 1С генерирует её автоматически в режиме "Предприятие". Однако создание собственной формы позволяет настроить фильтры, группировки и условное оформление (например, выделять цветом изменения важных портов или критических версий платформы).
Разберем ситуацию: что будет, если программист решит изменить данные справочника Кластеры программно, не открывая форму? В этом случае код, написанный в форме, не сработает, и запись в историю не попадет. Чтобы избежать такой потери данных, профессионалы используют Подписки на события.
Проанализируем алгоритм создания подписки:
ПодпискаНаСобытие.СправочникОбъект.Кластеры.ПриЗаписи.Код в общем модуле будет выглядеть практически так же, как и в модуле формы, но он будет гарантированно срабатывать при любом изменении объекта в системе — будь то ручной ввод, загрузка из Excel или обмен данными с другой базой.
Если состав реквизитов справочника Кластеры часто меняется, создавать под каждый новый реквизит отдельную колонку в регистре неудобно. В сообщении форума упоминается отличная идея — сериализация. Мы можем собрать все значимые поля объекта в структуру, преобразовать её в JSON-строку и записать в одно текстовое поле регистра.
Посмотрим на пример реализации такого подхода:
ЗаписьРегистра = РегистрыСведений.ИзмененияОбъектов.СоздатьМенеджерЗаписи();
ЗаписьРегистра.Период = ТекущаяДата();
ЗаписьРегистра.Наименование = ЭтотОбъект.Наименование;
// Собираем данные в структуру
ДанныеДляЗаписи = Новый Структура;
ДанныеДляЗаписи.Вставить("Порт", ЭтотОбъект.Порт);
ДанныеДляЗаписи.Вставить("Логин", ЭтотОбъект.Логин);
ДанныеДляЗаписи.Вставить("Версия", Строка(ЭтотОбъект.ВерсияПлатформы));
// Преобразуем в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ДанныеДляЗаписи);
СтрокаИстории = ЗаписьJSON.Закрыть();
ЗаписьРегистра.СтрокаДанных = СтрокаИстории;
ЗаписьРегистра.Записать();
Такой метод позволяет хранить неограниченное количество параметров, не расширяя структуру регистра сведений, что делает систему более гибкой и производительной при больших объемах данных.