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