Ведение лога изменений документов: модули   

Теперь - глобальный модуль:
//Переменные
Перем ПроведенСейчас Экспорт;
Перем ЗаписанСейчас Экспорт;

Функция глЗапомнитьДокумент(Конт) Экспорт
    Перем СЗ;
    ТекВид=Конт.Вид();
    СЗ=СоздатьОбъект("СписокЗначений");
    СЗ.ДобавитьЗначение(Конт.ДатаДок,"ДатаДок");
    СЗ.ДобавитьЗначение(Конт.ПолучитьВремя(),"ВремяДок");
    //Добавлено для сравнения общих реквизитов документа
    Для к=1 по Метаданные.ОбщийРеквизитДокумента() Цикл
        ТекИд=Метаданные.ОбщийРеквизитДокумента(к).Идентификатор;
        ТекЗнач=Конт.ПолучитьАтрибут(ТекИд);
        СЗ.ДобавитьЗначение(ТекЗнач,ТекИд);
    КонецЦикла;
    //Конец добавления
    Для к=1 по Метаданные.Документ(ТекВид).РеквизитШапки() Цикл
        ТекИд=Метаданные.Документ(ТекВид).РеквизитШапки(к).Идентификатор;
        ТекЗнач=Конт.ПолучитьАтрибут(ТекИд);
        СЗ.ДобавитьЗначение(ТекЗнач,ТекИд);
    КонецЦикла;
    Для к=1 по Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти() Цикл
        Если Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти(к).ИтогПоКолонке=1 Тогда
            ТекИд=Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти(к).Идентификатор;
            ТекЗнач=Конт.Итог(ТекИд);
            СЗ.ДобавитьЗначение(ТекЗнач,ТекИд);
        КонецЕсли;
    КонецЦикла;
    Возврат СЗ;
КонецФункции

Эта функция запоминает реквизиты документа в списке значений, который возвращается в вызывающий документ и хранится там в переменной до закрытия документа. Через метаданные перебираются все реквизиты шапки и реквизиты табличной части с итогами по колонке.

Процедура глСравнитьДокумент(Конт,СЗ) Экспорт
    Перем ТаблИзм;
    Перем СпрЛог, СпрИстория, СпрЗапись;
    ТекВид=Конт.Вид();
    ТаблИзм=СоздатьОбъект("ТаблицаЗначений");
    ТаблИзм.НоваяКолонка("Показатель","Строка",30);
    ТаблИзм.НоваяКолонка("Было","Строка",30);
    ТаблИзм.НоваяКолонка("Стало","Строка",30);
    //________________________________________________________/
    ТекЗнач=Конт.ДатаДок;
    ПрошлЗнач=СЗ.Получить("ДатаДок");
    Если ТекЗнач<>ПрошлЗнач Тогда
        ТаблИзм.НоваяСтрока();
        ТаблИзм.Показатель="Дата";
        ТаблИзм.Было=СокрЛП(Строка(ПрошлЗнач));
        ТаблИзм.Стало=СокрЛП(Строка(ТекЗнач));
    КонецЕсли;
    ТекЗнач=Конт.ПолучитьВремя();
    ПрошлЗнач=СЗ.Получить("ВремяДок");
    Если ТекЗнач<>ПрошлЗнач Тогда
        ТаблИзм.НоваяСтрока();
        ТаблИзм.Показатель="Время";
        ТаблИзм.Было=СокрЛП(Строка(ПрошлЗнач));
        ТаблИзм.Стало=СокрЛП(Строка(ТекЗнач));
    КонецЕсли;
    //Добавлено для сравнения общих реквизитов документа
    Для к=1 по Метаданные.ОбщийРеквизитДокумента() Цикл
        ТекИд=Метаданные.ОбщийРеквизитДокумента(к).Идентификатор;
        ТекЗнач=Конт.ПолучитьАтрибут(ТекИд);
        ПрошлЗнач=СЗ.Получить(ТекИд);
        Если ТекЗнач<>ПрошлЗнач Тогда
            ТаблИзм.НоваяСтрока();
            ТаблИзм.Показатель=СокрЛП(ТекИд);
            ТаблИзм.Было=СокрЛП(Строка(ПрошлЗнач));
            ТаблИзм.Стало=СокрЛП(Строка(ТекЗнач));
        КонецЕсли;
    КонецЦикла;
    //Конец добавления
    Для к=1 по Метаданные.Документ(ТекВид).РеквизитШапки() Цикл
        ТекИд=Метаданные.Документ(ТекВид).РеквизитШапки(к).Идентификатор;
        ТекЗнач=Конт.ПолучитьАтрибут(ТекИд);
        ПрошлЗнач=СЗ.Получить(ТекИд);
        Если ТекЗнач<>ПрошлЗнач Тогда
            ТаблИзм.НоваяСтрока();
            ТаблИзм.Показатель=СокрЛП(ТекИд);
            ТаблИзм.Было=СокрЛП(Строка(ПрошлЗнач));
            ТаблИзм.Стало=СокрЛП(Строка(ТекЗнач));
        КонецЕсли;
    КонецЦикла;
    Для к=1 по Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти() Цикл
        Если Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти(к).ИтогПоКолонке=1 Тогда
            ТекИд=Метаданные.Документ(ТекВид).РеквизитТабличнойЧасти(к).Идентификатор;
            ТекЗнач=Конт.Итог(ТекИд);
            ПрошлЗнач=СЗ.Получить(ТекИд);
            Если ТекЗнач<>ПрошлЗнач Тогда
                ТаблИзм.НоваяСтрока();
                ТаблИзм.Показатель=СокрЛП(ТекИд);
                ТаблИзм.Было=СокрЛП(Строка(ПрошлЗнач));
                ТаблИзм.Стало=СокрЛП(Строка(ТекЗнач));
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Если ТаблИзм.КоличествоСтрок()>0 Тогда
        //есть изменения - запоминаем документ
        СпрЛог=СоздатьОбъект("Справочник.Лог");
        СпрИстория=СоздатьОбъект("Справочник.История");
        СпрЗапись=СоздатьОбъект("Справочник.Запись");
        НачатьТранзакцию();
        Если СпрЛог.НайтиПоРеквизиту("Док",Конт.ТекущийДокумент(),1)=0 Тогда
            СпрЛог.Новый();
            СпрЛог.Док=Конт.ТекущийДокумент();
            СпрЛог.НомерДок=Конт.НомерДок;
            СпрЛог.ДатаДок=Конт.ДатаДок;
            СпрЛог.ВремяДок=Конт.ПолучитьВремя();
            СпрЛог.ВидДок=Конт.Вид();
            СпрЛог.Автор=Конт.Автор;
            СпрЛог.Удален=0;
            СпрЛог.Записать();
        КонецЕсли;
        СпрИстория.ИспользоватьВладельца(СпрЛог.ТекущийЭлемент());
        СпрИстория.Новый();
        СпрИстория.ДатаПравки=ТекущаяДата();
        СпрИстория.ВремяПравки=ТекущееВремя();
        СпрИстория.АвторИзменений=глПользователь;
        СпрИстория.ПК=ИмяКомпьютера();
        СпрИстория.Права=НазваниеНабораПрав(1);
        СпрИстория.Владелец=СпрЛог.ТекущийЭлемент();
        СпрИстория.Записать();
        ТаблИзм.ВыбратьСтроки();
        СпрЗапись.ИспользоватьВладельца(СпрИстория.ТекущийЭлемент());
        Пока ТаблИзм.ПолучитьСтроку()=1 Цикл
            СпрЗапись.Новый();
            СпрЗапись.Владелец=СпрИстория.ТекущийЭлемент();
            СпрЗапись.Показатель=ТаблИзм.Показатель;
            СпрЗапись.Было=ТаблИзм.Было;
            СпрЗапись.Стало=ТаблИзм.Стало;
            СпрЗапись.Записать();
        КонецЦикла;
        ЗафиксироватьТранзакцию();
    КонецЕсли;
КонецПроцедуры

В вышеописанной процедуре происходит сравнение значений сохраненных при открытии документа с конечными значениями. Если найдены расхождения, осуществляется запись в лог-справочники.

Процедура ПриОтменеПроведенияДокумента(Конт)
    СпрЛог=СоздатьОбъект("Справочник.Лог");
    СпрИстория=СоздатьОбъект("Справочник.История");
    СпрЗапись=СоздатьОбъект("Справочник.Запись");
    НачатьТранзакцию();
    Если СпрЛог.НайтиПоРеквизиту("Док",Конт.ТекущийДокумент(),1)=0 Тогда
        СпрЛог.Новый();
        СпрЛог.Док=Конт.ТекущийДокумент();
        СпрЛог.НомерДок=Конт.НомерДок;
        СпрЛог.ДатаДок=Конт.ДатаДок;
        СпрЛог.ВремяДок=Конт.ПолучитьВремя();
        СпрЛог.ВидДок=Конт.Вид();
        СпрЛог.Автор=Конт.Автор;
        СпрЛог.Удален=0;
        СпрЛог.Записать();
    КонецЕсли;
    СпрИстория.ИспользоватьВладельца(СпрЛог.ТекущийЭлемент());
    СпрИстория.Новый();
    СпрИстория.ДатаПравки=ТекущаяДата();
    СпрИстория.ВремяПравки=ТекущееВремя();
    СпрИстория.АвторИзменений=глПользователь;
    СпрИстория.ПК=ИмяКомпьютера();
    СпрИстория.Права=НазваниеНабораПрав(1);
    СпрИстория.Владелец=СпрЛог.ТекущийЭлемент();
    СпрИстория.Записать();
    СпрЗапись.ИспользоватьВладельца(СпрИстория.ТекущийЭлемент());
    СпрЗапись.Новый();
    СпрЗапись.Владелец=СпрИстория.ТекущийЭлемент();
    СпрЗапись.Показатель="ОтменаПроведения";
    СпрЗапись.Записать();
    ЗафиксироватьТранзакцию();
КонецПроцедуры

При отмене проведения документа тоже заполняется лог. Туда просто пишется запись об отмене проведения. Это нужно для того, чтобы можно было отличить новый только что созданный документ, от документа, который уже был проведен. Для ранее проведенного документа будет вестись лог, для нового не будет - пока он не будет проведен.

Процедура ПриУдаленииДокумента(Конт,Режим)
    СпрЛог=СоздатьОбъект("Справочник.Лог");
    СпрИстория=СоздатьОбъект("Справочник.История");
    СпрЗапись=СоздатьОбъект("Справочник.Запись");
    НачатьТранзакцию();
    Если СпрЛог.НайтиПоРеквизиту("Док",Конт.ТекущийДокумент(),1)=0 Тогда
        СпрЛог.Новый();
        СпрЛог.Док=Конт.ТекущийДокумент();
        СпрЛог.НомерДок=Конт.НомерДок;
        СпрЛог.ДатаДок=Конт.ДатаДок;
        СпрЛог.ВремяДок=Конт.ПолучитьВремя();
        СпрЛог.ВидДок=Конт.Вид();
        СпрЛог.Автор=Конт.Автор;
        СпрЛог.Удален=0;
        СпрЛог.Записать();
    КонецЕсли;
    СпрИстория.ИспользоватьВладельца(СпрЛог.ТекущийЭлемент());
    СпрИстория.Новый();
    СпрИстория.ДатаПравки=ТекущаяДата();
    СпрИстория.ВремяПравки=ТекущееВремя();
    СпрИстория.АвторИзменений=глПользователь;
    СпрИстория.ПК=ИмяКомпьютера();
    СпрИстория.Права=НазваниеНабораПрав(1);
    СпрИстория.Владелец=СпрЛог.ТекущийЭлемент();
    СпрИстория.Записать();
    СпрЗапись.ИспользоватьВладельца(СпрИстория.ТекущийЭлемент());
    СпрЗапись.Новый();
    СпрЗапись.Владелец=СпрИстория.ТекущийЭлемент();
    Если Режим=0 Тогда
        //Пометка
        СпрЛог.Удален=1;
        СпрЗапись.Показатель="Помечен";
    Иначе
        //полное удаление
        СпрЛог.Док=ПолучитьПустоеЗначение("Документ");
        СпрЛог.Удален=2;
        СпрЗапись.Показатель="Удален";
    КонецЕсли;
    СпрЛог.Записать();
    СпрЗапись.Записать();
    ЗафиксироватьТранзакцию();
КонецПроцедуры

При пометке на удаление - просто пишется лог, при полном удалении - очищается поле типа «Документ», чтобы не шокировать пользователя при просмотре лога надписями типа «объект не найден».

Функция глВестиЛог(Конт) Экспорт
    Перем СпрЛог;
    //Эта функция определяет, проведен ли документ
    //если проведен - ведется лог

    Если Конт.Проведен()=1 Тогда
        Возврат 1;
    КонецЕсли;
    //документ может быть сделанным непроведенным. при этом он должен попасть в справочник.
    //ищем по справочнику
    СпрЛог=СоздатьОбъект("Справочник.Лог");
    Если СпрЛог.ВыбратьЭлементыПоРеквизиту("Док",Конт.ТекущийДокумент(),,)=1 Тогда
        //Такой же документ найден
        Возврат 1;
    КонецЕсли;
    Возврат 0;
КонецФункции

Вышеописанная функция делает проверку необходимости ведения лога. Ищется документ по реквизиту. Если найден - лог ведется, нет - не ведется.

Теперь, для того, чтобы все работало - необходимо добавить в каждый документ следующий код:

В модуль документа в процедуру «ОбработкаПроведения»:

ПроведенСейчас=1;

В модуль формы документа необходимо добавить переменные:

Перем СравниватьДокумент, ТекСписок;

В модуль формы документа необходимо добавить или подкорректировать следующие процедуры:

Процедура ПриОткрытии()
    ПроведенСейчас=0;
    ЗаписанСейчас=0;
    Если глВестиЛог(Контекст)=1 Тогда
        СравниватьДокумент=1;
        ТекСписок=глЗапомнитьДокумент(Контекст);
    Иначе
        СравниватьДокумент=0;
    КонецЕсли;
КонецПроцедуры

Процедура приЗаписи()
    ЗаписанСейчас=1;
КонецПроцедуры

Процедура ПриЗакрытии()
    Если ((ПроведенСейчас=1) Или (ЗаписанСейчас=1)) И (СравниватьДокумент=1) Тогда
        глСравнитьДокумент(Контекст,ТекСписок);
    КонецЕсли;
КонецПроцедуры

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

Обработка для просмотра результатов - на следующей странице.

 

[1]-[2]-[3]