В практике разработчика 1С часто возникает ситуация, когда необходимо сформировать печатную форму или выполнить какую-либо обработку данных для еще не записанного объекта. Казалось бы, платформа предоставляет инструменты для работы с будущей ссылкой, такие как УстановитьСсылкуНового(), однако при попытке использовать эту ссылку в запросе разработчик сталкивается с тем, что объект оказывается «пустым». Разберем подробно, почему так происходит, как работают механизмы платформы «внутри» и какими способами можно решить эту задачу.
Рассмотрим ситуацию, описанную участником форума. Программист создает новый элемент справочника программно, заполняет его реквизиты и хочет получить ссылку до фактического вызова метода Записать(). Для этого используется стандартный механизм:
СправочникОбъект = Справочники.ИмяСправочника.СоздатьЭлемент();
// Заполнение реквизитов
СправочникОбъект.Наименование = "Тестовый элемент";
// Получение будущей ссылки
СсылкаНового = Справочники.ИмяСправочника.ПолучитьСсылку();
СправочникОбъект.УстановитьСсылкуНового(СсылкаНового);
После выполнения этого кода СправочникОбъект.Ссылка действительно перестает быть пустой. Однако при передаче этой ссылки в запрос или в модуль печати, который использует запросы, результат оказывается нулевым. Все реквизиты по этой ссылке в базе данных — неопределены. Проанализируем, в чем кроется причина такого поведения системы.
Важно понимать архитектурное различие между объектом (CatalogObject) и ссылкой (CatalogRef). Объект — это данные, которые в данный момент находятся в оперативной памяти сервера или клиента. Вы можете менять их, заполнять реквизиты и табличные части. Ссылка — это всего лишь уникальный идентификатор (GUID), адрес записи в таблице базы данных (СУБД).
Когда мы используем метод УстановитьСсылкуНового(), мы просто говорим платформе: «Когда этот объект будет записываться, используй вот этот конкретный GUID». Но сама запись в таблицах SQL появится только в момент выполнения метода Записать().
Запросы (Query) в 1С работают напрямую с таблицами базы данных (через SQL-запросы к СУБД). Они ничего не знают о том, что происходит в оперативной памяти вашего сеанса. Поэтому, когда запрос пытается найти данные по вашей «новой» ссылке, он обращается к таблице справочника, не находит там записи с таким GUID и возвращает пустоту (NULL). Это фундаментальный принцип работы клиент-серверной архитектуры.
Как справедливо заметили участники обсуждения, в типовых конфигурациях печать большинства документов и справочников требует предварительной записи. Проанализируем ситуацию: чаще всего логика формирования данных печатной формы вынесена в Модуль менеджера.
В модуль менеджера передается массив ссылок. Менеджер объекта строит запрос к базе данных, чтобы быстро получить данные всех необходимых полей. Поскольку незаписанного объекта в базе нет, менеджер просто не может получить его данные. Именно поэтому попытка «обмануть» систему подменой ссылки на незаписанную не срабатывает.
Если вам критически важно сформировать данные без финальной записи объекта в базу, рассмотрим несколько подходов.
Если вы не можете изменить логику запроса в печатной форме, вы можете передать данные объекта в запрос через временную таблицу. Это позволит «подсунуть» данные из оперативной памяти в механизм запросов. Проанализируем примерную схему:
ТаблицаЗначений с колонками, идентичными реквизитам справочника.СправочникОбъект.ВЫБРАТЬ * ПОМЕСТИТЬ ВтДанные ИЗ &ТЗ КАК ТЗ.Это самый правильный и «чистый» путь. Вместо того чтобы передавать в функцию печати только Ссылку, научим её принимать ДанныеЗаполнения. Это может быть либо сам объект, либо структура с его реквизитами.
Рассмотрим пример кода в функции формирования печатной формы:
Функция СформироватьПечатнуюФорму(СсылкаИлиОбъект)
Если ТипЗнч(СсылкаИлиОбъект) = Тип("СправочникОбъект.ИмяСправочника") Тогда
// Берем данные напрямую из объекта
ДанныеДляПечати = ПолучитьДанныеИзОбъекта(СсылкаИлиОбъект);
Иначе
// Объект уже в базе, используем стандартный запрос по ссылке
ДанныеДляПечати = ПолучитьДанныеЗапросом(СсылкаИлиОбъект);
КонецЕсли;
// ... дальнейшая логика вывода в табдок
КонецФункции
В исключительных случаях, если переписывание кода невозможно, программисты прибегают к использованию транзакций. Выясним причину популярности и риски этого метода:
НачатьТранзакцию().Объект.Записать()). Теперь он физически есть в БД, и запросы его видят.ОтменитьТранзакцию().Важно: При отмене транзакции запись из базы данных исчезнет, но сформированный табличный документ останется в памяти. Внимание! Этот метод крайне не рекомендуется для сложных систем, так как запись объекта может инициировать подписки на события, записи в регистры и другие побочные эффекты, которые могут некорректно откатиться или вызвать блокировки таблиц.
Попытка использовать УстановитьСсылкуНового() для работы с запросами — это заблуждение относительно механизмов работы платформы. Ссылка — это ключ, а не сами данные. Если вам нужно работать с объектом до записи, вы должны передавать в алгоритмы обработки сам объект или структуру его данных, избегая обращения к базе данных через запросы по этой еще не существующей ссылке.