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