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