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