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