В процессе разработки в среде 1С:Предприятие часто возникает задача получения данных из документа, который был создан «на основании» текущего. Например, нам нужно из «Заказа покупателя» узнать номер или дату «Реализации», которая была по нему создана. Если прямая ссылка в реквизитах отсутствует, задача может показаться сложной. В этой статье мы подробно разберем, как выстроить запрос для получения этих данных, рассмотрим различные сценарии хранения связей и научимся использовать механизмы платформы для поиска «потерянных» ссылок.
Это самый простой и классический случай. В большинстве типовых и самописных конфигураций у подчиненного документа (назовем его Документ2) есть реквизит в шапке, который хранит ссылку на ведущий документ (Документ1). Обычно этот реквизит называется ДокументОснование или просто Основание.
Проанализируем ситуацию: нам нужно вытащить данные Документа2, зная только Документ1. Поскольку ссылка хранится в подчиненном объекте, мы будем использовать Левое соединение или условие в блоке ГДЕ, обращаясь к таблице подчиненного документа.
Рассмотрим пример запроса:
ВЫБРАТЬ
Док2.Ссылка КАК СсылкаНаПодчиненный,
Док2.Дата КАК ДатаПодчиненного,
Док2.СуммаДокумента КАК Сумма
ИЗ
Документ.ТипДокумента2 КАК Док2
ГДЕ
Док2.ДокументОснование = &ВыбДокумент1
В данном примере мы ищем все записи в таблице Документ2, у которых в поле ДокументОснование записана ссылка на наш исходный документ. Это наиболее эффективный способ с точки зрения производительности базы данных.
Бывают ситуации, когда один документ может быть создан на основании нескольких других (например, «Поступление товаров» на основании нескольких «Заказов поставщику»). В этом случае ссылка на основание переносится не в шапку, а в табличную часть подчиненного документа.
Разберем по шагам, как составить запрос в этом случае. Предположим, ссылка хранится в табличной части Товары в реквизите Заказ. Нам нужно обратиться именно к таблице этой части:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТаблицаТовары.Ссылка КАК Документ2
ИЗ
Документ.ТипДокумента2.Товары КАК ТаблицаТовары
ГДЕ
ТаблицаТовары.Заказ = &ВыбДокумент1
Важный момент: использование ключевого слова РАЗЛИЧНЫЕ обязательно, так как если в одном документе Документ2 несколько строк ссылаются на один и тот же Документ1, без этого слова система вернет дубликаты ссылок на основной документ.
Если мы не знаем точно, в каком реквизите хранится связь, или если подчиненных документов может быть много разных типов, на помощь приходит механизм платформы — Критерии подчиненности. В конфигураторе почти всегда есть объект, называемый СвязанныеДокументы.
Проанализируем, как это работает программно. Если нам нужно быстро найти все связанные объекты в коде, мы можем использовать следующий подход:
// Получаем структуру критерия подчиненности
ВыбранноеОснование = Объект.Ссылка; // Наш Документ1
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| СвязанныеДокументы.Ссылка
|ИЗ
| КритерийПодчиненности.СвязанныеДокументы(&Основание) КАК СвязанныеДокументы";
Запрос.УстановитьПараметр("Основание", ВыбранноеОснование);
Результат = Запрос.Выполнить().Выгрузить();
Этот метод универсален, так как критерий подчиненности автоматически «знает» обо всех реквизитах во всех документах, которые помечены как входящие в этот критерий.
Если запрос не возвращает данных, хотя кнопка «Создать на основании» работает, выясним причину в коде. Нам необходимо открыть модуль объекта Документ2 и найти процедуру ОбработкаЗаполнения.
Рассмотрим типовой код внутри этой процедуры:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.ТипДокумента1") Тогда
// Здесь происходит копирование данных
ЭтотОбъект.Контрагент = ДанныеЗаполнения.Контрагент;
ЭтотОбъект.Организация = ДанныеЗаполнения.Организация;
// ВНИМАНИЕ: Если здесь нет строки записи ссылки, связь в БД не сохранится!
ЭтотОбъект.ДокументОснование = ДанныеЗаполнения;
КонецЕсли;
КонецПроцедуры
Если вы обнаружили, что данные просто копируются (например, Сумма = ДанныеЗаполнения.Сумма), но сама ссылка ДанныеЗаполнения никуда не записывается, то вытащить данные запросом в будущем будет невозможно. В таком случае необходимо добавить новый реквизит в Документ2 и обеспечить его заполнение.
Если структура базы крайне сложная, и вы не можете найти, где именно «спряталась» ссылка, воспользуемся методом НайтиСсылки(). Это мощный инструмент, который сканирует все таблицы базы данных на предмет упоминания конкретного объекта.
Посмотрим на пример использования:
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(Объект.Ссылка);
ТаблицаСсылок = НайтиСсылки(МассивСсылок);
Для Каждого Строка Из ТаблицаСсылок Цикл
Сообщить("Найдена связь в объекте: " + Строка.Данные);
Сообщить("Место нахождения: " + Строка.Метаданные.ПолноеИмя());
КонецЦикла;
Этот метод поможет вам точно определить имя реквизита или табличной части, где хранится связь, после чего вы сможете составить корректный и быстрый запрос, как было показано в предыдущих шагах.
Индексировать. Это значительно ускорит работу запроса на больших базах.СвязиДокументов или общая табличная часть в некоторых типах объектов. Если стандартные реквизиты пусты, проанализируйте движения документа в этом регистре.Таким образом, для успешного решения задачи необходимо сначала определить, сохраняется ли ссылка в базе физически, и если да — использовать либо прямой запрос к шапке/табличной части, либо универсальный механизм критериев подчиненности.