ОписаниеТипов, ИзменитьРеквизиты и ОграничениеТипа: управляем типом реквизита из кода 1С

· 9 мин

Содержание

Программно тип реквизита в 1С задают объектом ОписаниеТипов: Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)). Этот объект передают в Новый РеквизитФормы(...) и ИзменитьРеквизиты(), в колонку таблицы значений, в свойство ТипЗначения плана видов характеристик или в ОграничениеТипа поля формы. Тип реквизита объекта метаданных (справочника, документа) из кода в режиме Предприятия не меняется: это делают в Конфигураторе.

Всё ниже относится к платформе 8.3 и управляемым формам. Код одинаково работает в любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 или самописной. Имена справочников в примерах условные, подставьте свои.

Чем задают тип в 1С: ОписаниеТипов и квалификаторы

Тип в языке 1С — это значение Тип, например Тип("СправочникСсылка.Контрагенты"). Но реквизиту нужен не один тип, а описание: какие типы допустимы и с какими ограничениями. За это отвечает ОписаниеТипов.

Основная форма конструктора: Новый ОписаниеТипов(Типы, КвалификаторыЧисла, КвалификаторыСтроки, КвалификаторыДаты). Типы передают строкой через запятую или массивом значений Тип. Пропущенные параметры оставляют пустыми между запятыми.

// Строка до 150 символов, переменной длины
ТипСтрока = Новый ОписаниеТипов("Строка", ,
    Новый КвалификаторыСтроки(150, ДопустимаяДлина.Переменная));

// Число 15.2, только неотрицательное
ТипСумма = Новый ОписаниеТипов("Число",
    Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный));

// Дата без времени
ТипДата = Новый ОписаниеТипов("Дата", , ,
    Новый КвалификаторыДаты(ЧастиДаты.Дата));

// Ссылка на справочник
ТипКонтрагент = Новый ОписаниеТипов("СправочникСсылка.Контрагенты");

// Булево
ТипФлаг = Новый ОписаниеТипов("Булево");

Квалификаторы повторяют то, что в Конфигураторе задают в свойстве «Тип»:

КвалификаторПараметрыПример
КвалификаторыСтрокидлина, ДопустимаяДлина(50) — строка до 50 символов
КвалификаторыЧисларазрядность, дробная часть, ДопустимыйЗнак(10, 3) — до 9 999 999,999
КвалификаторыДатыЧастиДаты.Дата, .Время, .ДатаВремятолько дата без времени

Проверим пример из таблицы: разрядность 10 и дробная часть 3 дают 7 знаков до запятой. Максимум — 9 999 999,999.

Строка с длиной 0 — это неограниченная строка. Такой реквизит нельзя использовать в отборах и сравнениях запроса без ПОДСТРОКА, поэтому для полей, по которым ищут, ставьте конкретную длину.

Как установить тип реквизита формы программно

Самый частый сценарий — добавить на форму реквизит, которого нет в Конфигураторе. Например, в расширении или в форме, где набор полей зависит от настроек. Реквизит создают на сервере методом формы ИзменитьРеквизиты(), тип передают вторым параметром РеквизитФормы.

Шаг 1. Создать реквизит с нужным типом

&НаСервере
Процедура ДобавитьКомментарийСклада()

    ТипКомментария = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(500));

    Добавляемые = Новый Массив;
    Добавляемые.Добавить(Новый РеквизитФормы("КомментарийСклада",
        ТипКомментария, , "Комментарий для склада"));
    ИзменитьРеквизиты(Добавляемые);

КонецПроцедуры

Параметры РеквизитФормы: имя, описание типов, путь (пусто — реквизит верхнего уровня), заголовок. Вызывать удобно из ПриСозданииНаСервере.

Шаг 2. Вывести реквизит на форму

Реквизит без элемента пользователь не увидит. Добавьте поле и свяжите его через ПутьКДанным:

Поле = Элементы.Добавить("КомментарийСклада", Тип("ПолеФормы"));
Поле.Вид = ВидПоляФормы.ПолеВвода;
Поле.ПутьКДанным = "КомментарийСклада";

Шаг 3. Обращаться к значению по имени

Реквизит появился во время выполнения, поэтому в коде модуля к нему обращаются через квадратные скобки: ЭтотОбъект["КомментарийСклада"]. Прямое имя КомментарийСклада при компиляции модуля ещё неизвестно.

Реквизиты колонок и табличных частей формы

Чтобы задать тип колонки у реквизита-таблицы, третьим параметром укажите путь к таблице:

Добавляемые.Добавить(Новый РеквизитФормы("Скидка",
    Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(5, 2)), "Товары"));

Для таблицы значений в памяти (не на форме) тип колонки задают при добавлении: ТЗ.Колонки.Добавить("Сумма", ТипСумма, "Сумма, руб."). Без типа колонка примет любое значение, и при выводе на форму или в запрос с такой колонкой начнутся проблемы.

Как сменить тип уже созданного реквизита формы

Свойство типа у существующего реквизита формы не перезаписывается. Тип меняют пересозданием: удалить реквизит и добавить с тем же именем и новым типом.

&НаСервере
Процедура СменитьТипРеквизитаФормы(ИмяРеквизита, НовыйТип)

    Удаляемые = Новый Массив;
    Удаляемые.Добавить(ИмяРеквизита);
    ИзменитьРеквизиты(, Удаляемые);

    Добавляемые = Новый Массив;
    Добавляемые.Добавить(Новый РеквизитФормы(ИмяРеквизита, НовыйТип));
    ИзменитьРеквизиты(Добавляемые);

КонецПроцедуры

Удалить так можно только реквизит, добавленный программно. Реквизит, созданный в Конфигураторе, ИзменитьРеквизиты() не удалит. Для него есть другой путь: в Конфигураторе дать составной тип, а в коде сужать его через ОграничениеТипа (раздел про составной тип ниже). Значение реквизита при пересоздании теряется, сохраните его заранее.

Реквизиты, тип которых хранится в данных: планы видов характеристик

Бывает, что тип нужно менять без доработки конфигурации вообще. Для этого в 1С есть планы видов характеристик (ПВХ). Каждый элемент ПВХ хранит свой тип в стандартном реквизите ТипЗначения. На этом устроены дополнительные реквизиты и сведения в типовых конфигурациях, виды субконто в плане счетов, свойства номенклатуры.

Создать вид характеристики с нужным типом:

Вид = ПланыВидовХарактеристик.ВидыХарактеристик.СоздатьЭлемент();
Вид.Наименование = "Срок гарантии, мес.";
Вид.ТипЗначения = Новый ОписаниеТипов("Число",
    Новый КвалификаторыЧисла(3, 0, ДопустимыйЗнак.Неотрицательный));
Вид.Записать();

Три правила, без которых код работает не так, как ждёте:

  • 📌 В ТипЗначения можно указывать только типы, которые входят в «Тип значения характеристик» самого ПВХ. Этот общий список задают в Конфигураторе.
  • 📌 Смена ТипЗначения у вида, по которому уже есть значения, не конвертирует старые данные. Записи регистра сведений или табличной части останутся со старым типом. Их переносят отдельной обработкой.
  • 📌 Чтобы поле значения на форме подстраивалось под вид, у реквизита в Конфигураторе заполняют свойство «Связь по типу». Программно то же делают через Элементы.Значение.ОграничениеТипа = Вид.ТипЗначения.

В типовых конфигурациях на БСП дополнительные реквизиты создают не голым СоздатьЭлемент(), а через стандартную форму в разделе НСИ и администрирование → Общие настройки → Дополнительные реквизиты и сведения (в БП 3.0 — Администрирование → Общие настройки). Там при записи заполняются наборы свойств и служебные поля. Код в обход формы легко оставит «висящий» реквизит, которого нет ни в одном наборе.

Тип полей динамического списка

У динамического списка типы колонок задаёт не программист, а текст запроса. Поэтому «установить тип реквизита » списка — значит правильно написать запрос.

Сузить составное поле через ВЫРАЗИТЬ

Если поле составного типа, а в списке нужен только один вариант, приведите его в запросе:

Список.ПроизвольныйЗапрос = Истина;
Список.ТекстЗапроса =
"ВЫБРАТЬ
|    Платеж.Ссылка,
|    Платеж.Дата,
|    ВЫРАЗИТЬ(Платеж.Получатель КАК Справочник.Контрагенты) КАК Получатель
|ИЗ
|    Документ.ПлатежныйДокумент КАК Платеж
|ГДЕ
|    Платеж.Получатель ССЫЛКА Справочник.Контрагенты";
Список.ОсновнаяТаблица = "Документ.ПлатежныйДокумент";

После ВЫРАЗИТЬ колонка получает один тип, и через точку становятся доступны реквизиты контрагента. Условие ССЫЛКА отбирает строки нужного типа. Без него строки с другим типом покажут пустое значение.

Создать динамический список программно

Динамический список сам может быть реквизитом формы, созданным из кода. Тип реквизита — ДинамическийСписок:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    Добавляемые = Новый Массив;
    Добавляемые.Добавить(Новый РеквизитФормы("СписокДоговоров",
        Новый ОписаниеТипов("ДинамическийСписок")));
    ИзменитьРеквизиты(Добавляемые);

    СписокДоговоров = ЭтотОбъект["СписокДоговоров"];
    СписокДоговоров.ПроизвольныйЗапрос = Истина;
    СписокДоговоров.ОсновнаяТаблица = "Справочник.ДоговорыКонтрагентов";
    СписокДоговоров.ТекстЗапроса =
    "ВЫБРАТЬ
    |    Договоры.Ссылка,
    |    Договоры.Наименование
    |ИЗ
    |    Справочник.ДоговорыКонтрагентов КАК Договоры";

    Таблица = Элементы.Добавить("ТаблицаДоговоров", Тип("ТаблицаФормы"));
    Таблица.ПутьКДанным = "СписокДоговоров";

    Колонка = Элементы.Добавить("ТаблицаДоговоровНаименование", Тип("ПолеФормы"), Таблица);
    Колонка.Вид = ВидПоляФормы.ПолеВвода;
    Колонка.ПутьКДанным = "СписокДоговоров.Наименование";

КонецПроцедуры

Сначала заполняют ОсновнаяТаблица и текст запроса, потом выводят колонки. Колонку можно привязать только к полю, которое есть в запросе.

Составной тип реквизита: собрать, ограничить, задать конкретный

Составной тип — это ОписаниеТипов с несколькими типами. Собрать его можно тремя способами:

// 1. Строкой
ТипПолучателя = Новый ОписаниеТипов(
    "СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица");

// 2. Массивом типов — удобно, когда список собирается в цикле
Типы = Новый Массив;
Типы.Добавить(Тип("СправочникСсылка.Контрагенты"));
Типы.Добавить(Тип("СправочникСсылка.ФизическиеЛица"));
ТипПолучателя = Новый ОписаниеТипов(Типы);

// 3. От существующего описания: добавить или убрать типы
ТипСоСтрокой = Новый ОписаниеТипов(ТипПолучателя, "Строка");
ТипБезСтроки = Новый ОписаниеТипов(ТипСоСтрокой, , "Строка");

Третья форма конструктора: исходное описание, добавляемые типы, вычитаемые типы. Ею удобно расширять тип из метаданных: Новый ОписаниеТипов(Метаданные.Документы.ПлатежныйДокумент.Реквизиты.Получатель.Тип, "Строка").

Ограничить выбор типа на форме

Реквизит в Конфигураторе составной, а в конкретной ситуации пользователь должен выбирать только контрагентов. Сужают тип у поля формы:

Элементы.Получатель.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.Контрагенты");

Ограничение работает только в пределах типа реквизита. Тип, которого нет в описании реквизита, через ОграничениеТипа не добавить.

Задать конкретный тип значению

Иногда нужно не ограничить выбор, а сразу «переключить» пустое значение на нужный тип. Тогда кнопка выбора откроет форму именно этого справочника:

Объект.Получатель = Справочники.ФизическиеЛица.ПустаяСсылка();

Тот же результат даёт ПривестиЗначение, когда имя типа известно только во время выполнения:

ОписаниеНужногоТипа = Новый ОписаниеТипов("СправочникСсылка." + ИмяСправочника);
Объект.Получатель = ОписаниеНужногоТипа.ПривестиЗначение(Неопределено);

ПривестиЗначение возвращает значение по умолчанию для первого типа описания. Для ссылки это пустая ссылка нужного справочника.

Тип реквизита справочника или документа: что можно поменять из кода

Тип реквизита объекта конфигурации из кода можно только прочитать. Коллекция Метаданные в режиме Предприятия доступна лишь на чтение:

ТипАртикула = Метаданные.Справочники.Номенклатура.Реквизиты.Артикул.Тип;
Для Каждого ТекТип Из ТипАртикула.Типы() Цикл
    Сообщить(ТекТип);
КонецЦикла;

Изменить тип реквизита справочника или документа можно тремя путями:

СпособКогда подходитРиски
Конфигуратор: свойство «Тип» реквизитаразовая правка своей конфигурациив типовой нужно включить возможность изменения, это усложняет обновления
Новый реквизит в расширениитиповая на поддержке, нужен реквизит другого типастарый реквизит остаётся, данные переносят обработкой
Выгрузка конфигурации в XML и правка <Type>массовая правка, сборка в конвейереошибка в XML не даст загрузить конфигурацию

Пакетный вариант с командной строкой Конфигуратора:

"C:\Program Files\1cv8\8.3.24.1691\bin\1cv8.exe" DESIGNER /F"D:\Bases\Test" /DumpConfigToFiles "D:\cfg"
rem ...правка D:\cfg\Catalogs\Номенклатура.xml...
"C:\Program Files\1cv8\8.3.24.1691\bin\1cv8.exe" DESIGNER /F"D:\Bases\Test" /LoadConfigFromFiles "D:\cfg" /UpdateDBCfg

Номер версии в пути подставьте свой, его видно в папке C:\Program Files\1cv8\.

Смена типа реквизита с данными — реструктуризация базы. Значения, которые не приводятся к новому типу, станут пустыми. Перед изменением структуры сделайте резервную копию базы. Для обновления структуры нужен монопольный доступ, поэтому пользователей придётся отключить.

Проверка типа и ошибки при его установке

Перед записью значения в реквизит полезно проверить, подходит ли тип. Для этого есть ТипЗнч() и метод СодержитТип():

Функция ТипДопустим(Значение, ОписаниеТипаРеквизита)
    Возврат ОписаниеТипаРеквизита.СодержитТип(ТипЗнч(Значение));
КонецФункции

Если имя типа собирается из строки, проверьте, что объект есть в конфигурации. Иначе Тип() вызовет исключение:

Если Метаданные.Справочники.Найти(ИмяСправочника) = Неопределено Тогда
    ВызватьИсключение СтрШаблон("Справочник %1 не найден в конфигурации", ИмяСправочника);
КонецЕсли;
НужныйТип = Тип("СправочникСсылка." + ИмяСправочника);

Отдельная ловушка — тихое приведение. Присвоение значения реквизиту объекта или формы приводит его к типу реквизита без ошибки. Строку из 200 символов в реквизит длиной 100 платформа обрежет. Ссылку на другой справочник в реквизит с одним типом — заменит пустым значением. Поэтому проверка СодержитТип() перед записью ловит то, что иначе всплывёт через месяц в отчётах.

Диагностика: выберите симптом

Частые вопросы

Можно ли изменить тип реквизита документа без Конфигуратора?
Нет. Структуру объекта метаданных меняют в Конфигураторе или пакетной загрузкой конфигурации. Из кода в режиме Предприятия метаданные доступны только для чтения.
Чем ОграничениеТипа отличается от типа реквизита?
Тип реквизита определяет, что вообще можно хранить. ОграничениеТипа действует только на поле формы и сужает выбор для пользователя. Код по-прежнему может записать любой тип из описания реквизита.
Как узнать тип реквизита, если значение пустое?
Пустая ссылка тоже имеет тип: ТипЗнч вернёт его. Если значение Неопределено, тип не задан, и смотреть нужно описание типа реквизита в метаданных.
Почему в запросе к составному реквизиту медленно работает отбор?
Составной тип хранится несколькими полями, и обращение через точку соединяет все таблицы-кандидаты. ВЫРАЗИТЬ к одному типу и условие ССЫЛКА сокращают соединения.
Можно ли задать тип реквизита формы как ссылку на справочник из расширения?
Да, если справочник добавлен в расширение: тип СправочникСсылка.<Имя> доступен так же, как тип объекта основной конфигурации.