Ссылка нужного типа в 1С 8.3: приведение в запросе, получение из GUID, строки и реквизитов
Содержание
В запросе ссылку составного типа приводят к одному типу так: ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг). Если в поле лежит документ другого вида, результат будет NULL. В коде на встроенном языке «выразить ссылку» обычно значит другое — получить ссылку из GUID (ПолучитьСсылку), из строки (XMLЗначение) или найти её по реквизиту (НайтиПоРеквизиту).
Какой способ выбрать, зависит от того, что у вас на входе. Есть поле составного типа в запросе — нужен ВЫРАЗИТЬ. Пришёл идентификатор из обмена или внешней системы — ПолучитьСсылку. Есть только ИНН, код или номер — поиск через менеджер или запрос. Всё описанное работает в платформе 1С:Предприятие 8.3 и не зависит от конфигурации.
Как устроена ссылка в 1С и зачем её «выражать»
Ссылка — это значение, которое указывает на объект базы: элемент справочника, документ, план видов характеристик, счёт, бизнес-процесс, задачу. Сама ссылка не хранит реквизиты. Она хранит тип и уникальный идентификатор, а данные платформа читает из таблицы объекта, когда вы к ним обращаетесь.
Отсюда два вида типов:
- 📌 Простой ссылочный тип —
СправочникСсылка.Контрагенты. Поле всегда указывает на одну таблицу. - 🔀 Составной тип — поле может хранить ссылки на разные объекты. Классика —
Регистратору регистров,Субконто1..3в бухгалтерии,Документ-основание, реквизит «Объект» в универсальных регистрах.
В СУБД ссылка лежит как 16-байтовый двоичный идентификатор. У составного поля к нему добавляются колонки с признаком типа и номером таблицы. Поэтому платформа не знает заранее, в какую таблицу идти за реквизитами составного поля.
Вот зачем нужен ВЫРАЗИТЬ. Когда вы пишете Продажи.Регистратор.Дата, платформа строит левое соединение со всеми таблицами документов, которые могут быть регистраторами. Таких таблиц бывает несколько десятков. Запрос раздувается и тормозит. Приведение к одному типу оставляет одно соединение.
| Задача | Где решается | Инструмент |
|---|---|---|
| Сузить составное поле до одного типа | Запрос, СКД | ВЫРАЗИТЬ(... КАК Документ.Имя) |
| Проверить тип без приведения | Запрос | Поле ССЫЛКА Документ.Имя, ТИПЗНАЧЕНИЯ() |
| Получить ссылку из GUID | Встроенный язык | ПолучитьСсылку(Новый УникальныйИдентификатор(...)) |
| Получить ссылку из строки | Встроенный язык | XMLЗначение, ЗначениеИзСтрокиВнутр |
| Найти по коду, ИНН, номеру | Встроенный язык, запрос | НайтиПоКоду, НайтиПоРеквизиту, ГДЕ |
| Пустая или предопределённая ссылка | Запрос, код | ЗНАЧЕНИЕ(...), ПустаяСсылка() |
Как выразить ссылку в запросе через ВЫРАЗИТЬ
Синтаксис операции: ВЫРАЗИТЬ(<Выражение> КАК <Тип>). Для ссылочного типа указывают имя таблицы так же, как в ИЗ: Справочник.Номенклатура, Документ.ПоступлениеТоваровУслуг.
Реквизит регистратора без лишних соединений
Задача: вытащить из движений регистра номер и комментарий документа реализации.
ВЫБРАТЬ
Движения.Регистратор КАК Регистратор,
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Номер КАК НомерРеализации,
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Комментарий КАК Комментарий
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Здесь условие ССЫЛКА отсекает чужие документы, а ВЫРАЗИТЬ даёт доступ к полям нужного вида. Без условия строки других регистраторов тоже попадут в выборку, только НомерРеализации у них будет NULL.
Имя регистра и документа в примере подставьте свои. Набор документов и регистров в БП, УТ и ЗУП разный, а синтаксис один.
Приведение субконто
В бухгалтерских запросах субконто почти всегда составного типа. Чтобы взять ИНН контрагента из Субконто1, приведите его к справочнику:
ВЫБРАТЬ
ВЫРАЗИТЬ(Остатки.Субконто1 КАК Справочник.Контрагенты) КАК Контрагент,
ВЫРАЗИТЬ(Остатки.Субконто1 КАК Справочник.Контрагенты).ИНН КАК ИНН,
Остатки.СуммаОстатокДт КАК Долг
ИЗ
РегистрБухгалтерии.Хозрасчетный.Остатки(, , , ) КАК Остатки
ГДЕ
Остатки.Субконто1 ССЫЛКА Справочник.Контрагенты
Параметры виртуальной таблицы оставлены пустыми для краткости. В рабочем запросе задайте период и счёт, иначе выборка будет огромной.
Другие приведения, которые делают тем же оператором
ВЫРАЗИТЬ работает не только со ссылками. Два частых случая:
- 🔤 Строка неограниченной длины → строка фиксированной длины. Поля вида
Комментарийнельзя сравнивать и группировать напрямую. ПомогаетВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200)). - 🔢 Число → число с другой точностью:
ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2)). Удобно при объединении результатов с разной разрядностью.
Строку в ссылку в языке запросов превратить нельзя. Если у вас в поле лежит GUID строкой, получите ссылку в коде и передайте её в запрос параметром.
Ссылка из уникального идентификатора (GUID)
У каждого ссылочного объекта есть уникальный идентификатор. Его отдают обмены, REST-сервисы, выгрузки в JSON. Восстановить по нему ссылку можно через менеджер объекта:
СтрокаGUID = "5c1b2f6e-7a3d-11ee-9a2b-005056b3c1d4";
Идентификатор = Новый УникальныйИдентификатор(СтрокаGUID);
Ссылка = Справочники.Контрагенты.ПолучитьСсылку(Идентификатор);
Обратная операция — взять GUID из ссылки:
СтрокаGUID = Строка(Ссылка.УникальныйИдентификатор());
Ссылка «битая»: объект не найден
ПолучитьСсылку не обращается к базе. Он просто собирает ссылку из типа и идентификатора. Если объекта с таким GUID нет, вы получите ссылку, которая в форме покажется как <Объект не найден>.
Проверка существования через запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ИСТИНА КАК Есть
|ИЗ
| Справочник.Контрагенты КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
СсылкаСуществует = Не Запрос.Выполнить().Пустой();
В типовых конфигурациях на БСП есть готовая функция ОбщегоНазначения.СсылкаСуществует(Ссылка). Вариант Ссылка.ПолучитьОбъект() = Неопределено тоже работает, но читает объект целиком — на больших документах это медленно.
Когда GUID нужен заранее
Иногда объект создают в 1С с заранее известным идентификатором, например при загрузке из внешней системы. Для этого есть УстановитьСсылкуНового:
НовыйЭлемент = Справочники.Контрагенты.СоздатьЭлемент();
НовыйЭлемент.УстановитьСсылкуНового(Справочники.Контрагенты.ПолучитьСсылку(Идентификатор));
НовыйЭлемент.Наименование = "ООО Пример";
НовыйЭлемент.Записать();
Перед записью проверьте, что объекта с таким GUID ещё нет. Иначе запись упадёт с ошибкой о неуникальности, а при загрузке пакетом вы потеряете остаток данных.
Строка в ссылку и ссылка в строку
GUID без типа — полдела. Если строка должна сама нести и тип, и идентификатор, есть три механизма.
| Способ | Что в строке | Когда использовать |
|---|---|---|
XMLСтрока / XMLЗначение | Только GUID, тип задаёте сами | Обмен с внешними системами, JSON |
ЗначениеВСтрокуВнутр / ЗначениеИзСтрокиВнутр | Тип и идентификатор во внутреннем формате | Передача между базами 1С одной конфигурации, временное хранение |
ПолучитьНавигационнуюСсылку | Ссылка вида e1cib/data/... | Ссылки для пользователя: письма, задачи, мессенджеры |
Пример с XML-сериализацией. Тип тут задаётся явно, поэтому подходит, когда вы точно знаете, что пришло:
// Ссылка -> строка
СтрокаGUID = XMLСтрока(Ссылка);
// Строка -> ссылка
Ссылка = XMLЗначение(Тип("ДокументСсылка.РеализацияТоваровУслуг"), СтрокаGUID);
Пример с внутренним форматом. Тип хранится в самой строке, поэтому восстанавливать можно, не зная его:
СтрокаВнутр = ЗначениеВСтрокуВнутр(Ссылка);
// ... сохранили, передали, прочитали
Ссылка = ЗначениеИзСтрокиВнутр(СтрокаВнутр);
Строку внутреннего формата не правьте руками и не храните годами. Она привязана к метаданным: после переименования объекта или переноса в другую конфигурацию восстановить ссылку не получится.
Функция Строка(Ссылка) для этой задачи не годится. Она возвращает представление — наименование или «Реализация 00-000123 от …» — а не идентификатор. Обратно из представления ссылку не собрать.
Универсальная ссылка: тип узнаём на ходу
Когда в переменной лежит «какая-то» ссылка — например, из составного реквизита — сначала выясните её тип, а потом решайте, что делать.
Проверка типа
Если ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
// работаем как с реализацией
КонецЕсли;
// Любой документ?
ЭтоДокумент = Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение));
Аналогично есть Справочники.ТипВсеСсылки(). В БСП для общего вопроса «это вообще ссылка?» используют ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Значение)).
Объект метаданных и менеджер по ссылке
Имя таблицы объекта узнают через метаданные:
МетаОбъект = Метаданные.НайтиПоТипу(ТипЗнч(Ссылка));
ПолноеИмя = МетаОбъект.ПолноеИмя(); // например, "Документ.РеализацияТоваровУслуг"
Полное имя пригодится, чтобы собрать текст запроса к нужной таблице на лету. Менеджер в конфигурациях на БСП берут через ОбщегоНазначения.МенеджерОбъектаПоСсылке(Ссылка). Без БСП — по имени коллекции: Документы[МетаОбъект.Имя].
Пустая и предопределённая ссылка
- 🔹 В коде:
Справочники.Контрагенты.ПустаяСсылка(), проверка —Ссылка.Пустая(). - 🔹 В запросе:
ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка). - 🔹 Значение перечисления в запросе:
ЗНАЧЕНИЕ(Перечисление.<Имя>.<Значение>). - 🔹 Предопределённый элемент на клиенте:
ПредопределенноеЗначение("Справочник.<Имя>.<ИмяЭлемента>").
В ЗНАЧЕНИЕ() работают только пустые ссылки, значения перечислений и предопределённые элементы. Обычный элемент справочника по наименованию так не получить — его передают параметром.
Получить ссылку поиском по коду, наименованию или реквизиту
Когда GUID нет, ссылку ищут по данным, которые знает пользователь или внешняя система.
Методы менеджера
Ссылка = Справочники.Контрагенты.НайтиПоКоду("000000015");
Ссылка = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка", Истина); // Истина — точное совпадение
Ссылка = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7701234567");
Если Ссылка.Пустая() Тогда
// не нашли
КонецЕсли;
Для документов есть Документы.<Имя>.НайтиПоНомеру(Номер, Дата). Дату передают, если номера уникальны только в пределах периода — года, квартала.
Ограничения, о которых вспоминают поздно:
- ⚠️
НайтиПоРеквизитуиНайтиПоНаименованиювозвращают первый найденный объект. Если в базе два контрагента с одним ИНН (головная организация и филиал), получите любой из них. - ⚠️ Если ничего не найдено, вернётся пустая ссылка, а не
Неопределено. Проверяйте через.Пустая(). - ⚠️ Помеченные на удаление объекты тоже находятся.
Поиск запросом — когда нужны условия
Если нужно отбросить помеченные на удаление, учесть КПП или искать сразу много значений, пишите запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| К.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК К
|ГДЕ
| К.ИНН = &ИНН
| И К.КПП = &КПП
| И НЕ К.ПометкаУдаления";
Запрос.УстановитьПараметр("ИНН", "7701234567");
Запрос.УстановитьПараметр("КПП", "770101001");
Выборка = Запрос.Выполнить().Выбрать();
Ссылка = ?(Выборка.Следующий(), Выборка.Ссылка, Справочники.Контрагенты.ПустаяСсылка());
Для загрузки сотен строк один запрос с условием В (&СписокИНН) быстрее, чем сотня вызовов НайтиПоРеквизиту в цикле.
Ссылки в запросах и СКД: тонкие места
Обращение «через точку»
Каждая точка после ссылочного поля — неявное соединение. Документ.Контрагент.ГоловнойКонтрагент.ИНН — это два соединения. Для простого типа это терпимо. Для составного типа соединений становится столько, сколько типов в поле. Отсюда правило: у составных полей реквизиты берите только через ВЫРАЗИТЬ.
Проверка типа в условиях
Есть два равноценных способа:
ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
// или
ГДЕ ТИПЗНАЧЕНИЯ(Движения.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)
Первый короче и читается легче.
Представление вместо реквизитов
Если нужно только показать ссылку текстом, приведение не требуется. ПРЕДСТАВЛЕНИЕССЫЛКИ(Движения.Регистратор) даёт строку «Реализация … от …» для любого типа.
В схеме компоновки данных
В наборе данных «Запрос» ВЫРАЗИТЬ пишется так же, как в обычном запросе. Удобный приём — дать приведённому полю отдельный псевдоним, например Реализация. Тогда в настройках отчёта пользователь сможет брать реквизиты через точку уже от простого типа, и СКД не будет строить лишних соединений.
Параметры ссылочного типа в СКД задают на закладке «Параметры». Укажите у параметра конкретный тип значения. Иначе при выборе значения пользователь увидит список всех типов, а в запрос может прийти ссылка не того вида.
Ошибки при работе со ссылками и как их найти
Как отлаживать
- Откройте запрос в консоли запросов и выведите проблемное поле вместе с
ТИПЗНАЧЕНИЯ(Поле). Сразу видно, какие типы там реально лежат. - В коде поставьте точку останова и посмотрите значение в отладчике: в табло видно и тип, и представление.
- Для быстрой проверки без отладчика выведите
Сообщить(ТипЗнч(Значение))иМетаданные.НайтиПоТипу(ТипЗнч(Значение)).ПолноеИмя(). - Если подозреваете битые ссылки, проверьте их запросом существования, а не открытием формы.
Не исправляйте битые ссылки массовой обработкой в рабочей базе без копии. Замена ссылок затрагивает движения документов, и откатить её без резервной копии нельзя.