При разработке конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача консолидации данных. Типичный пример: справочник РабочиеСтанции должен содержать актуальный список оборудования, которое разнесено по разным узкоспециализированным справочникам (например, ВнешнееОборудование, СиловоеОборудование). Главная сложность здесь заключается не только в сборе данных, но и в обеспечении их уникальности, чтобы при каждом сохранении объекта строки в табличной части не множились бесконечно. Разберем по шагам, как элегантно и эффективно решить эту задачу.
Рассмотрим стандартный подход через модуль объекта. Чтобы избежать простого добавления строк (которое и ведет к дублированию), нам необходимо реализовать алгоритм «сопоставления». Вместо того чтобы просто очищать таблицу и записывать её заново (что может привести к потере ссылок на строки ТЧ, если они используются где-то еще), мы проанализируем текущий состав и внесем только необходимые изменения.
Проанализируем логику работы в процедуре ПередЗаписью. Для начала нам нужно собрать актуальные данные из всех источников, используя реквизит Name (ссылка на текущую рабочую станцию) в качестве фильтра. Для оптимизации лучше использовать один запрос к нескольким таблицам.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если объект новый, то ссылки еще нет, заполнение может не иметь смысла
Если ЭтоНовый() Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВнешнееОборудование.Назначение КАК Назначение,
| ВнешнееОборудование.ТипОборудования КАК ТипОборудования,
| ВнешнееОборудование.SN КАК SN,
| ВнешнееОборудование.Ссылка КАК ОбъектИсточник
|ИЗ
| Справочник.ВнешнееОборудование КАК ВнешнееОборудование
|ГДЕ
| ВнешнееОборудование.Name = &ТекущаяСтанция
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| СиловоеОборудование.Назначение,
| СиловоеОборудование.ТипОборудования,
| СиловоеОборудование.SN,
| СиловоеОборудование.Ссылка
|ИЗ
| Справочник.СиловоеОборудование КАК СиловоеОборудование
|ГДЕ
| СиловоеОборудование.Name = &ТекущаяСтанция";
Запрос.УстановитьПараметр("ТекущаяСтанция", Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
АктуальныеДанные = РезультатЗапроса.Выгрузить();
СинхронизироватьТабличнуюЧасть(АктуальныеДанные);
КонецЕсли;
КонецПроцедуры
В этом примере мы используем оператор ОБЪЕДИНИТЬ ВСЕ, что позволяет получить данные из пяти и более справочников за один вызов сервера. Обратите внимание на поле ОбъектИсточник — это ключевой момент для предотвращения дублей.
Выясним причину, по которой обычное сравнение по полям «Назначение» или «Серийный номер» может давать сбои. Данные в справочниках-источниках могут меняться, и только ссылка на конкретный элемент (объект-источник) гарантирует нам точность сопоставления. Рекомендуем добавить в табличную часть УстановленноеОборудование скрытый реквизит ОбъектИсточник с типом ЛюбаяСсылка.
Разберем процедуру СинхронизироватьТабличнуюЧасть, которая будет сравнивать то, что уже есть в ТЧ, с тем, что мы получили из запроса:
Процедура СинхронизироватьТабличнуюЧасть(АктуальныеДанные)
// 1. Удаляем строки, которых больше нет в источниках
Индекс = УстановленноеОборудование.Количество() - 1;
Пока Индекс >= 0 Цикл
СтрокаТЧ = УстановленноеОборудование[Индекс];
Если АктуальныеДанные.Найти(СтрокаТЧ.ОбъектИсточник, "ОбъектИсточник") = Неопределено Тогда
УстановленноеОборудование.Удалить(СтрокаТЧ);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// 2. Обновляем существующие и добавляем новые
Для Каждого СтрокаИсточник Из АктуальныеДанные Цикл
ПараметрыПоиска = Новый Структура("ОбъектИсточник", СтрокаИсточник.ОбъектИсточник);
НайденныеСтроки = УстановленноеОборудование.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
// Обновляем реквизиты, если они изменились в источнике
ЗаполнитьЗначенияСвойств(НайденныеСтроки[0], СтрокаИсточник);
Иначе
// Добавляем новую строку
НоваяСтрока = УстановленноеОборудование.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточник);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Такой подход полностью исключает дублирование, так как система всегда знает, какая строка ТЧ соответствует конкретному прибору в базе. Метод ЗаполнитьЗначенияСвойств значительно сокращает код, автоматически копируя данные из колонок ТЗ в колонки ТЧ с одинаковыми именами.
Проанализируем ситуацию с точки зрения производительности. Если справочников оборудования много, а количество записей в табличных частях исчисляется сотнями, постоянная перезапись объекта РабочиеСтанции может замедлить систему. В профессиональной среде для таких задач часто используют Регистры сведений.
Вместо хранения данных в табличной части справочника, мы создаем непериодический регистр сведений СоставРабочихСтанций с измерениями:
В этом случае уникальность записей контролируется самой платформой 1С на уровне ключа регистра. На форму справочника РабочиеСтанции мы просто выносим Динамический список с отбором по текущей ссылке. Это избавляет нас от необходимости писать сложный код синхронизации в ПередЗаписью.
Посмотрим на проблему со стороны пользователя. Если мы заполняем ТЧ только в ПередЗаписью, пользователь не увидит изменений, пока не нажмет кнопку «Записать». Это может сбивать с толку. Рассмотрим вариант создания кнопки «Обновить состав» на форме.
Алгоритм работы кнопки будет идентичен приведенному выше, но вызываться он будет в контексте формы. Также хорошей практикой считается установка свойства Только просмотр для такой табличной части. Поскольку данные подтягиваются автоматически из других справочников, ручное редактирование пользователем может нарушить целостность данных — процедура записи всё равно затрет ручные правки актуальными данными из источников.
Важные моменты для запоминания:
Объединить Все в запросах к нескольким справочникам для минимизации обращений к серверу.ЗаполнитьЗначенияСвойств для синхронизации реквизитов.Таким образом, мы обеспечили надежную систему автоматического заполнения, которая сохраняет уникальность данных и легко масштабируется при добавлении новых типов оборудования в систему.