Как получить данные по ссылке до записи объекта и почему запрос их не видит

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В практике разработчика 1С часто возникает ситуация, когда необходимо сформировать печатную форму или выполнить какую-либо обработку данных для еще не записанного объекта. Казалось бы, платформа предоставляет инструменты для работы с будущей ссылкой, такие как УстановитьСсылкуНового(), однако при попытке использовать эту ссылку в запросе разработчик сталкивается с тем, что объект оказывается «пустым». Разберем подробно, почему так происходит, как работают механизмы платформы «внутри» и какими способами можно решить эту задачу.

Суть проблемы: Ссылка есть, а данных нет

Рассмотрим ситуацию, описанную участником форума. Программист создает новый элемент справочника программно, заполняет его реквизиты и хочет получить ссылку до фактического вызова метода Записать(). Для этого используется стандартный механизм:


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

После выполнения этого кода СправочникОбъект.Ссылка действительно перестает быть пустой. Однако при передаче этой ссылки в запрос или в модуль печати, который использует запросы, результат оказывается нулевым. Все реквизиты по этой ссылке в базе данных — неопределены. Проанализируем, в чем кроется причина такого поведения системы.

Различие между оперативной памятью и базой данных

Важно понимать архитектурное различие между объектом (CatalogObject) и ссылкой (CatalogRef). Объект — это данные, которые в данный момент находятся в оперативной памяти сервера или клиента. Вы можете менять их, заполнять реквизиты и табличные части. Ссылка — это всего лишь уникальный идентификатор (GUID), адрес записи в таблице базы данных (СУБД).

Когда мы используем метод УстановитьСсылкуНового(), мы просто говорим платформе: «Когда этот объект будет записываться, используй вот этот конкретный GUID». Но сама запись в таблицах SQL появится только в момент выполнения метода Записать().

Запросы (Query) в 1С работают напрямую с таблицами базы данных (через SQL-запросы к СУБД). Они ничего не знают о том, что происходит в оперативной памяти вашего сеанса. Поэтому, когда запрос пытается найти данные по вашей «новой» ссылке, он обращается к таблице справочника, не находит там записи с таким GUID и возвращает пустоту (NULL). Это фундаментальный принцип работы клиент-серверной архитектуры.

Почему типовые печатные формы требуют записи объекта?

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

В модуль менеджера передается массив ссылок. Менеджер объекта строит запрос к базе данных, чтобы быстро получить данные всех необходимых полей. Поскольку незаписанного объекта в базе нет, менеджер просто не может получить его данные. Именно поэтому попытка «обмануть» систему подменой ссылки на незаписанную не срабатывает.

Варианты решения задачи

Если вам критически важно сформировать данные без финальной записи объекта в базу, рассмотрим несколько подходов.

Метод 1: Использование временных таблиц

Если вы не можете изменить логику запроса в печатной форме, вы можете передать данные объекта в запрос через временную таблицу. Это позволит «подсунуть» данные из оперативной памяти в механизм запросов. Проанализируем примерную схему:

  1. Создаем ТаблицаЗначений с колонками, идентичными реквизитам справочника.
  2. Заполняем одну строку данными из нашего СправочникОбъект.
  3. Передаем эту таблицу в запрос как параметр.
  4. В тексте запроса используем конструкцию ВЫБРАТЬ * ПОМЕСТИТЬ ВтДанные ИЗ &ТЗ КАК ТЗ.
  5. В основном запросе соединяем таблицы базы данных с нашей временной таблицей.

Метод 2: Модификация функции формирования данных

Это самый правильный и «чистый» путь. Вместо того чтобы передавать в функцию печати только Ссылку, научим её принимать ДанныеЗаполнения. Это может быть либо сам объект, либо структура с его реквизитами.

Рассмотрим пример кода в функции формирования печатной формы:


Функция СформироватьПечатнуюФорму(СсылкаИлиОбъект)
    Если ТипЗнч(СсылкаИлиОбъект) = Тип("СправочникОбъект.ИмяСправочника") Тогда
        // Берем данные напрямую из объекта
        ДанныеДляПечати = ПолучитьДанныеИзОбъекта(СсылкаИлиОбъект);
    Иначе
        // Объект уже в базе, используем стандартный запрос по ссылке
        ДанныеДляПечати = ПолучитьДанныеЗапросом(СсылкаИлиОбъект);
    КонецЕсли;
    // ... дальнейшая логика вывода в табдок
КонецФункции

Метод 3: Грязный хак с транзакцией

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

  1. Вы вызываете НачатьТранзакцию().
  2. Записываете объект (Объект.Записать()). Теперь он физически есть в БД, и запросы его видят.
  3. Формируете печатную форму (выводите её на экран или сохраняете).
  4. Вызываете ОтменитьТранзакцию().

Важно: При отмене транзакции запись из базы данных исчезнет, но сформированный табличный документ останется в памяти. Внимание! Этот метод крайне не рекомендуется для сложных систем, так как запись объекта может инициировать подписки на события, записи в регистры и другие побочные эффекты, которые могут некорректно откатиться или вызвать блокировки таблиц.

Выводы

Попытка использовать УстановитьСсылкуНового() для работы с запросами — это заблуждение относительно механизмов работы платформы. Ссылка — это ключ, а не сами данные. Если вам нужно работать с объектом до записи, вы должны передавать в алгоритмы обработки сам объект или структуру его данных, избегая обращения к базе данных через запросы по этой еще не существующей ссылке.

← На главную