При разработке отчетов по валовой прибыли в конфигурации «1С:Управление торговлей 10.3» (УТ 10.3) часто возникает сложная архитектурная задача. Нам необходимо не просто посчитать общую прибыль, а разделить ее по источнику происхождения товара. Например, выделить в отдельные категории продажи товаров, которые были собраны нами самостоятельно (документом оприходования в партии выступает КомплектацияНоменклатуры), и обычную перепродажу покупных товаров.
Для решения этой задачи необходимо связать данные о выручке из регистра накопления Продажи и данные о себестоимости из регистра ПартииТоваровНаСкладах. В этой статье мы подробно разберем, почему простое соединение этих таблиц «в лоб» приводит к критическим ошибкам в расчетах, и рассмотрим правильные методики реализации этой задачи с помощью запросов и Системы компоновки данных (СКД).
Давайте проанализируем структуру данных обоих регистров. Регистр Продажи фиксирует факт отгрузки товара и сумму выручки. Регистр ПартииТоваровНаСкладах фиксирует списание себестоимости по конкретным партиям (документам оприходования).
Попытка соединить эти таблицы в запросе по полям Регистратор (документ реализации) и Номенклатура напрямую приводит к так называемому декартову произведению (задвоению строк). Рассмотрим простой пример:
Если мы соединим эти таблицы напрямую по номенклатуре и регистратору, то в результате запроса мы получим две строки. Но самое опасное — в каждой из этих строк сумма выручки (10 000 рублей) и общее количество продаж (10 штук) продублируются. Итоговая сумма выручки по отчету превратится в 20 000 рублей вместо реальных 10 000 рублей. Если же в документе продажи товар был разбит на несколько строк с разной ценой, ситуация с искажением данных станет еще более запутанной.
Чтобы избежать искажения сумм и количества, мы должны предварительно сгруппировать данные в независимых временных таблицах (ВТ) до уникального ключа Регистратор + Номенклатура + ХарактеристикаНоменклатуры, а затем соединить их и применить математическое распределение выручки пропорционально списанному количеству.
Разберем этот алгоритм по шагам.
Сначала выберем данные о продажах. Нам необходимо сгруппировать показатели выручки и количества продаж до уровня уникального товара в документе:
ВЫБРАТЬ
ПродажиОбороты.Регистратор КАК Регистратор,
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
СУММА(ПродажиОбороты.КоличествоОборот) КАК КоличествоПродажи,
СУММА(ПродажиОбороты.СтоимостьОборот) КАК СуммаВыручки
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ПродажиОбороты
ГРУППИРОВАТЬ ПО
ПродажиОбороты.Регистратор,
ПродажиОбороты.Номенклатура,
ПродажиОбороты.ХарактеристикаНоменклатуры
;
Теперь выберем расходные движения по партиям. Поскольку наша задача — разделить продажи на «Комплектацию» и «Прочее», мы добавим вычисляемое поле-признак ЭтоКомплектация на основе типа документа оприходования. Группировка по этому признаку позволит нам получить максимум две строки по одному товару в документе:
ВЫБРАТЬ
ПартииТоваров.Регистратор КАК Регистратор,
ПартииТоваров.Номенклатура КАК Номенклатура,
ПартииТоваров.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
ВЫБОР
КОГДА ПартииТоваров.ДокументОприходования ССЫЛКА Документ.КомплектацияНоменклатуры
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК ЭтоКомплектация,
СУММА(ПартииТоваров.Количество) КАК КоличествоСписано,
СУММА(ПартииТоваров.Стоимость) КАК Себестоимость
ПОМЕСТИТЬ ВТ_Себестоимость
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваров
ГДЕ
ПартииТоваров.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
И ПартииТоваров.Период МЕЖДУ &НачалоПериода И &КонецПериода
ГРУППИРОВАТЬ ПО
ПартииТоваров.Регистратор,
ПартииТоваров.Номенклатура,
ПартииТоваров.ХарактеристикаНоменклатуры,
ВЫБОР
КОГДА ПартииТоваров.ДокументОприходования ССЫЛКА Документ.КомплектацияНоменклатуры
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ
;
Теперь мы можем соединить наши таблицы. Обратите внимание: так как списание партий может разбивать одну продажу на две строки (часть товара произведена комплектацией, часть — закуплена), выручку по документу мы должны распределить **пропорционально количеству** списанного товара. Это исключит проблему разных цен в табличной части реализации.
Для распределения применим формулу:
РаспределеннаяВыручка = ОбщаяВыручка * (КоличествоСписаннойПартии / ОбщееКоличествоПродажи)
Посмотрим на итоговый запрос пакета:
ВЫБРАТЬ
ВТ_Себестоимость.Регистратор КАК Регистратор,
ВТ_Себестоимость.Номенклатура КАК Номенклатура,
ВТ_Себестоимость.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
ВТ_Себестоимость.ЭтоКомплектация КАК ЭтоКомплектация,
ВТ_Себестоимость.КоличествоСписано КАК Количество,
ВТ_Себестоимость.Себестоимость КАК Себестоимость,
// Пропорциональное распределение выручки:
ВЫБОР
КОГДА ЕСТЬNULL(ВТ_Продажи.КоличествоПродажи, 0) = 0
ТОГДА 0
ИНАЧЕ ЕСТЬNULL(ВТ_Продажи.СуммаВыручки, 0) * (ВТ_Себестоимость.КоличествоСписано / ВТ_Продажи.КоличествоПродажи)
КОНЕЦ КАК Выручка,
// Расчет чистой прибыли:
(ВЫБОР
КОГДА ЕСТЬNULL(ВТ_Продажи.КоличествоПродажи, 0) = 0
ТОГДА 0
ИНАЧЕ ЕСТЬNULL(ВТ_Продажи.СуммаВыручки, 0) * (ВТ_Себестоимость.КоличествоСписано / ВТ_Продажи.КоличествоПродажи)
КОНЕЦ) - ВТ_Себестоимость.Себестоимость КАК Прибыль
ИЗ
ВТ_Себестоимость КАК ВТ_Себестоимость
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
ПО ВТ_Себестоимость.Регистратор = ВТ_Продажи.Регистратор
И ВТ_Себестоимость.Номенклатура = ВТ_Продажи.Номенклатура
И ВТ_Себестоимость.ХарактеристикаНоменклатуры = ВТ_Продажи.ХарактеристикаНоменклатуры
Данный подход гарантирует абсолютную математическую точность. Все копейки выручки будут распределены корректно, а рентабельность по обоим направлениям деятельности компании (собственное производство/комплектация и обычная дистрибуция) посчитается верно.
В ходе обсуждения на форумах часто предлагают использовать вспомогательный регистр накопления ПродажиСебестоимость (сообщение 7 и 8). В УТ 10.3 этот регистр действительно хранит себестоимость в привязке к продажам, и в нем даже есть поле НомерСтроки, что теоретически позволяет связать его с продажами без сложных математических распределений.
Однако этот регистр не решает нашу задачу. Дело в том, что в регистре ПродажиСебестоимость отсутствуют измерения партионного учета, в частности — измерение ДокументОприходования. В нем содержатся только аналитики продаж: Номенклатура, Покупатель, Заказ, Документ продажи. Поскольку мы не можем получить из этого регистра информацию о том, была ли партия оприходована документом КомплектацияНоменклатуры, использование регистра ПартииТоваровНаСкладах является единственным рабочим решением.
Если вы разрабатываете отчет на СКД, задачу можно решить еще изящнее, переложив математику распределения на саму платформу 1С. Для этого мы можем спроектировать структуру отчета на основе **связи наборов данных**.
Разберем этот метод по шагам:
НаборДанныхПродажи), где выбираем данные из регистра Продажи.Обороты в разрезе полей Регистратор, Номенклатура и ХарактеристикаНоменклатуры.НаборДанныхСебестоимость), в котором выбираем расходные движения из регистра партий, вычисляем признак комплектации (поле ЭтоКомплектация) и группируем результат.НаборДанныхПродажи, в качестве приемника — НаборДанныхСебестоимость. Связываем их по полям:
Регистратор = РегистраторНоменклатура = НоменклатураХарактеристикаНоменклатуры = ХарактеристикаНоменклатурыКакое бы техническое решение вы ни выбрали — пакетный запрос или связку наборов в СКД — расчет себестоимости напрямую зависит от качества ведения учета в информационной базе.
Для корректной работы отчета критически важно соблюдение следующих условий:
ПартииТоваровНаСкладах такие строки вообще не запишутся до момента проведения поступления и восстановления последовательности, из-за чего отчет покажет завышенную прибыль.Таким образом, сочетание правильного группирующего запроса во временных таблицах и поддержание порядка в партионном учете позволит вам получить точный и быстродействующий инструмент для анализа прибыльности собственного производства и торговых операций компании.