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