Иногда я слышал, что 1С – это не язык программирования, а просто язык для написания скриптов. На самом деле это не так. Хороший разработчик 1С должен знать не только Basic-подобный язык программирования, но также знать SQL и принципы построения баз данных, для того, чтобы правильно конструировать прикладные объекты для хранения данных.
В некоторых случаях решения получаются настолько изящными и красивыми, что грех о них не рассказать.
У меня было два регистра сведений. Один содержал текущие записи, другой – историю их изменений. Обычно записи вносились из интерфейса массированно по 1000-2000 элементов. Соответственно, во втором регистре было дополнительное измерение «Документ», ссылка на документ-протокола, который создавался при каждом таком изменении.
Возникла задача – не только видеть изменения, но и восстанавливать их.
В обычном регистре аналогичное поле «Документ» размещалось в ресурсах и показывало, какой последний документ установил значение записей.
В форму документа-протокола я добавил два табличных поля, ссылающиеся на списки записей первого и второго регистров, и при открытии делал отбор по документу протокола:
ЭлементыФормы.ТабличноеПоле1.Значение.Отбор.Документ.Значение = Ссылка;
ЭлементыФормы.ТабличноеПоле1.Значение.Отбор.Документ.Использование = Истина;
ЭлементыФормы.ТабличноеПоле2.Значение.Отбор.Документ.Значение = Ссылка;
ЭлементыФормы.ТабличноеПоле2.Значение.Отбор.Документ.Использование = Истина;
В форме списка истории изменений можно выделить нужные для восстановления записи (причем используя сложные отборы). После этого нужно нажать восстановить и начинается процесс восстановления.
МассивСтрокДляВосстановления = ЭлементыФормы.ТабличноеПоле2.ВыделенныеСтроки;
Если МассивСтрокДляВосстановления.Количество() = 0 Тогда
Предупреждение("Не выделены строки, восстанавливать нечего!");
КонецЕсли;
//создадим новый документ протокола
НовыйДок = Документы.спПротоколУстановкиБонусов.СоздатьДокумент();
НовыйДок.Категории = "Все";
НовыйДок.Автор = ПараметрыСеанса.ТекущийПользователь;
НовыйДок.Дата = ТекущаяДата();
НовыйДок.ПричинаИзменения = Перечисления.спПричиныИзмененияБонусов.Восстановление;
НовыйДок.Записать(РежимЗаписиДокумента.Запись);
//заполним новый набор бонусов свойствами из регистра протоколирования
Всего = МассивСтрокДляВосстановления.Количество();
Сч = 0;
МЗ = РегистрыСведений.спИзмененияБонусов.СоздатьМенеджерЗаписи();
Для Каждого СтрокаВосстановления ИЗ МассивСтрокДляВосстановления Цикл
Сч = Сч + 1;
Если Сч % 10 = 0 Тогда
Состояние("Восстановлено " + Сч + " из " + Всего);
ОбработкаПрерыванияПользователя();
КОнецЕсли;
НЗДляЗаписи = РегистрыСведений.спБонусы.СоздатьНаборЗаписей();
//Считываем из базы значение записи
ЗаполнитьЗначенияСвойств(МЗ, СтрокаВосстановления); //В форме должны быть все поля!!!
МЗ.Прочитать(); //Считываем историю
МЗДляЗаписи = НЗДляЗаписи.Добавить();
ЗаполнитьЗначенияСвойств(МЗДляЗаписи, МЗ);
МЗДляЗаписи.Документ = НовыйДок.Ссылка;
УстановитьОтборНабораЗаписейПоИзмерениям(НЗДляЗаписи, МЗДляЗаписи); //Чтобы записать только одну запись
НЗДляЗаписи.ДополнительныеСвойства.Вставить("ДокументПротокола", НовыйДок.Ссылка);
НЗДляЗаписи.Записать();
КонецЦикла;
Интересности были связаны со следующими моментами:
Ну и «волшебная» функция, которая делает нужные отборы:
Функция УстановитьОтборНабораЗаписейПоИзмерениям(НЗ, Структура) Экспорт
//Структура - структура или менеджер набора записей
МД = НЗ.Метаданные();
МассивПолей = Новый Массив();
Для Каждого МДИзмерение ИЗ МД.Измерения Цикл
ИмяИзмерения = МДИзмерение.Имя;
МассивПолей.Добавить(ИмяИзмерения);
КонецЦикла;
//Как быть с регистратором, не знаю, пока отложил...
Если МД.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
МассивПолей.Добавить("Период");
КонецЕсли;
Для Каждого ИмяИзмерения ИЗ МассивПолей Цикл
Если ТипЗнч(Структура) = Тип("Структура") Тогда
Если Структура.Свойство(ИмяИзмерения) Тогда
ИспользуетсяОтбор = истина;
Иначе
ИспользуетсяОтбор = ложь;
КонецЕсли;
Иначе
ИспользуетсяОтбор = истина;
КонецЕсли;
ТекОтбор = НЗ.Отбор[ИмяИзмерения];
Если ИспользуетсяОтбор Тогда
ТекОтбор.ВидСравнения = ВидСравнения.Равно;
ТекОтбор.Значение = Структура[ИмяИзмерения];
КонецЕсли;
ТекОтбор.Использование = ИспользуетсяОтбор;
КонецЦикла;
КонецФункции