Протоколирование изменений в любом регистре сведений

Представляю написанную мной процедуру, которая, будучи своевременно встроена в программу 1С Предприятие 8.2, позволит отследить внесение изменений (запись/удаление) в любом регистре сведений (или в нескольких регистрах одновременно).

Процедура встраивается в конфигурацию путём добавления её в общий модуль, далее нужно создать подписку на событие ПередЗаписью требуемых регистров сведений и назначить эту процедуру обработчиком этого события.

Результатом работы процедуры являются записи в журнале регистрации, в которых кратко записывается суть производимых с регистром сведений изменений (одна-две строки в журнале регистрации на каждую транзакцию в регистре). При написании процедуры я руководствовался принципом : результат работы процедуры в журнале регистрации должен быть хорошо читаем и быстро осознаваем человеком.

Особенности реализации : если в наборе записей регистра сведений больше одной записи, то подробно протоколируется только первая запись набора и количество записей в наборе.

Программный код написан в стиле этюда : в тесте процедуры закомментирован код, который позволит ещё более усовершенствовать работу этой процедуры (в частности : получить информацию, которая будет вытеснена из регистра после записи набора). Мне это усовершенствование на практике пока не потребовалось, поэтому данная ветка на момент написания этой статьи не получила развитие.

О практической пользе этой процедуры. Эта процедура, будучи своевременно встроена в базу 1С Зарплата и управление персоналом 8.2, реально помогла мне отследить порчу конкретным пользователем штатного расписания в базе 1С Зарплата и Управление персоналом 8.2 и переклассифицировать инцидент «порча данных штатного расписания» из разряда «программный сбой» в «ошибку пользователя».

Процедура ЖурнализированиеРегистраСведенийПередЗаписью(Источник, Отказ, Замещение) Экспорт

    НаборЗаписей = Источник;

    ТаблицаДляПечати1 = НаборЗаписей.Выгрузить();

    ОписаниеИзменений1 = «»;

    СсылкаДляОтраженияВЖурналеРегистрации1 = Неопределено;

    Если ТаблицаДляПечати1.Количество() = 0 Тогда
       
// в регистр пишется пустой набор записей, т.е. происходит удаление из регистра сведений по наложенному в наборе отбору

        ЧтоПроисходит1 = «Удаление в регистре сведений»;

        МетаданныеНабора = НаборЗаписей.Метаданные();

        ИмяРегистра = МетаданныеНабора.ПолноеИмя();

        // Формируем текст условия блока ГДЕ основного запроса,
        // в соответствии с установленным отбором для набора записей

        Запрос = Новый Запрос;

        СписокПолейУсловияОтбораТекст = «»;
       
Итерация = 0;
        Для каждого
ЭлементОтбора Из НаборЗаписей.Отбор Цикл
            Если не
ЭлементОтбора.Использование Тогда
                Продолжить;
            КонецЕсли;

            Если Итерация = Тогда
               
ОписаниеИзменений1 = ОписаниеИзменений1 + «Отбор : «;
            Иначе
               
СписокПолейУсловияОтбораТекст = СписокПолейУсловияОтбораТекст  + » И «;
            КонецЕсли;

            СписокПолейУсловияОтбораТекст = СписокПолейУсловияОтбораТекст +» Набор.» + ЭлементОтбора.Имя + » = &» + ЭлементОтбора.Имя;

            // здесь нужно сохранить в журнале регистрации значения отборов
           
ОписаниеИзменений1 = ОписаниеИзменений1 + «» + ЭлементОтбора.Имя + » = » + СокрЛП(Формат(ЭлементОтбора.Значение, «ДФ=dd.MM.yyyy»)) + «» + Символы.ПС;

            Если СсылкаДляОтраженияВЖурналеРегистрации1 = Неопределено Тогда
                Если
ТипЗнч(ЭлементОтбора.Значение) <> Тип(«Дата») Тогда // дата в качестве представления объекта не нужна! лучше любой ссылочный тип
                   
СсылкаДляОтраженияВЖурналеРегистрации1 = ЭлементОтбора.Значение;
                КонецЕсли;
            КонецЕсли;
           
Запрос.УстановитьПараметр(ЭлементОтбора.Имя, ЭлементОтбора.Значение);

            Итерация = 1;
        КонецЦикла;

        Если Итерация = 1 Тогда
           
СписокПолейУсловияОтбораТекст = » ГДЕ » + СписокПолейУсловияОтбораТекст;
        КонецЕсли;

        Запрос.Текст = «ВЫБРАТЬ * ИЗ » + ИмяРегистра + » КАК Набор
        | «
+ СписокПолейУсловияОтбораТекст + «»;

        // ТаблицаДляПечати1 = Запрос.Выполнить().Выгрузить();
        // а тут лежат старые данные в регистре (которые перетираются!!!)
        // их можно анализировать и печатать

    Иначе // записывается непустой набор записей

        ЧтоПроисходит1 = «Запись в регистр сведений»;

        Для каждого Запись1 Из ТаблицаДляПечати1 Цикл

            Для каждого Колонка1 Из ТаблицаДляПечати1.Колонки Цикл

                ЗначениеКолонки1 = Запись1[Колонка1.Имя];
                Если
ЗначениеЗаполнено(ЗначениеКолонки1) Тогда // печатаем почти все значения непустых измерений, ресурсов, реквизитов!

                    ТипЗнач1 = ТипЗнч(ЗначениеКолонки1);

                    МожноПечататьПолноеЗначениеКолонки1 = Истина;
                    Если
ТипЗнач1 = Тип(«Число») Тогда
                        Если
ЗначениеКолонки1 > 1000 Тогда
                           
// защита данных в журнале регистрации : не печатаем числовые значения колонок, если они превышают 1000 !
                            // например, это могут быть вилки окладов!
                           
МожноПечататьПолноеЗначениеКолонки1 = Ложь;
                        КонецЕсли;
                    КонецЕсли;

                    Если МожноПечататьПолноеЗначениеКолонки1 Тогда
                       
ОписаниеИзменений1 = ОписаниеИзменений1 + Колонка1.Имя + » = » + Формат(ЗначениеКолонки1, «ДФ=dd.MM.yyyy») + Символы.ПС;

                        Если СсылкаДляОтраженияВЖурналеРегистрации1 = Неопределено Тогда
                            Если
ТипЗнач1 <> Тип(«Дата») Тогда
                               
СсылкаДляОтраженияВЖурналеРегистрации1 = ЗначениеКолонки1;
                            КонецЕсли;
                        КонецЕсли;

                    Иначе
                       
ОписаниеИзменений1 = ОписаниеИзменений1 + Колонка1.Имя + » > 1000″ + Символы.ПС;
                    КонецЕсли;

                КонецЕсли;
            КонецЦикла;

            Прервать; // прочтём только первую запись

        КонецЦикла;

        Если ТаблицаДляПечати1.Количество() > 1 Тогда

            // по остальным записям — укажем только полное количество записей

            ОписаниеИзменений1 = «Всего записей набора = » + ТаблицаДляПечати1.Количество() + «; первая запись :» + Символы.ПС + ОписаниеИзменений1;

        КонецЕсли;

    КонецЕсли;

    ЗаписьЖурналаРегистрации(ЧтоПроисходит1, УровеньЖурналаРегистрации.Предупреждение, МетаданныеНабора, СсылкаДляОтраженияВЖурналеРегистрации1, СокрЛП(ОписаниеИзменений1));

КонецПроцедуры

Запись опубликована в рубрике Ошибки пользователей, Программное обеспечение, Работа, Регламенты с метками , , , . Добавьте в закладки постоянную ссылку.

3 комментария на «Протоколирование изменений в любом регистре сведений»

  1. Andrey говорит:

    Какое значение передавать в параметре «Источник»?

  2. Николай говорит:

    Было бы не плохо если бы он еще показывал запись до изменения! 🙂

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.