Перепроведение изменённых документов. Часть II.

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

П ервая статья принесла один отклик, однако он оказался настолько концептуальным, что пришлось сесть за написание второй части. У новой технологии нет ни одного из недостатков, упомянутых в первой статье. Кроме того, описанную идею можно использовать и в других целях. Например, для организации детального журналирования изменений БД, совершенных пользователем.

автор статьи: Фёдор Езеев
fe@alterplast.ru
опубликовано: апрель 2003

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

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

Однако, уважаемый Mx@mail.ru и здесь проявил могучий разум, он предложил еще одну здравую мысль (просто результат он знал заранее ;-). Мысль такая: в момент записи у нас есть один документ в двух состояниях, которые можно довольно просто сравнить. Первое состояние – Контекст документа. Это то, что получилось после внесения изменений. Второе состояние –- записанный документ в базе данных.

Теория кончилась – берем в руки клавиатуру и пишем код. Начнем с анализа реквизитов шапки.

//Берем документ из БД
Д=СоздатьОбъект("Документ");
Д.НайтиДокумент(ТекущийДокумент());

МетаД=Метаданные.Документ(Вид());

//Определим, изменился реквизит, или нет
Для С=1 По МетаД.РеквизитШапки() Цикл
   РеквИД=МетаД.РеквизитШапки(С).Идентификатор;
   СтароеЗначение=Д.ПолучитьАтрибут(РеквИД);
   НовоеЗначение=ПолучитьАтрибут(РеквИД);
   Если СтароеЗначение<>НовоеЗначение Тогда
      Сообщить("Изменился реквизит шапки "+РеквИД);
      Сообщить("Старое значение: "+СтароеЗначение);
      Сообщить("Новое значение: "+НовоеЗначение);
      Сообщить("===============================");
   КонецЕсли;
КонецЦикла;

Теперь я хочу понять, насколько критичны сделанные изменения. И сообщить только о критичных.

// В список значений занесем идентификаторы тех реквизитов,
// изменение которых нас не волнует


Косметика=СоздатьОбъект("СписокЗначений");
Косметика.ДобавитьЗначение("Комментарий");
Косметика.ДобавитьЗначение("Автор");

Д=СоздатьОбъект("Документ");
Д.НайтиДокумент(ТекущийДокумент());

МетаД=Метаданные.Документ(Вид());

Для С=1 По МетаД.РеквизитШапки() Цикл
   РеквИД=МетаД.РеквизитШапки(С).Идентификатор;
   Если Косметика.НайтиЗначение(РеквИД)>0 Тогда
      Продолжить;
   КонецЕсли;

   СтароеЗначение=Д.ПолучитьАтрибут(РеквИД);
   НовоеЗначение=ПолучитьАтрибут(РеквИД);
   Если СтароеЗначениеНовоеЗначение Тогда
      Сообщить("Изменился реквизит шапки "+РеквИД);
      Сообщить("Старое значение: "+СтароеЗначение);
      Сообщить("Новое значение: "+НовоеЗначение);
      Сообщить("===============================");
   КонецЕсли;
КонецЦикла;

Ну и в заключение на основании сделанного анализа примем нужное нам решение.

Процедура ПриЗаписи()

Если Выбран()=0 Тогда
Возврат;
КонецЕсли;

Косметика=СоздатьОбъект("СписокЗначений");
Косметика.ДобавитьЗначение("Комментарий");
Косметика.ДобавитьЗначение("Автор");
ИзменениеКритично=0;

Д=СоздатьОбъект("Документ");
Д.НайтиДокумент(ТекущийДокумент());
МетаД=Метаданные.Документ(Вид());

Для С=1 По МетаД.РеквизитШапки() Цикл
   РеквИД=МетаД.РеквизитШапки(С).Идентификатор;
   Если Косметика.НайтиЗначение(РеквИД)>0 Тогда
      Продолжить;
   КонецЕсли;
   СтароеЗначение=Д.ПолучитьАтрибут(РеквИД);
   НовоеЗначение=ПолучитьАтрибут(РеквИД);
   Если СтароеЗначение<>НовоеЗначение Тогда
      ИзменениеКритично=1;
      //Одного критичного изменения вполне достаточно
      Возврат;

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

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


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

Общая картина ясна. Осталось только дописать анализ общих реквизитов и табличной части документа. Проблема может возникнуть разве что со сравнением табличной части. Тут главное – не забыть о том, что кроме изменения строк бывает еще и удаление и добавление.

Кусочек, сравнивающий строки, будет выглядеть примерно так (предполагаем, что любое изменение табличной части требует перепроведения):

Если КоличествоСтрок()<>Д.КоличествоСтрок() Тогда
   ИзменениеКритично=1;
   Возврат;
Иначе
   ВыбратьСтроки();
   Пока ПолучитьСтроку()=1 Цикл
      Д.ПолучитьСтрокуПоНомеру(НомерСтроки);
      Для С=1 По МетаД.РеквизитТабличнойЧасти() Цикл
         РеквИД=МетаД.РеквизитТабличнойЧасти(С).Идентификатор;
         СтароеЗначение=Д.ПолучитьАтрибут(РеквИД);
         НовоеЗначение=ПолучитьАтрибут(РеквИД);
         Если СтароеЗначение<>НовоеЗначение Тогда
            ИзменениеКритично=1;
            Возврат;
         КонецЕсли;
      КонецЦикла;
   КонецЦикла;
КонецЕсли;

Преимуществами этого метода можно признать:

  1. Прозрачность для пользователя.
  2. Размещение кода, ответственного за одну задачу в одном месте.
  3. Отсутствие "каскада записей" при изменении сразу нескольких "неважных" реквизитов.
Ну и еще раз хочется упомянуть, что данную методику можно с успехом применять для детального журналирования действий пользователя.

Начать дискуссию