При составлении внешних отчетов в 1С с использованием Системы Компоновки Данных (СКД), особенно когда требуется получить данные по себестоимости со счета 41.04 "Товары отгруженные" (или аналогичных субсчетов счета 41), часто возникают сложности. Разработчики сталкиваются с тем, что в консоли запросов данные выглядят корректно, но в отчете СКД суммы себестоимости оказываются завышенными или содержат странные значения. Давайте подробно разберем, почему это происходит и как правильно решить данную проблему, обеспечив точность ваших отчетов.
Наш читатель столкнулся с проблемой при формировании отчета для 1С:Бухгалтерии 2.0 на СКД. Основная сложность заключалась в корректном извлечении суммы себестоимости со счета 41.04. В исходном запросе, предназначенном для анализа продаж и связанной с ними себестоимости, была попытка соединить данные о продажах (Sales) с данными о себестоимости (PrimeCost).
Посмотрим на часть исходного запроса, касающуюся получения данных о продажах и себестоимости:
ВЫБРАТЬ
Sales.Номенклатура КАК Номенклатура,
Sales.Контрагент КАК Покупатель,
Sales.ДоговорКонтрагента КАК ДоговорКонтрагента,
Sales.Количество КАК Количество,
Sales.Сумма КАК Сумма,
Sales.Цена КАК Цена,
Sales.Регистратор КАК Регистратор,
ВЫРАЗИТЬ(Sales.Сумма - Sales.Количество * PrimeCost.ЦенаСебест КАК ЧИСЛО(15, 2)) КАК Др,
ВЫБОР КОГДА Sales.Количество * PrimeCost.ЦенаСебест = 0 ТОГДА 0 ИНАЧЕ ВЫРАЗИТЬ(Sales.Сумма * 100 / (Sales.Количество * PrimeCost.ЦенаСебест) - 100 КАК ЧИСЛО(15, 2)) КОНЕЦ КАК GP1_Percent1,
PrimeCost.ЦенаСебест КАК ЦенаСебест,
ВЫРАЗИТЬ(Sales.Количество * PrimeCost.ЦенаСебест КАК ЧИСЛО(15, 2)) КАК COGS, // Поле себестоимости
Sales.СуммаБезНдс КАК Net_Sales_YTD
ИЗ
(ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СубконтоКт3 КАК Номенклатура, // Номенклатура из 90.01
ХозрасчетныйОборотыДтКт.СубконтоДт1 КАК Контрагент,
ХозрасчетныйОборотыДтКт.СубконтоДт2 КАК ДоговорКонтрагента,
ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК Количество,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК Сумма,
ХозрасчетныйОборотыДтКт.СуммаНУОборотКт КАК СуммаБезНдс,
ВЫБОР КОГДА ХозрасчетныйОборотыДтКт.КоличествоОборотКт = 0 ТОГДА 0 ИНАЧЕ ВЫРАЗИТЬ(ЕСТЬNULL(ХозрасчетныйОборотыДтКт.СуммаОборот, 0) / ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК ЧИСЛО(15, 2)) КОНЕЦ КАК Цена,
ХозрасчетныйОборотыДтКт.Регистратор КАК Регистратор
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(&НачПер, &КонПер, Регистратор, , , СчетКт = &Сч9001, , Организация = &Организация) КАК ХозрасчетныйОборотыДтКт
) КАК Sales
ЛЕВОЕ СОЕДИНЕНИЕ
(ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК Количество,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК Сумма,
ХозрасчетныйОборотыДтКт.СубконтоКт1 КАК Номенклатура, // Номенклатура из счетов учета запасов
ВЫБОР КОГДА ХозрасчетныйОборотыДтКт.КоличествоОборотКт = 0 ТОГДА 0 ИНАЧЕ ХозрасчетныйОборотыДтКт.СуммаОборот / ХозрасчетныйОборотыДтКт.КоличествоОборотКт КОНЕЦ КАК ЦенаСебест
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт( &НачПер, &КонПер, Период, СчетДт = &Сч9002 ИЛИ СчетДт = &Сч9102, , СчетКт = &Сч43 ИЛИ СчетКт = &Сч4101 ИЛИ СчетКт = &Сч4104 ИЛИ СчетКт = &Сч10, , Организация = &Организация) КАК ХозрасчетныйОборотыДтКт
) КАК PrimeCost
ПО Sales.Номенклатура = PrimeCost.Номенклатура
Проблема проявлялась в том, что поле COGS (себестоимость) либо содержало пустые значения, либо некорректные, многократно завышенные суммы. При этом данные о продажах (таблица Sales) из счета 90.01 выводились отлично. В консоли запросов, при отдельной проверке, суммы могли быть верными, но в СКД возникала "окалесица" – суммы в несколько раз больше.
Чтобы понять, почему данные себестоимости задваиваются или отображаются неверно, давайте рассмотрим основные причины, выявленные в ходе обсуждения:
JOIN): Это одна из наиболее распространенных причин задвоения данных в СКД. Если счет 41.04 имеет несколько субконто (например, "Номенклатура", "Партия", "Склад"), а условие соединения с другими таблицами (например, таблицей продаж) выполняется только по одному субконто (например, "Номенклатура"), то для каждой проданной номенклатуры могут быть найдены все ее партии и склады. Это приведет к многократному увеличению сумм при агрегации в отчете.РегистрБухгалтерии.Хозрасчетный.Обороты или ОборотыДтКт могут возвращать несколько строк для одной и той же номенклатуры, если были разные движения (например, приход и расход в рамках одного периода по разным партиям или складам). Если эти данные соединяются с таблицей продаж без учета всех разрезов, это может привести к дублированию.СуммаОборот) настроены на простое "Суммирование", но при этом не учтены все необходимые группировки или неверно определены связи между наборами данных, агрегация может произойти неправильно, суммируя задвоенные строки.Регистратор: В виртуальных таблицах оборотов Регистратор часто используется как параметр периодичности (например, `РегистраторБухгалтерии.Хозрасчетный.Обороты(..., Регистратор, ...)`, что позволяет получить движения по каждому документу-регистратору), а не как поле для прямой выборки без указания этого параметра в периодичности. Если вам нужен сам объект Регистратор в результате, его необходимо явно указать в списке выбираемых полей, когда он используется как параметр периодичности.PrimeCost. Если подзапрос PrimeCost не выбирает все необходимые субконто для соединения (например, только СубконтоКт1 КАК Номенклатура), это не позволит выполнить полное и точное соединение.Прежде чем приступать к написанию запроса, критически важно понять, какие субконто используются для счета 41.04 в вашей конфигурации 1С. Состав субконто может различаться, и от него зависит правильность условий соединения.
Запомним эти субконто, так как они будут использоваться в условиях соединения нашего запроса. Критически важно, чтобы субконто в вашей выборке себестоимости совпадали с теми, что используются для связи с продажами.
Себестоимость проданных товаров обычно отражается по дебету счета 90.02 "Себестоимость продаж" в корреспонденции с кредитом счета 41.04. Для получения этих данных мы будем использовать виртуальную таблицу РегистрБухгалтерии.Хозрасчетный.Обороты.
Рассмотрим пример запроса, который позволяет вытащить себестоимость по счету 41.04 с учетом регистратора и всех субконто:
ВЫБРАТЬ
ХозрасчетныйОбороты.Регистратор КАК РегистраторСебестоимости,
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК НоменклатураСебестоимости,
ХозрасчетныйОбороты.Субконто2 КАК ПартияСебестоимости,
ХозрасчетныйОбороты.Субконто3 КАК СкладСебестоимости,
ХозрасчетныйОбороты.КорСчет КАК КорСчет,
-ХозрасчетныйОбороты.СуммаОборот КАК СуммаСебестоимости,
ХозрасчетныйОбороты.КоличествоОборот КАК КоличествоСебестоимости
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачПер,
&КонПер,
Регистратор, // Периодичность по регистратору, чтобы получить детализацию до документа
Счет В ИЕРАРХИИ (&Сч4104), // Отбор по счету 41.04 и его субсчетам
, // Без отбора по субконто счета 41.04 - выбираем все субконто
Организация = &Организация,
КорСчет В ИЕРАРХИИ (&Сч9002), // Отбор по корреспондирующему счету 90.02
// Без отбора по субконто корреспондирующего счета
) КАК ХозрасчетныйОбороты
Пояснения к запросу:
РегистраторСебестоимости: Мы явно указываем Регистратор в списке выбираемых полей. Это позволяет получить сам документ-регистратор движения, что критически важно для дальнейшего точного соединения с продажами.Счет В ИЕРАРХИИ (&Сч4104): Этот параметр отбирает движения по счету 41.04 и всем его субсчетам (например, 41.04.1, 41.04.2), обеспечивая полный охват.КорСчет В ИЕРАРХИИ (&Сч9002): Мы ищем проводки по кредиту 41.04, которые корреспондируют с дебетом счета 90.02 "Себестоимость продаж". Это стандартная проводка для отражения себестоимости реализованных товаров.-ХозрасчетныйОбороты.СуммаОборот КАК СуммаСебестоимости: Так как себестоимость по счету 41.04 отражается по кредиту (уменьшая остаток товаров), а СуммаОборот в таблице Обороты обычно отражает общие обороты, для получения положительного значения себестоимости (как расхода) мы используем отрицательный знак. Важно: убедитесь, что в вашей конфигурации СуммаОборот возвращает корректное значение для дебета/кредита с учетом направления. Если нет, то используйте СуммаОборотКт, который отражает оборот по кредиту.Субконто1, Субконто2, Субконто3: Выбираем все доступные субконто для счета 41.04. Это крайне важно для корректного соединения на следующем шаге. Мы переименовали их, чтобы избежать конфликтов имен при соединении.Рекомендация: Всегда тестируйте этот отдельный запрос в консоли запросов 1С. Убедитесь, что он возвращает ожидаемые данные по себестоимости, соответствующей движениям по счету 41.04 в корреспонденции с 90.02, и что детализация по регистратору и субконто верна.
Теперь, когда у нас есть корректные данные по себестоимости, необходимо правильно соединить их с данными о продажах. Основная проблема задвоения возникает именно здесь из-за неполных условий соединения.
В исходном запросе автора (Сообщение 1) и в попытке исправления (Сообщение 34) соединение происходило по одному полю: Sales.Номенклатура = PrimeCost.Номенклатура или PrimeCost.Номенклатура = ХозрасчетныйОбороты.Субконто1. Этого недостаточно, если у счета 41.04 есть несколько субконто (Номенклатура, Партия, Склад) или если продажи могут быть по одной номенклатуре, но из разных партий/складов в одном документе. Для каждой продажи номенклатуры система может найти несколько записей себестоимости (например, по разным партиям или складам), что и приводит к многократному завышению сумм.
Мы должны соединять данные по всем значимым субконто и по регистратору, чтобы получить уникальную связь.
Сначала мы модифицируем подзапрос PrimeCost из исходного запроса автора так, чтобы он возвращал все необходимые субконто и регистратор, если они используются для счета 41.04.
Пример модификации второго подзапроса (PrimeCost) из Сообщения 1:
// ... ваш первый подзапрос Sales без изменений
ЛЕВОЕ СОЕДИНЕНИЕ
(ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК Количество,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК Сумма,
ХозрасчетныйОборотыДтКт.СубконтоКт1 КАК Номенклатура,
ХозрасчетныйОборотыДтКт.СубконтоКт2 КАК Партия, // Добавляем Партию
ХозрасчетныйОборотыДтКт.СубконтоКт3 КАК Склад, // Добавляем Склад
ХозрасчетныйОборотыДтКт.Регистратор КАК РегистраторСебестоимости, // Добавляем Регистратор
ВЫБОР КОГДА ХозрасчетныйОборотыДтКт.КоличествоОборотКт = 0 ТОГДА 0 ИНАЧЕ ХозрасчетныйОборотыДтКт.СуммаОборот / ХозрасчетныйОборотыДтКт.КоличествоОборотКт КОНЕЦ КАК ЦенаСебест
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(
&НачПер,
&КонПер,
Регистратор, // Важно указать периодичность для получения Регистратора
СчетДт = &Сч9002 ИЛИ СчетДт = &Сч9102, // Себестоимость списания на 90.02/91.02
, // Без отбора по субконто дебета
СчетКт = &Сч43 ИЛИ СчетКт = &Сч4101 ИЛИ СчетКт = &Сч4104 ИЛИ СчетКт = &Сч10, // Списание с 43, 41.01, 41.04, 10
, // Без отбора по субконто кредита
Организация = &Организация
) КАК ХозрасчетныйОборотыДтКт
) КАК PrimeCost
ПО Sales.Номенклатура = PrimeCost.Номенклатура
И Sales.Регистратор = PrimeCost.РегистраторСебестоимости // Соединяем по регистратору
// Если в Sales есть поля для Партии и Склада (что редкость для табличной части документа реализации)
// И Sales.Партия = PrimeCost.Партия
// И Sales.Склад = PrimeCost.Склад
Важно: Обратите внимание на условие соединения. Если в вашем подзапросе Sales есть детализация по партии и складу (что обычно не так для строки реализации), то необходимо добавить эти поля в условие ПО. Однако чаще всего Sales детализирован только до Номенклатуры и Регистратора. В таком случае, если себестоимость учитывается по партиям/складам, предыдущий запрос может все еще приводить к задвоению, так как для одной строки продажи (Номенклатура, Регистратор) может быть несколько партий/складов себестоимости. В этом сценарии лучше использовать агрегацию через временные таблицы.
Этот подход часто является более надежным для СКД, особенно когда исходные источники данных имеют разные уровни детализации или требуется сложная агрегация. Он позволяет контролировать уровень детализации себестоимости перед ее соединением с продажами.
ВТ_Продажи). Собираем все необходимые данные о продажах с их номенклатурой, количеством, суммой и регистратором. Это ваш подзапрос Sales.ВТ_Себестоимость). Используем запрос из Шага 2, но агрегируем данные по Номенклатуре и Регистратору. Это позволит получить одну агрегированную запись себестоимости на каждую продажу номенклатуры по конкретному документу, даже если внутри документа себестоимость списывалась по разным партиям или складам.
Пример агрегации себестоимости:
ВЫБРАТЬ
ХозрасчетныйОбороты.Регистратор КАК РегистраторСебестоимости,
ХозрасчетныйОбороты.Субконто1 КАК НоменклатураСебестоимости,
СУММА(-ХозрасчетныйОбороты.СуммаОборот) КАК СуммаСебестоимости,
СУММА(ХозрасчетныйОбороты.КоличествоОборот) КАК КоличествоСебестоимости
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачПер, &КонПер, Регистратор,
Счет В ИЕРАРХИИ (&Сч4104), , Организация = &Организация,
КорСчет В ИЕРАРХИИ (&Сч9002)
) КАК ХозрасчетныйОбороты
СГРУППИРОВАТЬ ПО
ХозрасчетныйОбороты.Регистратор,
ХозрасчетныйОбороты.Субконто1
Этот запрос позволит получить агрегированную себестоимость по Регистратору и Номенклатуре, что упрощает дальнейшее соединение и минимизирует риски задвоения.
ВТ_Продажи с ВТ_Себестоимость по Номенклатуре и Регистратору.
Полный запрос с использованием временных таблиц будет выглядеть так:
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СубконтоКт3 КАК Номенклатура,
ХозрасчетныйОборотыДтКт.СубконтоДт1 КАК Контрагент,
ХозрасчетныйОборотыДтКт.СубконтоДт2 КАК ДоговорКонтрагента,
ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК Количество,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК Сумма,
ХозрасчетныйОборотыДтКт.СуммаНУОборотКт КАК СуммаБезНдс,
ВЫБОР КОГДА ХозрасчетныйОборотыДтКт.КоличествоОборотКт = 0 ТОГДА 0 ИНАЧЕ ВЫРАЗИТЬ(ЕСТЬNULL(ХозрасчетныйОборотыДтКт.СуммаОборот, 0) / ХозрасчетныйОборотыДтКт.КоличествоОборотКт КАК ЧИСЛО(15, 2)) КОНЕЦ КАК Цена,
ХозрасчетныйОборотыДтКт.Регистратор КАК Регистратор
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(&НачПер, &КонПер, Регистратор, , , СчетКт = &Сч9001, , Организация = &Организация) КАК ХозрасчетныйОборотыДтКт;
ВЫБРАТЬ
ХозрасчетныйОбороты.Регистратор КАК РегистраторСебестоимости,
ХозрасчетныйОбороты.Субконто1 КАК НоменклатураСебестоимости,
СУММА(-ХозрасчетныйОбороты.СуммаОборот) КАК СуммаСебестоимости,
СУММА(ХозрасчетныйОбороты.КоличествоОборот) КАК КоличествоСебестоимости
ПОМЕСТИТЬ ВТ_Себестоимость
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачПер, &КонПер, Регистратор,
Счет В ИЕРАРХИИ (&Сч4104), , Организация = &Организация,
КорСчет В ИЕРАРХИИ (&Сч9002)
) КАК ХозрасчетныйОбороты
СГРУППИРОВАТЬ ПО
ХозрасчетныйОбороты.Регистратор,
ХозрасчетныйОбороты.Субконто1;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Продажи.Номенклатура КАК Номенклатура,
ВТ_Продажи.Контрагент КАК Покупатель,
ВТ_Продажи.ДоговорКонтрагента КАК ДоговорКонтрагента,
ВТ_Продажи.Количество КАК Количество,
ВТ_Продажи.Сумма КАК Сумма,
ВТ_Продажи.Цена КАК Цена,
ВТ_Продажи.Регистратор КАК Регистратор,
ВТ_Продажи.СуммаБезНдс КАК Net_Sales_YTD,
ЕСТЬNULL(ВТ_Себестоимость.СуммаСебестоимости, 0) КАК COGS // Агрегированная себестоимость
ИЗ
ВТ_Продажи КАК ВТ_Продажи
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Себестоимость КАК ВТ_Себестоимость
ПО ВТ_Продажи.Номенклатура = ВТ_Себестоимость.НоменклатураСебестоимости
И ВТ_Продажи.Регистратор = ВТ_Себестоимость.РегистраторСебестоимости
Этот подход гарантирует, что для каждой строки продажи будет найдена либо одна агрегированная сумма себестоимости, либо 0, если себестоимость не найдена, что исключает задвоение.
Когда вы сталкиваетесь с некорректными данными в СКД, систематический подход к отладке поможет быстро выявить проблему:
Номенклатуру, КоличествоОборот и СуммаОборот с корректными отборами по Счет и КорСчет. Убедитесь, что этот запрос работает корректно в консоли запросов 1С.ХозрасчетныйОбороты.СуммаОборот из разных подзапросов, СуммаОборотДт, СуммаОборотКт) и выводить их в отчет без агрегации. Это позволит увидеть сырые значения до агрегации и понять, какие данные приходят из каждого источника и на каком этапе они начинают "задваиваться".Сумма). Также проверьте, что все необходимые поля включены в группировки отчета, если вы ожидаете детализированные данные. Неправильные группировки или их отсутствие при агрегированных ресурсах могут приводить к неверным итогам.ПО в запросе, в СКД есть вкладка "Связи наборов данных", где также указываются связи между наборами данных. Убедитесь, что они корректны и не дублируют или не конфликтуют с условиями вашего запроса.ЛЕВОЕ СОЕДИНЕНИЕ: Используйте ЛЕВОЕ СОЕДИНЕНИЕ для соединения продаж с себестоимостью (ВТ_Продажи ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Себестоимость). Это гарантирует, что все строки продаж будут включены в отчет, даже если для какой-то номенклатуры не найдена себестоимость (например, из-за неправильных проводок, отсутствия списания или других учетных особенностей).&НачПер, &КонПер, &Организация, &Сч9001, &Сч9002, &Сч4104 и другие) корректно передаются из настроек отчета в запрос СКД. Иногда проблема может быть в неверных значениях параметров.Следуя этим шагам, вы сможете точно определить источник проблемы и построить корректный отчет по себестоимости товаров со счета 41.04 в 1С:Бухгалтерии 2.0.