Сравнение с образцом

Описываю полезную технологию, которая была применена на практике.

Мы разрабатывали загрузку документов из других баз и формирование по ним проводкам.

Правила были очень сложными и постоянно менялись.

Пользователи боялись, что если мы добавим что-то одно, то как бы не сломали другое.

На вооружение им был дан инструмент проверки, о котором ниже.

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

Через некоторое время мы сравниваем проводки нового загруженного документа со старым документом, и видим разницу, в разрезе до строк документа, т.к. каждая проводка содержит номер строки документа.

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

Пользователи видели, что изменилось в проводках документа со временем в виде отчета:

В отчет выводятся только те строки, в которых есть различия.

Отчет оптимизирован для просмотра в Excel, если значения идентичны, в ячейку выводится только одно значение. Если значения различные, то в ячейке выводится сначала значения из образца, а затем, с новой строки, но в той же ячейке - значение из нового документа (объекта). Ячейки с разными значениями выделяются розовым цветом.

В колонку ошибки выводятся имена колонок с ошибками, таким образом, с помощью автофильра Excel легко можно отобрать нужные коды ошибок.

С точки зрения интерфейса пользователя все сделано очень просто - кнопка "Сделать образцом" превращает документ в образец, выключая его проводки.

Кнопка "Сравнить образец" сравнивает текущий образец с документом-объектом.

Естественно, нужно предусмотреть некую связь между ними, чтобы по образцу мог быть найден исходный документ.

Нужно также предусмотреть некоторую защиту образцов, чтобы их случайно не провели или не отменили проведение.

Код для превращения документа в образец:

Если Объект.ЭтоОбразец Тогда

                Сообщить("Документ уже является образцом, на всякий случай отключим еще раз проводки!", СтатусСообщения.Важное);

Иначе

                Если ЛЕВ(Объект.ИдВнешний,8) <> "Образец_" Тогда

                               Объект.ИдВнешний = "Образец_" + Объект.ИдВнешний;    //Для поиска исходного объекта, достаточно произвольно

                КонецЕсли;

КонецЕсли;

//Выключаем проводки

НЗ = Объект.Движения.Основной;

НЗ.Прочитать();

НачатьТранзакцию();

Объект.ЭтоОбразец = истина;

Объект.Записать();

//Далее начинаем делать проводки неактивными

Для Каждого МЗ ИЗ НЗ Цикл

                МЗ.Активность = ложь;

КонецЦикла;

НЗ.Записать();

ЗафиксироватьТранзакцию();

Отчет можно было бы сделать на СКД (очень хорошо бы сыграло полное соединение по номеру строки), но делать было некогда, поэтому просто сделал на коленке.

Вот код:

Перем Таб, Макет, Колонки;

Процедура КнопкаВыполнитьНажатие(Кнопка)

                Работа();

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

Функция СравнитьСтроки(ПроводокСтроки, СтрокаОбъект, СтрокаОбразец)

                флОшибка = ложь;

                Область = Макет.ПолучитьОбласть("Строка");

                Ошибка = "";

               

                Если СтрокаОбъект = Неопределено Тогда

                               Ошибка = Ошибка + "ПустойОбъект"+";";

                КонецЕсли;

                Если СтрокаОбразец = Неопределено Тогда

                               Ошибка = Ошибка + "ПустойОбразец"+";";

                КонецЕсли;

 

                Для Каждого Колонка ИЗ Колонки Цикл

                               флОшибкаКолонки = ложь;

                               Если СтрокаОбъект = Неопределено Тогда

                                               ЗнОбъект = "-";

                                               флОшибкаКолонки = истина;

                               Иначе

                                               ЗнОбъект = СтрокаОбъект[Колонка];

                               КонецЕсли;

                               Если СтрокаОбразец = Неопределено Тогда

                                               ЗнОбразец = "-";

                                               флОшибкаКолонки = истина;

                               Иначе

                                               ЗнОбразец = СтрокаОбразец[Колонка];

                               КонецЕсли;

                              

                               Если ЗнОбъект = ЗнОбразец Тогда

                                               Зн = ЗнОбъект;

                               Иначе

                                               Зн = "" + ЗнОбразец + Символы.ПС + ЗнОбъект;

                                               флОшибкаКолонки = истина;

                               КонецЕсли;

                              

                               Область.Параметры[Колонка] = Зн;

                              

                               Если флОшибкаКолонки Тогда

                                               ИскКолонка = Область.НайтиТекст(Колонка,,,,истина).Лево; //Ячейку целиком

                                               Область.Область(1, ИскКолонка, 1, ИскКолонка).ЦветФона = WebЦвета.Розовый;

                                               Ошибка = Ошибка + Колонка+";";

                               Иначе

                                               //Область.Область(1, ИскКолонка, 1, ИскКолонка).ЦветФона = WebЦвета.Белый;

                               КонецЕсли;

                               флОшибка = флОшибка ИЛИ флОшибкаКолонки

                КонецЦикла;

                //Область.Параметры.НомерСтрокиДокумента = НомерСтрокиДокумента;

                Область.Параметры.Ошибка = Ошибка;

               

               

                Если флОшибка Тогда

                               Таб.Вывести(Область);

                КонецЕсли;

 

КонецФункции              

Функция Работа() Экспорт      

                Колонки = Новый Массив();

                Колонки.Добавить("НомерСтрокиДокумента");

                Колонки.Добавить("Период");

                Колонки.Добавить("Сумма");

                Колонки.Добавить("СчетДт");

                Колонки.Добавить("СчетКт");

                Колонки.Добавить("ВалютаДт");

                Колонки.Добавить("ВалютаКт");

                Колонки.Добавить("ВалютнаяСуммаДт");

                Колонки.Добавить("ВалютнаяСуммаКт");

                Колонки.Добавить("КоличествоДт");

                Колонки.Добавить("КоличествоКт");

                Колонки.Добавить("СубконтоДт1");

                Колонки.Добавить("СубконтоКт1");

                Колонки.Добавить("СубконтоДт2");

                Колонки.Добавить("СубконтоКт2");

                Колонки.Добавить("СубконтоДт3");

                Колонки.Добавить("СубконтоКт3");

                Колонки.Добавить("Подразделение");

               

                Макет = ПолучитьМакет("Макет");

                Таб = ЭлементыФормы.ПолеТабличногоДокумента;

                Таб.Очистить();

                Область = Макет.ПолучитьОбласть("Шапка");

                Таб.Вывести(область);

               

                ТЧОбъект = Объект.ПолучитьОбъект().Движения.Основной;

                ТЧОбъект.Прочитать();

                ТЧОбъект = ТЧОбъект.Выгрузить();

                ТЧОбъект.Сортировать("НомерСтрокиДокумента");

                ТЧОбразец = Образец.ПолучитьОбъект().Движения.Основной;

                ТЧОбразец.Прочитать();

                ТЧОбразец = ТЧОбразец.Выгрузить();

                ТЧОбразец.Сортировать("НомерСтрокиДокумента");

               

                ИндОбъект = 0;

                ИндОбразец = 0;

                ПроводокСтроки = 0;

                ПредНомерСтрокиДокумента = 0;

               

                Сч = 0;

               

                Пока истина Цикл

                              

                               Если Сч % 10 = 0 Тогда

                                               Состояние("Сравнение: " + Сч);

                                               ОбработкаПрерыванияПользователя();

                               КонецЕсли;

                               Сч = Сч + 1;

                              

                               Если ИндОбъект >= ТЧОбъект.Количество() Тогда

                                               СтрокаОбъект = Неопределено;

                               Иначе

                                               СтрокаОбъект = ТЧОбъект[ИндОбъект];

                                КонецЕсли;

                              

                               Если ИндОбразец >= ТЧОбразец.Количество() Тогда

                                               СтрокаОбразец = Неопределено;

                               Иначе

                                               СтрокаОбразец = ТЧОбразец[ИндОбразец];

                               КонецЕсли;

                              

                               //Если строки закончились

                               Если СтрокаОбъект = Неопределено И СтрокаОбразец = Неопределено Тогда

                                               Прервать;

                               КонецЕсли;

                              

                               Если СтрокаОбразец = Неопределено ИЛИ СтрокаОбъект <> Неопределено И СтрокаОбъект.НомерСтрокиДокумента < СтрокаОбразец.НомерСтрокиДокумента Тогда

                                               СравнитьСтроки(ПроводокСтроки, СтрокаОбъект, Неопределено);

                                               ИндОбъект = ИндОбъект + 1;

                               ИначеЕсли СтрокаОбъект = Неопределено  ИЛИ СтрокаОбразец <> Неопределено И СтрокаОбъект.НомерСтрокиДокумента > СтрокаОбразец.НомерСтрокиДокумента Тогда

                                               СравнитьСтроки(ПроводокСтроки, Неопределено, СтрокаОбразец);

                                               ИндОбразец = ИндОбразец + 1;

                               Иначе // СтрокаОбъект.НомерСтрокиДокумента = СтрокаОбразец.НомерСтрокиДокумента

                                               Если СтрокаОбъект.НомерСтрокиДокумента <> СтрокаОбразец.НомерСтрокиДокумента Тогда

                                                               Сообщить("Рассинхронизация номеров строк: " + СтрокаОбъект.НомерСтрокиДокумента + " и " + СтрокаОбразец.НомерСтрокиДокумента, СтатусСообщения.Важное);

                                               КонецЕсли;

                                               ПроводокСтроки = ПроводокСтроки + 1;

                                               СравнитьСтроки(ПроводокСтроки, СтрокаОбъект, СтрокаОбразец);

                                               ИндОбъект = ИндОбъект + 1;

                                               ИндОбразец = ИндОбразец + 1;

                               КонецЕсли;

                              

                КонецЦикла;

КонецФункции

В коде есть интересный момент - каким образом ищется область ячейки для раскраски розовым цветом.

Ну и сам код по параллельному продвижению по двум отсортированным спискам тоже может много где пригодиться.

Вот собственно, и вся технология! Удачи!