Тип значения реквизита из кода: ОписаниеТипов, ТипЗнч и приведение

· 9 мин

Содержание

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

Есть одно ограничение. Тип реквизита справочника или документа, заданный в конфигураторе, из кода во время работы не меняется: метаданные доступны только на чтение. Меняют либо конфигурацию, либо то, что лежит поверх неё, — реквизиты формы, колонки, ограничение типа элемента и само значение в реквизите составного типа.

Что в 1С вообще можно типизировать из кода

Каждое значение во встроенном языке имеет тип. Переменная модуля своего типа не имеет: она принимает тип того значения, которое в неё записали. А вот реквизиты, колонки и поля форм хранят описание допустимых типов, и именно его задают программно.

Где задаётся типМожно ли менять в режиме 1С:ПредприятиеЧем задаётся
Реквизит справочника, документа, регистра❌ Только в конфигуратореСвойство «Тип» в палитре свойств
Реквизит формы, добавленный программно✅ ДаРеквизитФормы + ИзменитьРеквизиты()
Колонка таблицы значений, дерева✅ При добавлении колонкиВторой параметр Колонки.Добавить()
Поле формы с составным типом✅ Сузить набор типовСвойство ОграничениеТипа
Значение реквизита составного типа✅ Записать значение нужного типаПрисваивание, ПривестиЗначение()

Прочитать тип реквизита из метаданных можно всегда:

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

Имена справочника и реквизита здесь условные — подставьте свои.

Конструктор ОписаниеТипов и функция Тип

Функция Тип()

Тип("ИмяТипа") возвращает значение типа Тип по его имени. Имя пишут так же, как в синтаксис-помощнике:

  • 🔢 Тип("Число"), Тип("Строка"), Тип("Дата"), Тип("Булево");
  • 📁 Тип("СправочникСсылка.Контрагенты");
  • 📄 Тип("ДокументСсылка.РеализацияТоваровУслуг");
  • 📦 Тип("ТаблицаЗначений"), Тип("Структура").

Опечатка в имени не ловится при синтаксическом контроле. Ошибка всплывёт только при выполнении строки.

Конструктор ОписаниеТипов

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

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

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

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

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

Пропущенные параметры отмечают запятыми. Порядок квалификаторов фиксирован: число, строка, дата. Перепутали местами — получите строку неограниченной длины или число без дробной части.

Строка с нулевой длиной в КвалификаторыСтроки(0) — это строка неограниченной длины. Для колонки, которая пойдёт в запрос, задавайте конкретную длину: по неограниченным строкам нельзя группировать и сравнивать в условии запроса.

Вторая форма: на основе существующего описания

Если нужно добавить или убрать тип из уже готового описания, используют конструктор с исходным описанием:

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

Третий параметр — вычитаемые типы. Так удобно получить «тот же тип, что у реквизита, но без одного варианта».

Установка типа реквизита формы и колонки программно

Реквизит управляемой формы

Реквизит формы создают на сервере. Тип задаётся вторым параметром РеквизитФормы, а сам реквизит появляется после вызова ИзменитьРеквизиты(). Затем к нему привязывают элемент.

&НаСервере
Процедура ДобавитьРеквизитКоэффициент()

    ТипКоэф = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 3));

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

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

    ЭтотОбъект.Коэффициент = 1;

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

Третий параметр РеквизитФормы — путь. Пустой путь означает реквизит верхнего уровня. Чтобы добавить колонку в реквизит-таблицу формы, укажите путь к ней, например "ТаблицаРасчета".

Поменять тип уже созданного реквизита напрямую нельзя. Порядок такой:

  1. Удалить элемент формы, связанный с реквизитом, через Элементы.Удалить().
  2. Удалить реквизит: передать его имя в массиве вторым параметром ИзменитьРеквизиты(, Удаляемые).
  3. Добавить реквизит с тем же именем и новым описанием типов.
  4. Создать элемент заново и заполнить значение.

Удалить можно только реквизит, добавленный программно. Реквизиты, созданные в конфигураторе, ИзменитьРеквизиты() не удаляет и вызывает ошибку.

Колонка таблицы значений

Тип колонки задают при добавлении:

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

Колонка без типа принимает что угодно, и в коде это часто сходит с рук. Проблема появляется, когда такую таблицу передают параметром в запрос и помещают во временную таблицу: платформе нужно знать тип каждой колонки. Типизируйте колонки сразу, если таблица дальше пойдёт в запрос, в табличный документ или в реквизит формы.

Проверка и приведение типов перед присваиванием

ТипЗнч и сравнение с Тип

Проверка типа значения — сравнение результата ТипЗнч() со значением Тип():

Если ТипЗнч(Значение) = Тип("Число") Тогда
    Итог = Итог + Значение;
ИначеЕсли ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") Тогда
    ОбработатьКонтрагента(Значение);
КонецЕсли;

Когда ссылка может быть любого ссылочного типа, сравнивать её с каждым типом по очереди неудобно. Метод Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)) возвращает объект метаданных, а дальше по его имени или по Метаданные.Документы.Содержит() решают, что делать со значением.

Для проверки «входит ли тип в описание» есть метод СодержитТип():

ТипРеквизита = Метаданные.Документы.ЗаказКлиента.Реквизиты.Контрагент.Тип;
Если ТипРеквизита.СодержитТип(ТипЗнч(Значение)) Тогда
    Объект.Контрагент = Значение;
КонецЕсли;

ПривестиЗначение

ОписаниеТипов.ПривестиЗначение(Значение) возвращает значение, приведённое к описанию. Если привести нельзя, возвращается пустое значение первого подходящего типа, исключения не будет.

Описание типовЧто передалиЧто вернётся
Число(15,2)"123.45"123.45
Число(15,2)"абв"0
Строка(5)"Бухгалтерия""Бухга"
СправочникСсылка.Номенклатурассылка на контрагентапустая ссылка номенклатуры

Последняя строка — главная ловушка. Неверная ссылка молча превращается в пустую, и документ записывается с незаполненным реквизитом. Если пустое значение недопустимо, проверяйте СодержитТип() до приведения, а не после.

Для преобразования строки в число с проверкой лучше не полагаться на Число() без обработки исключения: на строке с буквами оно падает. ПривестиЗначение не падает, но и не сообщает, что преобразование не удалось.

Значения произвольного типа в табличной части

Частый случай — табличная часть «Свойство / Значение», где колонка «Значение» имеет составной тип. Нужный тип значения определяется выбранным свойством. Обычно это план видов характеристик: у каждого вида характеристики есть стандартный реквизит ТипЗначения с описанием типов.

Без кода: связь по типу

У реквизита составного типа в конфигураторе есть свойство «Связь по типу». В нём указывают реквизит строки, который хранит вид характеристики. После этого поле ввода само ограничивает выбор типами из ТипЗначения выбранного свойства. Если ваша конфигурация позволяет такую правку, это самый чистый вариант.

Кодом: ограничение типа поля

В управляемой форме ссылку на клиенте «разыменовать» нельзя: ТекущиеДанные.Свойство.ТипЗначения на клиенте недоступно. Тип получают серверным вызовом без контекста и ставят в ОграничениеТипа поля колонки:

&НаКлиенте
Процедура СвойстваПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    ТекДанные = Элементы.Свойства.ТекущиеДанные;
    Если ЗначениеЗаполнено(ТекДанные.Свойство) Тогда
        Элементы.СвойстваЗначение.ОграничениеТипа = ТипЗначенияСвойства(ТекДанные.Свойство);
    КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ТипЗначенияСвойства(Свойство)
    Возврат Свойство.ТипЗначения;
КонецФункции

При смене свойства в уже заполненной строке приведите старое значение, чтобы в строке не остался, например, текст при новом типе «Число»:

&НаСервереБезКонтекста
Функция ПривестиКТипуСвойства(Свойство, Значение)
    Возврат Свойство.ТипЗначения.ПривестиЗначение(Значение);
КонецФункции

Ограничение типа поля — это только интерфейс. Значение, записанное в табличную часть кодом (обработкой, обменом), этим ограничением не проверяется. Проверку типа для таких строк делайте в ОбработкаПроверкиЗаполнения или ПередЗаписью объекта.

Задать тип нового вида характеристики

Сам набор допустимых типов для вида характеристики тоже задаётся кодом, когда вид создают программно, например при первоначальном заполнении базы:

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

Имя плана видов характеристик условное. Допустимые типы в ТипЗначения ограничены свойством «Тип значения характеристик» самого плана: указать тип, которого там нет, не получится.

Тип значения в запросе

Если колонка составного типа участвует в отборе, тип проверяют прямо в запросе:

ВЫБРАТЬ
    Свойства.Ссылка,
    ВЫРАЗИТЬ(Свойства.Значение КАК Справочник.Номенклатура) КАК Номенклатура
ИЗ
    Документ.ЗаказКлиента.Свойства КАК Свойства
ГДЕ
    ТИПЗНАЧЕНИЯ(Свойства.Значение) = ТИП(Справочник.Номенклатура)

ВЫРАЗИТЬ сужает тип поля и убирает лишние соединения с таблицами других типов. На больших табличных частях это заметно ускоряет запрос.

Ошибки несоответствия типов и как их ловить

Ошибки типов в 1С делятся на две группы: явные, когда платформа выдаёт исключение, и тихие, когда значение молча меняется.

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

Для кода, который получает данные извне (загрузка из Excel, обмен, HTTP-сервис), полезно собрать проверку в одну функцию:

Функция ЗначениеДопустимо(Значение, ОписаниеТипа, ТекстОшибки)
    Если Не ОписаниеТипа.СодержитТип(ТипЗнч(Значение)) Тогда
        ТекстОшибки = СтрШаблон("Недопустимый тип значения: %1", Строка(ТипЗнч(Значение)));
        Возврат Ложь;
    КонецЕсли;
    Возврат Истина;
КонецФункции

Если операция всё же может упасть, оберните её в Попытка … Исключение и выведите ОписаниеОшибки(). Глушить исключение пустым блоком нельзя: ошибка уйдёт, а неправильные данные останутся.

Неопределено, NULL и пустое значение

Неопределено — отдельный тип с единственным значением. Его получают:

  • 🔹 переменная, которой ничего не присвоили;
  • 🔹 незаполненный реквизит составного типа;
  • 🔹 результат Найти() в структуре или соответствии, если ключа нет;
  • 🔹 функция, которая завершилась без Возврат значения.

Пустой реквизит единичного типа — не Неопределено, а пустое значение своего типа: 0, пустая строка, пустая ссылка. Отсюда частая ошибка: проверка Если Объект.Контрагент = Неопределено для реквизита с одним ссылочным типом всегда даёт Ложь.

СитуацияКак проверять
Заполнен ли реквизит любого типаЗначениеЗаполнено(Значение)
Задано ли значение составного типа вообщеЗначение = Неопределено
Пустое поле в результате запроса после левого соединенияЕСТЬ NULL в запросе, ЕстьNULL() в тексте запроса
Значение определённого типаТипЗнч(Значение) = Тип("...")

NULL и Неопределено — разные вещи. NULL приходит из запроса, когда в соединении не нашлось записи. В выборке из запроса его можно встретить в любом поле, даже если реквизит в метаданных имеет единичный тип. Перед арифметикой с такими полями оборачивайте их в ЕСТЬNULL(Поле, 0) прямо в тексте запроса.

Хотите, чтобы реквизит мог явно хранить «ничего не выбрано»? Сделайте его тип составным. Отдельно добавлять Неопределено в описание не нужно: для составного типа это и так пустое значение.

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

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