Power BI, DAX, Консолидация, План-факт

Управленческая отчётность холдинга в Power BI

План‑факт ОПУ, консолидация с исключением внутригрупповых оборотов, деньги, долг и дебиторка группы из пяти компаний в одном отчёте Power BI.

Группа компаний, контрагенты и цифры выдуманы. Отчёт собран в Power BI Desktop 2.158.1177.0.

PBIX, 493 КБ, открывается в Power BI Desktop; PDF, 388 КБ, 5 страниц

Ключевые цифры
ПоказательЗначение
Консолидированная выручка, январь — сентябрь 20261 955,6 млн ₽
EBITDA к плану251,1 против 274,6 млн ₽ (−8,6 %)
Исключено внутригрупповой выручки1 134,3 млн ₽
Расхождение внутригрупповых оборотов, найденное отчётом2,4 млн ₽
Чистый долг / EBITDA на 30.09.20261,90 при ковенанте 3,0
Просроченная дебиторка свыше 90 дней31,1 млн ₽

Задача и для кого

Финансовому директору группы нужно каждый месяц видеть результат холдинга целиком и по каждой компании: где отклонение от плана и почему, сколько выручки и прибыли остаётся после исключения оборотов внутри группы, не расходятся ли взаимные обороты в учёте компаний, хватает ли денег и далеко ли до ковенанта банка, кто из покупателей задерживает оплату. Отчёт собран в Power BI на выдуманных данных группы «Новолесье» (январь 2025 — сентябрь 2026) и отвечает на эти вопросы на пяти страницах с общими срезами: год, месяц, компания и режим «Сумма юрлиц / Консолидированно».

Что внутри

  • Главное: шесть показателей с отклонением к плану и к прошлому году, выручка по месяцам, вклад компаний в EBITDA
  • План‑факт ОПУ: матрица с промежуточными итогами (валовая прибыль, EBITDA, EBIT, чистая прибыль) и цветом отклонений
  • Каскад EBITDA: от плана к факту через отклонения выручки, себестоимости, коммерческих и управленческих расходов
  • Консолидация: ОПУ по юрлицам, столбцы «Исключения» и «Консолидированно»
  • Сверка внутригрупповых оборотов по парам «продавец → покупатель» и месяцам, отчёт сам находит расхождение
  • Деньги: каскад остатка по видам деятельности, остатки по компаниям, кредиты по банкам
  • Долг: чистый долг / EBITDA за 12 месяцев против ковенанта 3,0
  • Дебиторка: корзины просрочки по компаниям, десять крупнейших должников, оборачиваемость по месяцам
  • Модель «звезда» из 6 таблиц фактов и 6 справочников, 154 меры DAX на русском языке

Допущения

Консолидация — исключением внутригрупповых оборотов: у каждого контрагента есть признак «внутригрупповой» и код компании группы, при консолидации вычитаются обороты между выбранными компаниями. Нереализованная прибыль в запасах не исключается: торговый дом продаёт продукцию завода в месяце закупки, запасов внутригрупповой продукции на конец месяца нет. Суммы без НДС, включая дебиторку. Налог на прибыль — 25 % от прибыли до налога каждого месяца, без отложенного налога. Дебиторка — только по внешним покупателям. План ОПУ детализирован до статьи и признака «внешний контрагент / компания группы». Проценты уплаченные отнесены к финансовой деятельности, заём управляющей компании дочерней — к инвестиционной у кредитора. EBITDA = валовая прибыль − коммерческие − управленческие расходы; чистый долг / EBITDA считается по EBITDA за последние 12 месяцев.

Данные

Данные выдуманы: компании, контрагенты и банки не существуют. Сгенерированы скриптом на Python с фиксированным начальным значением генератора: сначала годовой бюджет с сезонностью, затем факт — выручка из документов реализации покупателям, расходы от выручки и постоянных сумм, оплаты по отсрочкам, день за днём остатки денег, кредитные линии, проценты и налог. Контрольные цифры посчитаны отдельно из CSV и совпали с отчётом до рубля. В данные заложены: перевыполнение выручки торговым домом при снижении маржи, рост себестоимости завода с апреля 2026 года, недобор выручки инжиниринга и его кассовый разрыв в июле, закрытый займом управляющей компании, расхождение внутригрупповых оборотов в августе 2026 года и просроченный долг крупного заказчика.

Страницы отчёта

Экспорт из Power BI с настройками по умолчанию: 2026 год, по сентябрь, все компании, консолидированно. По щелчку — полный размер.

Главное: выручка 1 955,6 млн ₽ на уровне плана, EBITDA 251,1 млн ₽ — на 8,6 % ниже плана, но на 13,2 % выше прошлого года. Отвечает на вопрос, как группа идёт к годовому плану.
Главное: выручка 1 955,6 млн ₽ на уровне плана, EBITDA 251,1 млн ₽ — на 8,6 % ниже плана, но на 13,2 % выше прошлого года. Отвечает на вопрос, как группа идёт к годовому плану.
План‑факт ОПУ: где потеряна EBITDA. Выручка −12,7 и себестоимость −13,1 млн ₽ к плану; по компаниям видно, что торговый дом перевыполнил выручку на 6,1 % ценой маржи (−2,1 п. п.), а инжиниринг недобрал 15,8 % выручки.
План‑факт ОПУ: где потеряна EBITDA. Выручка −12,7 и себестоимость −13,1 млн ₽ к плану; по компаниям видно, что торговый дом перевыполнил выручку на 6,1 % ценой маржи (−2,1 п. п.), а инжиниринг недобрал 15,8 % выручки.
Консолидация: сумма юрлиц 3 089,9 млн ₽ выручки, исключения −1 134,3, консолидированно 1 955,6. Сверка показывает единственное расхождение: август 2026, завод → торговый дом, 2,4 млн ₽.
Консолидация: сумма юрлиц 3 089,9 млн ₽ выручки, исключения −1 134,3, консолидированно 1 955,6. Сверка показывает единственное расхождение: август 2026, завод → торговый дом, 2,4 млн ₽.
Деньги и долг: операционный поток 149,0 млн ₽ ушёл на инвестиции и обслуживание долга, остаток снизился со 167,2 до 106,7 млн ₽. Чистый долг / EBITDA 1,90 — запас до ковенанта 1,10.
Деньги и долг: операционный поток 149,0 млн ₽ ушёл на инвестиции и обслуживание долга, остаток снизился со 167,2 до 106,7 млн ₽. Чистый долг / EBITDA 1,90 — запас до ковенанта 1,10.
Дебиторка: 408,2 млн ₽, просрочено 24 %. Почти вся просрочка свыше 90 дней — 31,1 млн ₽ — у одного заказчика инжиниринга.
Дебиторка: 408,2 млн ₽, просрочено 24 %. Почти вся просрочка свыше 90 дней — 31,1 млн ₽ — у одного заказчика инжиниринга.

Модель данных

Таблицы модели
ТаблицаТипСтрокЧто в ней
Факт ОПУфакт2 562Доходы и расходы по месяцам, компаниям, статьям и контрагентам, январь 2025 — сентябрь 2026.
План ОПУфакт1 368Бюджет на 2025 и 2026 годы помесячно; внутригрупповые строки выделены, чтобы план тоже консолидировался.
ДДСфакт7 530Движение денег по дням: оплаты покупателей и поставщиков, зарплата, налоги, кредиты, займы внутри группы.
Остатки ДС на началофакт5Остаток денег каждой компании на 01.01.2025.
Кредитыфакт105Остаток банковских кредитов на конец месяца по компаниям и банкам.
Дебиторкафакт4 301Документы реализации внешним покупателям со сроком и датой оплаты; дебиторка считается на любую дату.
Календарьсправочник730Даты 2025–2026, помечен как таблица дат.
Компаниисправочник5Пять юрлиц группы.
Статьи ОПУсправочник24Раздел, группа, статья, порядок и знак.
Статьи ДДСсправочник18Вид деятельности: операционная, инвестиционная, финансовая.
Контрагентысправочник79Покупатели, поставщики, банки и компании группы с признаком «внутригрупповой».
Банкисправочник3Банки-кредиторы.

Связи

  • Календарь 1 → * Факт ОПУ, План ОПУ, ДДС, Кредиты по дате
  • Компании 1 → * Факт ОПУ, План ОПУ, ДДС, Остатки ДС на начало, Кредиты, Дебиторка по коду компании
  • Статьи ОПУ 1 → * Факт ОПУ, План ОПУ по коду статьи
  • Статьи ДДС 1 → * ДДС по коду статьи ДДС
  • Контрагенты 1 → * Факт ОПУ, План ОПУ, ДДС, Дебиторка по коду контрагента
  • Банки 1 → * Кредиты по коду банка
Вид «Модель»: звезда из таблиц фактов и справочников, связи один ко многим, служебные таблицы отдельно.
Вид «Модель»: звезда из таблиц фактов и справочников, связи один ко многим, служебные таблицы отдельно.

Меры DAX

Самые показательные меры из модели, код без правок. Всего в модели 154 меры на русском языке, разложенные по папкам.

ОПУ строка

Одна мера на всю матрицу ОПУ: по типу строки берёт раздел, статью или сумму всех разделов выше. Суммы хранятся со знаком, поэтому итоговые строки не нужно описывать формулами.

ОПУ строка =
VAR RowType = SELECTEDVALUE('Строки ОПУ'[Тип])
VAR RowKey = SELECTEDVALUE('Строки ОПУ'[Ключ])
VAR UpToSection = SELECTEDVALUE('Строки ОПУ'[До раздела])
RETURN
    SWITCH(
        RowType,
        "Раздел", CALCULATE([ОПУ], 'Статьи ОПУ'[Раздел] = RowKey),
        "Статья", CALCULATE([ОПУ], 'Статьи ОПУ'[Код статьи] = RowKey),
        "Итог", CALCULATE([ОПУ], 'Статьи ОПУ'[Порядок раздела] <= UpToSection)
    )

ОПУ внутригрупповые

Основа консолидации: обороты с компаниями, которые входят в текущий выбор. Для всей группы исключаются все внутригрупповые обороты, для одной компании — ничего, для двух — только взаимные.

ОПУ внутригрупповые =
VAR Selected = VALUES('Компании'[Код компании])
RETURN
    CALCULATE([ОПУ все строки], KEEPFILTERS(TREATAS(Selected, 'Контрагенты'[Компания группы])))

Выручка

Консолидированная выручка: режим из среза решает, вычитать ли внутригрупповые обороты; месяцы ограничены срезом «Месяц по».

Выручка = CALCULATE([ОПУ], 'Статьи ОПУ'[Раздел] = "Выручка")

ОПУ =
VAR MonthTo = [Последний месяц периода]
RETURN CALCULATE([ОПУ все периоды], KEEPFILTERS('Календарь'[Номер месяца] <= MonthTo))

ОПУ все периоды = IF([Режим консолидированно], [ОПУ все строки] - [ОПУ внутригрупповые], [ОПУ все строки])

ВГО у покупателя

Сверка внутригрупповых оборотов: в строке визуала продавец, а считать нужно учёт покупателя, поэтому TREATAS меняет местами компанию и контрагента.

ВГО у покупателя =
VAR MonthTo = [Последний месяц периода]
VAR Sellers = VALUES('Компании'[Код компании])
VAR Buyers = FILTER(VALUES('Контрагенты'[Компания группы]), NOT ISBLANK('Контрагенты'[Компания группы]))
RETURN
    CALCULATE(
        SUM('Факт ОПУ'[Сумма]),
        REMOVEFILTERS('Компании'),
        REMOVEFILTERS('Контрагенты'),
        TREATAS(Buyers, 'Компании'[Код компании]),
        TREATAS(Sellers, 'Контрагенты'[Компания группы]),
        'Статьи ОПУ'[Знак] = -1,
        KEEPFILTERS('Календарь'[Номер месяца] <= MonthTo)
    )

Остаток ДС на конец

Полуаддитивная мера: остаток на начало плюс весь поток до конца периода. Остатки по месяцам нельзя складывать, поэтому период задаёт дату, а не сумму.

Остаток ДС на конец =
VAR PeriodEnd = [Конец периода]
RETURN
    IF(
        MIN('Календарь'[Дата]) <= PeriodEnd,
        SUM('Остатки ДС на начало'[Сумма]) + CALCULATE(SUM('ДДС'[Сумма]), 'Календарь'[Дата] <= PeriodEnd)
    )

EBITDA за 12 месяцев

Скользящая EBITDA для ковенанта через DATESINPERIOD. Пока данных меньше чем за год, мера пустая, а не завышенная.

EBITDA за 12 месяцев =
VAR PeriodEnd = [Конец периода]
VAR IsFullYear = EDATE(PeriodEnd, -12) + 1 >= [Первая дата факта]
RETURN
    IF(IsFullYear && MIN('Календарь'[Дата]) <= PeriodEnd,
        CALCULATE([EBITDA все периоды], DATESINPERIOD('Календарь'[Дата], PeriodEnd, -12, MONTH)))

Чистый долг / EBITDA

Показатель ковенанта: кредиты минус деньги на конец периода к EBITDA за 12 месяцев.

Чистый долг / EBITDA = DIVIDE([Чистый долг], [EBITDA за 12 месяцев])

Дебиторка по корзине

Дебиторка на конец любого периода по реестру документов: дни просрочки считаются от срока оплаты, корзину задаёт несвязанная таблица.

Дебиторка по корзине =
VAR PeriodEnd = [Конец периода]
VAR DaysFrom = SELECTEDVALUE('Корзины просрочки'[Дней от], -100000)
VAR DaysTo = SELECTEDVALUE('Корзины просрочки'[Дней до], 100000)
RETURN
    IF(
        MIN('Календарь'[Дата]) <= PeriodEnd,
        SUMX(
            FILTER(
                'Дебиторка',
                VAR DaysOverdue = INT(PeriodEnd - 'Дебиторка'[Срок оплаты])
                RETURN
                    'Дебиторка'[Дата документа] <= PeriodEnd
                        && (ISBLANK('Дебиторка'[Дата оплаты]) || 'Дебиторка'[Дата оплаты] > PeriodEnd)
                        && DaysOverdue >= DaysFrom && DaysOverdue <= DaysTo
            ),
            'Дебиторка'[Сумма]
        )
    )

В Power BI Desktop

Снимки окна Power BI Desktop с открытым отчётом.

Отчёт открыт в Power BI Desktop: страница «Главное» и панель данных с таблицами модели.
Отчёт открыт в Power BI Desktop: страница «Главное» и панель данных с таблицами модели.
Мера «ОПУ строка» в строке формул: одна мера считает статьи, разделы и итоговые строки ОПУ.
Мера «ОПУ строка» в строке формул: одна мера считает статьи, разделы и итоговые строки ОПУ.