История изменений реквизитов документа, реквизитов табличных частей документа Ключевые слова: история изменения реквизитов, версионирование
1. Зачем это нужно
Стандартный журнал регистрации в 1С фиксирует только изменения объектов, без указания конкретного реквизита и значения реквизита. А как быть, если хочется узнать, например, кто и когда в документе изменил склад, изменил количество материала в табличной части и удалил строку из табличной части? Собственно для этого и был написан данный модуль.
2. Как это работает
Из процедуры ПередЗаписью() модуля объекта вызывается процедура общего модуля, в которой сравниваются реквизиты документа и реквизиты табличных частей документа при открытии и при записи, и происходит запись в РС только тех значений, которые различаются, с соответствующим видом операции(Добавление, Изменение, Удаление). Сравнение табличных частей происходит полным соединением таблиц( До и После записи) в запросе по ключевым колонкам.
3. Ключевые колонки ТЧ
Ключевые колонки – гарантия того, что в табличной части не будет повторяющихся строк.
Если все-таки будут – ищите другие пути.
Ключевые колонки – это колонки, по которым происходит сравнение ТЧ документов(при открытии и при записи).
Например, если у вас ТЧ вида:
Материал | Количество | Цена | Сумма |
и вы знаете, что в одной табличной части никогда не будет одинаковых материалов – выбираете одну ключевую колонку «Материал». Если будут одинаковые материалы, но с разными ценами – ключевых колонок должно быть две: «Материал,Цена». Можно указать ключевыми колонками все 4, и в таком случае не должно быть полностью одинаковых строк в документе(собственно, в такой записи нет никакой необходимости).
Ключевые поля указываются через запятую в вызове процедуры
ФиксацияРеквизитовДокумента(ДокОбъект, СписокКлючевыхПолей), где ДокОбъект- это ЭтотОбъект документа, СписокКлючевыхПолей – это те ключевые поля, в разрезе которых будет происходить сравнение таблиц До и После записи. Указывается строкой, перечислением(через запятую) колонок с указанием номера табличной части. («Материал1,Цена1», если две табличной части: «Материал1,Цена1,Тара2». Тара2 – означает, что у второй табличной части нас интересует только колонка «Тара»)
Записи измененных значений в РС записываются в виде строки.
4. Код модуля
ПРоцедура ФиксацияРеквизитовДокумента(ДокОбъект, СписокКлючевыхПолей) Экспорт
Ссылка = ДокОбъект.Ссылка;
//--Реквизиты объекта
СтруктураДо = Новый Структура;
Для каждого Реквизит из Ссылка.Метаданные().Реквизиты Цикл
СтруктураДо.Вставить(Реквизит.Имя,Ссылка[Реквизит.Имя]);
КОнецЦикла;
СтруктураПосле = Новый СТруктура;
Для каждого Реквизит из ДокОбъект.Метаданные().Реквизиты Цикл
СтруктураПосле.Вставить(Реквизит.Имя,ДокОбъект[Реквизит.Имя]);
КОнецЦикла;
//--проверка и запись
Для каждого КлючИЗначение из СтруктураДо Цикл
Если КлючИЗначение.Значение <> СтруктураПосле[КлючИЗначение.Ключ] Тогда
Изменение = Перечисления.ВидДействияСОбъектом.Изменение;
РегистрацияВРегистр(ДокОбъект,Изменение,КлючИЗначение.Ключ, Строка(СтруктураДо[КлючИЗначение.Ключ]),Строка(СтруктураПосле[КлючИЗначение.Ключ]))
КонецЕсли;
КонецЦикла;
//++Проверка и запись
//++Реквизиты объекта
//--Реквизиты ТЧ объекта
СчетчикТЧ = 1;
ДЛя каждого ТЧ ИЗ Ссылка.Метаданные().ТабличныеЧасти Цикл
ТАблицаЗначенийДо = Ссылка[ТЧ.Имя].Выгрузить();
ТаблицаЗначенийПосле = ДокОбъект[ТЧ.Имя].Выгрузить();
Запрос = Новый Запрос;
Текст = "ВЫБРАТЬ
| *
|ПОМЕСТИТЬ Первая
|ИЗ
| &ТаблицаЗначенийДо КАК ЛотТЧлота
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| *
|ПОМЕСТИТЬ Вторая
|ИЗ
| &ТаблицаЗначенийПосле КАК ЛотТЧлота
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| *
|ИЗ
| Первая КАК Первая
| ПОЛНОЕ СОЕДИНЕНИЕ Вторая КАК Вторая
|ПО";
СоюзИ = "";
ИзменяемоеЗначение ="";
списокКолонок = СписокКлючевыхПолей+",";
СписокКолонок = Новый СписокЗначений;
Если Найти(СписокКлючевыхПолей,Строка(СчетчикТЧ)) Тогда //Если поля по текущей ТЧ указаны
Пока Найти(СокрЛП(СписокКлючевыхПолей),Строка(СчетчикТЧ)) Цикл//то добавляем их в условие соединения
КОлонкаТЧ = Сред(СписокКлючевыхПолей,1,Найти(СписокКлючевыхПолей,Строка(СчетчикТЧ))-1);
СписокКолонок.Добавить(КОлонкаТЧ);
СписокКлючевыхПолей = СтрЗаменить(СписокКлючевыхПолей, КолонкаТЧ+Строка(СчетчикТЧ)+",","");
Текст = Текст+СоюзИ+"
| Первая."+КолонкаТЧ+" = Вторая."+КолонкаТЧ;
СоюзИ ="
|И";
КонецЦикла;
Иначе
ПРервать;
КонецЕСли;
Запрос.Текст = Текст;
Запрос.УстановитьПараметр("ТаблицаЗначенийДо",ТаблицаЗначенийДо);
Запрос.УстановитьПараметр("ТаблицаЗначенийПосле",ТаблицаЗначенийПосле);
Выборка = Запрос.Выполнить().Выбрать();
Пока выборка.Следующий() Цикл
Если Выборка[КолонкаТЧ] = NULL тогда //Добавили
ВИдДействия = Перечисления.ВидДействияСОбъектом.Добавление;
Добавлено = "1";
КонецЕсли;
Если Выборка[КолонкаТЧ+"1"] = NULL тогда //Удалили
ВидДействия = Перечисления.ВидДействияСОбъектом.Удаление;
Добавлено = "";
КонецЕсли;
Если Выборка[КолонкаТЧ] = Выборка[КолонкаТЧ+"1"] тогда //Изменили
ВидДействия = перечисления.ВидДействияСОбъектом.Изменение;
Добавлено = "";
КонецЕсли;
Для каждого Колонка из ТаблицаЗначенийДо.КОлонки Цикл
Если Колонка.Имя <> "НомерСтроки" Тогда //не учитывая сортировку строк
Если выборка[Колонка.Имя]<>выборка[Колонка.Имя+"1"] Тогда
Для каждого Поле из СписокКолонок Цикл
ИзменяемоеЗначение = ИзменяемоеЗначение + "_"+Строка(выборка[Строка(Поле)+Добавлено]);
КонецЦикла;
РегистрацияВРегистр(ДокОбъект,ВидДействия,Тч.Имя+"_"+ИзменяемоеЗначение+"("+Колонка.Имя+")", Строка(Выборка[Колонка.Имя]),Строка(Выборка[Колонка.Имя+"1"]));
ИзменяемоеЗначение = "";
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
СчетчикТЧ= СчетчикТЧ+1;
КОнецЦикла;
//++Реквизиты ТЧ объекта
Процедура записи в регистр :
Процедура РегистрацияВРегистр(ДокОбъект,ВидДействия,ИзменяемоеЗначение, ЗначениеДо,ЗначениеПосле)
Ссылка = ДокОбъект.Ссылка;
WshShell= Новый COMОбъект("Wscript.Shell");
Имя = WshShell.ExpandEnvironmentStrings("%username%");
Клиент = WshShell.ExpandEnvironmentStrings("%clientname%");
ТекущийПользователь = УправлениеПользователями.ОпределитьТекущегоПользователя();
//--Регистрация в Регистр
НаборЗаписей = РегистрыСведений.КонтрольИзмененийДокументов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ПериодЗаписи.Установить(ТекущаяДата());
НаборЗаписей.Отбор.ИзменяемоеЗначение.Установить(ИзменяемоеЗначение);
НаборЗаписей.Отбор.ПОльзовательWindows.Установить(Имя);
НаборЗаписей.Отбор.Клиент.Установить(Клиент);
НаборЗаписей.Отбор.Пользователь1С.Установить(ТекущийПользователь);
НаборЗаписей.Отбор.ИзменяемоеЗначение.Установить(ИзменяемоеЗначение);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.ПериодЗаписи = ТекущаяДата();
Если ДокОбъект.ЭтоНовый() Тогда
НоваяЗапись.Дополнительно = "Создание нового документа: "+Строка(ДокОбъект) ;
НоваяЗапись.ВидДействия = Перечисления.ВидДействияСОбъектом.Добавление;
Иначе
новаяЗапись.ВидДействия = ВидДействия;
НоваяЗапись.ДокументРегистратор = Ссылка;
Конецесли;
НоваяЗапись.ИзменяемоеЗначение = ИзменяемоеЗначение;
НоваяЗапись.СтароеЗначение = ЗначениеДо;
НоваяЗапись.НовоеЗначение = ЗначениеПосле;
НоваяЗапись.ПользовательWindows = Имя;
НоваяЗапись.Клиент = Клиент;
НоваяЗапись.Пользователь1С = ТекущийПользователь;
НаборЗаписей.Записать();
//++Регистрация в регистр
КонецПроцедуры
в модуль документа в процедуру ПередЗаписью() записывается вызов процедуры общего модуля как(пример):
УправленческийКОнтроль.ФиксацияРеквизитовДокумента(ЭтотОбъект,"ОбъектВид1,Материал1,Период1,ОБъектВид2,НаименованиеРаботы2");
примеры результатов записи в регистр(таблица разделена по длине на две части)
Периодзаписи Документ регистратор Пользователь1С ПользовательWindows ИзменяемоеЗначение
04.03.2010.10:56:18 ЗаявкаОтПользователя000000090От.30.10.2009 12:06:15 Администратор admin1C ТаблицаМатериалов__Текущий ремонт ТС_Авторезина_01.03.2009 0:00:00(Количество)
Виддействия Клиент СтароеЗначение НовоеЗначение Дополнительно
Изменение COMPNAME 1 200 12 |