При активной работе с расширениями в платформе «1С:Предприятие 8.3» разработчики часто сталкиваются с неприятным ограничением: при попытке заимствовать регистр сведений из основной конфигурации ветка «Ресурсы» (а также «Измерения» и «Реквизиты») блокируется для добавления новых элементов. Кнопка «Добавить» просто отсутствует, и платформа не позволяет напрямую расширить структуру существующей таблицы.
В этой статье мы подробно разберем, почему платформа ведет себя именно так, проанализируем технические аспекты этого ограничения СУБД, а также рассмотрим три практических способа решения этой задачи, которые применяются в реальной практике разработки.
Давайте выясним причину, по которой разработчики платформы «1С» заблокировали возможность добавления собственных ресурсов в заимствованные регистры сведений. Всё дело в физической структуре хранения данных в СУБД (MS SQL Server, PostgreSQL и др.):
Ссылка). Это называется горизонтальным или вертикальным делением таблиц без потери уникальности.По этой причине разработчики «1С» жестко заблокировали изменение состава измерений и ресурсов заимствованных регистров. Тем не менее, решить задачу хранения дополнительных данных по существующим измерениям необходимо. Рассмотрим рабочие архитектурные паттерны.
Этот способ является наиболее чистым с точки зрения сохранения основной конфигурации на полной поддержке. Мы создадим в расширении собственный независимый регистр сведений, который будет хранить только наш дополнительный ресурс, а сопоставлять данные будем по измерениям исходного регистра.
Разберем по шагам, как реализовать этот паттерн:
Расш1_ДополнительныеСвойстваНоменклатуры). Обратите внимание: создавать его нужно как абсолютно новый объект расширения, а не заимствовать исходный.Номенклатура (с типом СправочникСсылка.Номенклатура) и Характеристика (с типом СправочникСсылка.ХарактеристикиНоменклатуры), мы создаем точно такие же измерения в нашем новом регистре в расширении.Расш1_ДополнительныйПараметр с требуемым типом данных).ПриЗаписи.Рассмотрим пример программного кода в расширении для синхронизации записи данных. Мы используем аннотацию &После для перехвата события записи основного регистра:
&После("ПриЗаписи")
Процедура Расш1_ПриЗаписи(Отказ, Замещение)
// Если запись инициирована с отказом, ничего не делаем
Если Отказ Тогда
Возврат;
КонецЕсли;
// Блокируем запись нашего параллельного регистра, чтобы избежать коллизий
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.Расш1_ДополнительныеСвойстваНоменклатуры");
// Устанавливаем пространство блокировки на основе структуры измерений текущего набора записей
// ...
// Создаем набор записей нашего нового регистра
НаборПараллельный = РегистрыСведений.Расш1_ДополнительныеСвойстваНоменклатуры.СоздатьНаборЗаписей();
// Переносим отборы из текущего (заимствованного) набора записей
Для Каждого ЭлементОтбора Из ЭтотОбъект.Отбор Цикл
ЗаполнитьЗначенияСвойств(НаборПараллельный.Отбор[ЭлементОтбора.Имя], ЭлементОтбора);
КонецЦикла;
// Читаем существующий набор, если необходимо обновить только конкретный ресурс
НаборПараллельный.Прочитать();
// Очищаем или заполняем новые записи на основании данных исходного набора
Если ЭтотОбъект.Количество() = 0 Тогда
// Если исходный набор очищен, очищаем и наш параллельный регистр
НаборПараллельный.Очистить();
Иначе
Для Каждого СтрокаОсновная Из ЭтотОбъект Цикл
// Ищем строку в параллельном наборе по ключевым измерениям
ПараметрыОтбора = Новый Структура("Номенклатура, Характеристика", СтрокаОсновная.Номенклатура, СтрокаОсновная.Характеристика);
НайденныеСтроки = НаборПараллельный.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
НоваяСтрока = НайденныеСтроки[0];
Иначе
НоваяСтрока = НаборПараллельный.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаОсновная);
КонецЕсли;
// Заполняем наш новый ресурс (значение можно получать из контекста или параметров формы)
// НоваяСтрока.Расш1_ДополнительныйПараметр = ...
КонецЦикла;
EndIf;
Попытка
НаборПараллельный.Записать(Истина);
Исключение
ЗаписьЖурналаРегистрации("Расширение.РегистрСведений", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Отказ = Истина;
КонецПопытки;
КонецПроцедуры
При чтении данных (например, при построении отчетов на СКД или динамических списков) мы просто связываем две таблицы левым соединением по ключевым полям-измерениям:
ВЫБРАТЬ
ОсновнойРегистр.Номенклатура КАК Номенклатура,
ОсновнойРегистр.Характеристика КАК Характеристика,
ОсновнойРегистр.ОсновнойРесурс КАК ОсновнойРесурс,
ЕСТЬNULL(ПараллельныйРегистр.Расш1_ДополнительныйПараметр, ЗНАЧЕНИЕ(Справочник.ЗначенияСвойствОбъектов.ПустаяСсылка)) КАК ДополнительныйРесурс
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ОсновнойРегистр
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Расш1_ДополнительныеСвойстваНоменклатуры КАК ПараллельныйРегистр
ПО ОсновнойРегистр.Номенклатура = ПараллельныйРегистр.Номенклатура
И ОсновнойРегистр.Характеристика = ПараллельныйРегистр.Характеристика
Если ваша конфигурация разработана на основе 1С:Библиотеки стандартных подсистем (БСП), то во многих случаях изменять структуру таблиц вообще не требуется. Для этого предназначен механизм «Дополнительные реквизиты и сведения».
Проанализируем этот вариант подробнее:
ДополнительныеСведения.Как справедливо заметили опытные разработчики в исходном обсуждении на форуме, чрезмерное увлечение расширениями для обхода базовых ограничений платформы часто приводит к «архитектурному маразму». Если система хранит огромные массивы данных в регистре сведений, постоянное левое соединение двух больших таблиц в запросах приведет к существенной потере производительности.
Рекомендуемый стандарт разработки (Best Practice) в сообществе 1С выглядит следующим образом:
Выбор конкретного решения всегда остается за архитектором системы. Если вам требуется быстрое решение «здесь и сейчас» без изменения основной конфигурации, используйте **Способ 1** (создание параллельного регистра в расширении). Если же база данных высоконагруженная и предполагается активная работа с регистром в тяжелых отчетах, правильнее использовать **Способ 3** и добавить ресурс в основную конфигурацию, вынеся всю логику его обработки в расширение.