При кастомизации типовых конфигураций «1С:Предприятие» разработчики регулярно сталкиваются с необходимостью расширения состава данных в табличных частях (ТЧ) документов или справочников. Начиная с платформы 8.3, для этих целей активно применяются расширения конфигурации. Однако этот простой, на первый взгляд, шаг таит в себе множество подводных камней: от риска полной потери данных при случайном удалении расширения и сложностей с правами доступа до проблем при построении отчетов в СКД.
В этой статье мы подробно проанализируем существующие подходы к решению этой задачи, взвесим все плюсы и минусы каждого метода, разберем особенности поведения платформы 1С 8.3 разных версий (включая современные релизы 8.3.24 и 8.3.25) и пошагово реализуем наиболее надежный архитектурный паттерн связи ТЧ с независимым регистром сведений.
---
Этот способ кажется наиболее очевидным и простым. Мы заимствуем объект (например, документ РеализацияТоваровУслуг) в расширение, добавляем в его табличную часть Товары новый реквизит МойДополнительныйРеквизит и выводим его на форму.
Рассмотрим, с какими сложностями мы гарантированно столкнемся при таком подходе, и как их правильно преодолеть.
Правило безопасности: Если вам необходимо временно отключить логику расширения, ни в коем случае не удаляйте его. Просто снимите флаг «Активно» в списке расширений. В этом случае таблицы в СУБД останутся нетронутыми, а данные сохранятся.
Для решения этой проблемы в платформе (начиная с версии 8.3.14) у заимствованных ролей предусмотрено свойство «Устанавливать права для реквизитов и табличных частей по умолчанию». Обязательно активируйте этот флаг для ролей расширения, чтобы не прописывать права вручную на каждый чих.
Также помните: если ваша конфигурация построена на БСП (Библиотека Стандартных Подсистем), после добавления новых ролей в расширение необходимо обновить вспомогательные данные БСП. Запустите базу в режиме Предприятия с параметром /C СтартЗаполненияОбщихСпособовВыделенияДанных или выполните в консоли кода метод:
Справочники.ИдентификаторыОбъектовМетаданных.ОбновитьДанные()
.erf), запускаемые пользователями, не смогут напрямую обратиться к этому полю в текстах запросов СКД — они его просто «не видят» из внешнего контекста.
Чтобы отчеты работали корректно, сам отчет должен быть спроектирован и встроен непосредственно внутрь этого же расширения конфигурации. Если отчет обязательно должен оставаться внешним, вам придется отказаться от прямых запросов к этой табличной части и передавать данные в СКД программно через Таблицу Значений (Набор данных - Объект).
---
Многие разработчики сознательно отказываются от хранения данных в расширениях и предпочитают добавлять реквизиты непосредственно в основную конфигурацию, а через расширение осуществлять исключительно визуализацию (вывод на управляемую форму) и прописывать логику обработки.
Преимущества:
Недостатки:
---
Проанализируем наиболее изящный и профессиональный метод, который позволяет полностью сохранить типовую конфигурацию «на замке», не перегружать таблицы расширений физическими реквизитами ТЧ и гарантировать сохранность данных. Этот подход активно применяется самой фирмой «1С» в крупных тиражных решениях (например, в «1С:Зарплата и управление персоналом 3» для связи показателей расчета со строками документов). При проектировании подобных механизмов для работы с мастер-таблицами полезно использовать методику связывания главной и подчиненной таблиц на управляемых формах.
Суть метода заключается в том, что все дополнительные данные строк табличной части хранятся в отдельном независимом регистре сведений, а связующим звеном выступает уникальный идентификатор строки.
Почему нельзя связывать данные по обычному «Номеру строки»?
Если вы свяжете данные регистра со строкой ТЧ по индексу или номеру строки, то любая интерактивная манипуляция пользователя (сортировка таблицы, удаление строки из середины или добавление строки вверх списка) полностью разрушит эту связь. Данные регистра начнут отображаться совсем в других строках.
Разберем по шагам, как правильно реализовать данный паттерн.
Создадим в расширении независимый регистр сведений, например, ДополнительныеРеквизитыТЧ.
Определим следующие реквизиты регистра:
СсылкаНаОбъект (Тип: ЛюбаяСсылка на документ или справочник, в котором расширяется ТЧ). Установите свойство «Ведущее».КлючСвязиСтроки (Тип: УникальныйИдентификатор или Число(15)).МойДополнительныйРеквизит (Нужный вам тип данных, который вы хотели добавить в ТЧ).Заимствуем в расширение целевую табличную часть объекта. Если в типовой конфигурации в ТЧ уже предусмотрен стандартный реквизит связи (например, в БСП часто есть реквизит КлючСвязи или ИдентификаторСтроки), мы будем использовать его. Если такого реквизита нет, то мы добавим в ТЧ через расширение всего один служебный реквизит КлючСвязиСтроки с типом УникальныйИдентификатор. Это минимальное и безопасное вмешательство в структуру объекта.
Заимствуем форму документа в расширение и переходим в ее модуль. Нам необходимо решить три задачи: заполнить программное поле на форме при открытии, сгенерировать уникальные ключи для новых строк и сохранить данные в регистр при записи документа.
Рассмотрим пример кода реализации этих этапов.
&НаСервере
Процедура Расш_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Шаг 1. Считаем данные из нашего регистра сведений в один обход СУБД
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДопДанные.КлючСвязиСтроки КАК КлючСвязиСтроки,
| ДопДанные.МойДополнительныйРеквизит КАК МойДополнительныйРеквизит
|ИЗ
| РегистрСведений.ДополнительныеРеквизитыТЧ КАК ДопДанные
|ГДЕ
| ДопДанные.СсылкаНаОбъект = &СсылкаНаОбъект";
Запрос.УстановитьПараметр("СсылкаНаОбъект", Объект.Ссылка);
ТаблицаДопДанных = Запрос.Выполнить().Выгрузить();
// Шаг 2. Заполним значения в табличной части формы
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
// Если у существующей строки почему-то пустой ключ — сгенерируем его
Если Не ЗначениеЗаполнено(СтрокаТЧ.КлючСвязиСтроки) Тогда
СтрокаТЧ.КлючСвязиСтроки = Новый УникальныйИдентификатор;
КонецЕсли;
НайденнаяСтрока = ТаблицаДопДанных.Найти(СтрокаТЧ.КлючСвязиСтроки, "КлючСвязиСтроки");
Если НайденнаяСтрока <> Неопределено Тогда
// Записываем данные во временную колонку на форме
СтрокаТЧ.МойДополнительныйРеквизит = НайденнаяСтрока.МойДополнительныйРеквизит;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Теперь нам нужно гарантировать, что при добавлении пользователем новой строки в табличную часть (или при копировании строки) у нее автоматически заполнится уникальный идентификатор связи.
&НаКлиенте
Процедура Расш_ТоварыПриИзмененииПосле(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// Проверяем заполненность ключа связи, генерируем новый при отсутствии
Если Не ЗначениеЗаполнено(ТекущиеДанные.КлючСвязиСтроки) Тогда
ТекущиеДанные.КлючСвязиСтроки = Новый УникальныйИдентификатор;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Последний важный этап — сохранение данных из формы обратно в базу данных при записи документа. Мы будем использовать обработчик записи на сервере.
&НаСервере
Процедура Расш_ПриЗаписиНаСервереПосле(ТекущийОбъект, ПараметрыЗаписи)
// Инициализируем набор записей по нашему объекту-владельцу
НаборЗаписей = РегистрыСведений.ДополнительныеРеквизитыТЧ.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.СсылкаНаОбъект.Установить(ТекущийОбъект.Ссылка);
// Заполняем набор актуальными данными из табличной части
Для Каждого СтрокаТЧ Из ТекущийОбъект.Товары Цикл
// Записываем только те строки, где наш доп. реквизит действительно заполнен
Если ЗначениеЗаполнено(СтрокаТЧ.МойДополнительныйРеквизит) Тогда
// Если ключ связи по какой-то причине пуст — генерируем его перед записью
Если Не ЗначениеЗаполнено(СтрокаТЧ.КлючСвязиСтроки) Тогда
СтрокаТЧ.КлючСвязиСтроки = Новый УникальныйИдентификатор;
КонецЕсли;
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.СсылкаНаОбъект = ТекущийОбъект.Ссылка;
НоваяЗапись.КлючСвязиСтроки = СтрокаТЧ.КлючСвязиСтроки;
НоваяЗапись.МойДополнительныйРеквизит = СтрокаТЧ.МойДополнительныйРеквизит;
КонецЕсли;
КонецЦикла;
// Записываем набор данных с замещением старых записей
НаборЗаписей.Записать(Истина);
КонецПроцедуры
Почему этот подход считается эталонным?
Во-первых, вы не перегружаете основную конфигурацию и не снимаете ее с поддержки. Во-вторых, даже если ваше расширение будет случайно удалено, физические таблицы регистра сведений СУБД останутся в базе данных, и вы сможете легко восстановить информацию. В-третьих, этот метод гарантирует 100% точность сопоставления данных при любых манипуляциях пользователя со строками на клиенте (сортировка, перемещение вверх/вниз).
---
Для того чтобы вам было проще сделать выбор в реальном проекте, проанализируем сравнительную таблицу всех трех вариантов решения задачи:
| Критерий оценки | Добавление напрямую в ТЧ расширения | Добавление в ТЧ основной конфигурации | Связь через Регистр Сведений и UUID |
|---|---|---|---|
| Безопасность данных при удалении расширения | Крайне низкая (данные удаляются навсегда) | Абсолютная (данные хранятся в основной БД) | Высокая (таблицы регистра сохраняются в БД) |
| Сложность разработки | Низкая (простое добавление реквизита) | Низкая (простое добавление реквизита) | Средняя (требуется написание кода в форме) |
| Влияние на обновление (поддержку) | Отсутствует (конфигурация на полной поддержке) | Среднее (объект снимается с полной поддержки) | Отсутствует (конфигурация на полной поддержке) |
| Работа с СКД и внешними отчетами | Ограниченная (требуются обходные пути) | Полная поддержка «из коробки» | Полная поддержка (через левое соединение с РС) |
| Производительность СУБД | Высокая (одна физическая таблица) | Высокая (одна физическая таблица) | Средняя (требуется соединение таблиц в запросе) |
Подводя итог, можно дать следующую рекомендацию: если перед вами стоит задача быстрого внедрения небольшого прототипа, а база регулярно бэкапится — прямое добавление реквизита в табличную часть расширения вполне жизнеспособно (не забывайте только про настройку прав). Однако для крупных корпоративных систем со строгими требованиями к обновлению и сохранности данных однозначным выбором является архитектурный паттерн с использованием независимого регистра сведений и уникальных идентификаторов строк. Для дополнительной защиты информации при таких доработках можно автоматизировать регулярное архивирование и тестирование баз.