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