Project Costing в ERPNext: рентабельность объекта и Gross Margin
АйТи Фреш
IT-аутсорсинг и бизнес

Рентабельность объекта в ERPNext: из чего складывается Gross Margin и что в него не попадёт

Автор: , директор ООО «АйТи-Фреш» · · ~15 мин чтения
Весы с материалами и счетами на разрезе строящегося дома: рентабельность объекта в ERPNext
В рентабельность попадёт только то, что привязано к объекту.

Рентабельность объекта в ERPNext показывает карточка Project: поле Gross Margin равно выставленным заказчику счетам минус три вида затрат — труд по табелям, закупки по проведённым счетам и материалы, списанные на объект. Ниже: что куда попадает, что не попадает и как проверить цифру.

Из чего ERPNext складывает себестоимость объекта и где её смотреть

Начну с места, где цифра живёт. Рентабельность считается в карточке проекта (Project), в блоке Margin. Там два поля только для чтения: «Gross Margin» в деньгах и «Gross Margin %». Они не вводятся руками: система пересчитывает их из пяти связанных полей, и каждое из них питается своим документом. Мы используем эту механику в ERPNext под ключ для прибыльности объекта: объект там — проект, этап — задача, а затраты собираются из закупок, склада и табелей. Дальше разбираю её по исходному коду версии 15, потому что глава в нашей базе знаний давала формулу с повторяющимися слагаемыми, и копировать её дословно нельзя.

По коду Project (v15) формула такая: Gross Margin = Total Billed Amount минус сумма трёх затрат: Total Costing Amount, Total Purchase Cost и Total Consumed Material Cost. Процентная маржа равна Gross Margin, делённой на Total Billed Amount, умноженной на сто. Обратите внимание на то, что стоит в начале: не выручка по заказу и не сумма договора, а именно то, что выставлено счетами покупателю (Sales Invoice) и проведено. Пока вы не выставили счёт, Gross Margin объекта отрицательна на сумму затрат, а процент в коде v15 при нулевых счетах просто равен нулю. Для строителя с поэтапным выставлением это ожидаемо, но директору надо объяснить заранее, иначе он решит, что система врёт.

Откуда берётся каждая сумма, в одной таблице: | Поле проекта | Откуда считается | Что обязательно | |---|---|---| | Total Billed Amount (via Sales Invoice) | проведённые Sales Invoice | поле Project в счёте или в строке счёта | | Total Purchase Cost (via Purchase Invoice) | проведённые Purchase Invoice | поле Project в строке счёта поставщика | | Total Consumed Material Cost (via Stock Entry) | проведённые Stock Entry | Project в шапке, строка без склада-получателя | | Total Costing Amount (via Timesheet) | проведённые Timesheet | Project в строке табеля, ставка себестоимости | | Total Sales Amount (via Sales Order) | проведённые Sales Order | справочное поле, в маржу не входит |

Что видно из таблицы. Во-первых, ни одна цифра не появляется сама: каждая требует привязки документа к проекту. Во-вторых, все суммы берутся только из проведённых документов (docstatus = 1): черновики не считаются. В-третьих, заявка (Material Request), заказ поставщику (Purchase Order) и приёмка (Purchase Receipt) в себестоимость проекта не входят, поэтому отчёт отстаёт от реальных обязательств. Это не ошибка, а свойство схемы, и о нём надо помнить, когда смотрите на цифры утром до того, как бухгалтер провёл вчерашние счета.

Закупки в проекте: почему Total Purchase Cost растёт только после счёта поставщика

Total Purchase Cost суммирует чистую сумму строк (Net Amount в валюте компании) из проведённых счетов поставщика, где в строке стоит ваш проект. Механика видна в коде счёта: при проведении (submit) счёт прибавляет сумму строки к закупочной стоимости проекта и пересчитывает маржу, при отмене вычитает. То есть цифра обновляется сразу по событию, без ночных задач. Если же по какой-то причине сумма в карточке не сходится, в форме проекта есть кнопка «Update Costing and Billing», которая пересчитывает все поля заново по базе.

Отсюда практическое правило, которое я прописываю в регламенте снабжения. Проект указывают в строках счёта поставщика (Purchase Invoice), а не только в заказе. На заказе проект тоже полезен, потому что он тянется в связанные документы, но в себестоимость попадёт только счёт. Если снабженец проводит счёт без проекта, расход уйдёт в общий пул и в рентабельности объекта не появится. Это самая частая причина фразы «у нас в проекте нулевые закупки, а материал уже на объекте».

Доставка. Если счёт транспортной компании приходит позже приёмки материала, её распределяют на себестоимость уже принятых позиций документом Landed Cost Voucher: по количеству или по сумме. Но у него есть ограничение, о котором забывают: документ распределяет стоимость, но сам не создаёт кредиторскую задолженность перед перевозчиком. Долг надо закрыть счётом или платёжным документом, иначе в отчёте о прибылях и убытках появится минус по статье расходов. Ограничения этого документа и разметку центров затрат я разбираю отдельно, а здесь важно только одно: стоимость доставки доходит до объекта через цену материала на складе, а не через Total Purchase Cost.

Материалы со склада: как списание на проект попадает в маржу

Если материал идёт через склад, затрата попадает в проект другим путём. Правильная цепочка движения такая. Сначала материал перемещают с центрального склада на склад объекта документом Stock Entry с назначением Material Transfer: остаток на объекте растёт, в расход ничего не списывается. Потом фактический расход списывают документом Stock Entry с назначением Material Issue со склада объекта и указывают счёт расходов. В этом документе в шапке заполняется поле Project. Только после этого сумма строки попадёт в Total Consumed Material Cost.

Точное условие в коде: берутся проведённые Stock Entry с этим проектом, а в суммирование идут строки, у которых не заполнен склад-получатель (Target Warehouse). Именно поэтому перемещение на склад объекта в расход не попадает, а списание попадает: у списания склада-получателя нет. Это удобно. Но пока Material Issue не оформлен, стоимость материала, лежащего на объекте, в рентабельности не видна. На практике я прошу начальников участков списывать материал по факту работ раз в неделю, а не в конце месяца, иначе маржа объекта в середине месяца выглядит слишком оптимистично.

Теперь предупреждение, которое я нашёл при разборе кода и которое надо проверить на вашем стенде. Закупочная цифра и цифра списания считаются независимо друг от друга. Если вы купили материал на склад, провели счёт поставщика с проектом в строке, а потом списали тот же материал на тот же проект, то затраты могут быть посчитаны дважды: один раз в закупках, второй раз в списании. Чтобы этого не случилось, я принимаю простое правило: материал, купленный на объект «с колёс», идёт через счёт поставщика с проектом; материал, купленный на склад и списываемый на объекты, идёт через списание с проектом, а в строках счёта проект не ставим. Одно правило на материал, а не оба.

Один материал считается в проекте одним путём: либо счёт поставщика с проектом в строке (покупка «с колёс»), либо списание Material Issue с проектом в шапке (покупка на склад). Оба сразу дадут двойную затрату. Проверьте поведение на стенде своей версии.

Труд: Timesheet, Activity Cost и ставка себестоимости часа

Труд бригады попадает в проект через табель (Timesheet). В строке табеля указывают проект, задачу (Task) и вид работ (Activity Type), а себестоимость часа берётся из ставки. Приоритет такой: сначала ищется запись Activity Cost для пары «сотрудник и вид работ», если её нет, берутся значения по умолчанию из вида работ. В строке табеля видны Costing Rate и Costing Amount (ставка и сумма себестоимости) и Billing Rate с Billing Amount (ставка и сумма для выставления). В себестоимость проекта идёт Costing Amount, и только из проведённых табелей.

Отсюда два риска. Первый: ставка себестоимости должна быть настоящей, а не нулевой. Если для сотрудника и вида работ нет Activity Cost, а в виде работ нет ставки, часы в системе есть, а себестоимость труда равна нулю, и рентабельность выглядит лучше, чем есть. Я всегда прохожу по справочнику вместе с бухгалтером: ставка должна включать зарплату, начисления и накладные, которые вы готовы отнести на час работы. Второй риск: табели, которые не проводят. Если бригадир их заполняет, но не подтверждает, система видит ноль. Поэтому проведение табеля входит в еженедельную рутину.

Простой пример на условных числах. Бригада из трёх человек отработала на объекте неделю: 3 × 40 часов = 120 часов. Ставка себестоимости для вида работ «Монтаж потолков» задана 650 ₽ за час. Costing Amount = 120 × 650 = 78 000 ₽. Эта сумма в разрезе проекта и задачи прибавится к Total Costing Amount после проведения табеля. Если бригада отработала на двух объектах, часы разносят по разным строкам с разными проектами, а не записывают в одну.

Что в расчёт не попадает и почему отчёт может «врать»

Отчёты не врут, они считают ровно то, что им передали. Для сравнения: как устроен контур закупок и склада в других открытых системах, видно в материале про ERPNext для производства и про Odoo и российскую локализацию. Но вывод читателя расходится с реальностью, если он не знает границ. Ниже границы, которые я проговариваю на запуске. Часть из них следует из кода v15, часть из глав базы знаний; там, где я не смог подтвердить, прямо пишу «проверьте».

Отдельно про расхождение с главой базы знаний, которое я нашёл при проверке. В главе сказано, что в рентабельность входят авансовые отчёты (Expense Claim). В коде Project версии 15 нет ни поля с суммой авансовых отчётов, ни такого слагаемого в формуле маржи. Если вам нужно учитывать авансовые расходы по объекту, закладывайте это как отдельную настройку и проверяйте на стенде в вашей версии и с вашим приложением HRMS: коробочный Gross Margin их не включает. Это важнее, чем кажется: строительные компании часто закрывают мелкие закупки через подотчёт. Про то, как автоматизировать сверку выписок и категоризацию расходов, у меня есть отдельный разбор.

И ещё одно уточнение про названия. Отчёта под названием «Project vs Budget Cost», упомянутого в главе, я в списке отчётов ядра v15 не нашёл. План-факт по бюджетам даёт Budget Variance Report: в нём можно выбрать измерение, в том числе Project. Для сводок по проектам есть Project Summary, Project Billing Summary и Project wise Stock Tracking. Бюджет (Budget) контролирует деньги по счетам расходов, а не количество материала, и об этом я подробно пишу в других материалах.

Разбор: три объекта «Потолка и Стены» и одна пропущенная привязка

Условный пример. Ремонтная компания «Потолок и Стена», 9 рабочих мест: директор, сметчик, два прораба, снабженец, бухгалтер и три монтажника в штате. Три объекта в работе, затраты ведутся в ERPNext, регламентированный учёт в 1С. Директор открыл карточки проектов в конце месяца и увидел валовую маржу. Цифры условные, посчитаны по формуле из раздела 1.

Результат по трём объектам, в рублях: | Объект | Счета заказчику | Закупки по счетам | Списано на объект | Труд по табелям | Gross Margin | % | |---|---|---|---|---|---|---| | Квартира 54 м² | 1 000 000 | 520 000 | 145 000 | 180 000 | 155 000 | 15,5 | | Офис 120 м² | 1 800 000 | 900 000 | 210 000 | 320 000 | 370 000 | 20,6 | | Магазин 80 м² (как увидел директор) | 1 200 000 | 350 000 | 150 000 | 250 000 | 450 000 | 37,5 | Магазин выглядел лучшим объектом, и это насторожило сметчика: по смете ожидали около четверти. Он поднял журнал закупок и нашёл счёт поставщика на 170 000 ₽, проведённый снабженцем без проекта в строках: в день проведения снабженец был на объекте с телефона и выбрал склад, но не проект. После привязки счёта к проекту Total Purchase Cost вырос с 350 000 до 520 000 ₽, затраты составили 920 000 ₽ (520 000 + 150 000 + 250 000), Gross Margin упала до 280 000 ₽, то есть до 23,3 %.

Что мы изменили после этого. Поле Project в строках счёта поставщика сделали обязательным для закупок по объектам, а общие закупки без объекта оформляет отдельная роль. В регламент добавили ежедневную сверку: счета без проекта фильтруются списком и разбираются снабженцем до конца дня. Еженедельно бухгалтер проверяет, что табели проведены и что на Material Issue стоит проект. Ни одна из этих мер не требует доработки кода, это дисциплина и обязательные поля. Но без неё рентабельность превращается в красивую цифру, которой нельзя доверять.

Как проверить цифру на стенде до того, как показывать её директору

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

Для быстрой сверки без интерфейса удобна консоль bench. Команды ниже читают значения полей проекта; название проекта и сайта подставьте свои:

bench --site site1.example.com console
frappe.db.get_value(
    "Project", "PROJ-0001",
    ["total_billed_amount", "total_purchase_cost",
     "total_consumed_material_cost", "total_costing_amount",
     "gross_margin", "per_gross_margin"],
    as_dict=True,
)

Формулу проверьте сами: gross_margin должен равняться total_billed_amount минус три затраты. Расхождение значит, что у вас версия с другой логикой или документ без привязки: тогда нажмите «Update Costing and Billing» в форме проекта и повторите.

Что я проверяю обязательно: 1) черновик счёта поставщика не меняет Total Purchase Cost, проведённый меняет; 2) Material Transfer на склад объекта не меняет Total Consumed Material Cost, Material Issue меняет; 3) непроведённый табель не даёт Costing Amount; 4) нет двойного счёта одного материала; 5) цифра по Sales Order не влияет на маржу. Если хотя бы один пункт не сходится с тем, что я описал, значит, у вас другая версия или другая настройка, и важнее выяснить это на стенде, а не на отчёте директору. Показать контрольный прогон на живых данных можно на демостенде erp-demo.itfresh.ru по запросу. Развернуть такой стенд на вашем сервере можно в рамках услуги внедрения open source.

Частые вопросы

Как посмотреть прибыль по строительному объекту в ERPNext?

Откройте карточку Project и найдите блок Margin: поля Gross Margin и Gross Margin %. Они считаются как Total Billed Amount минус затраты по табелям, закупкам и списанию материалов. Цифры верны, только если все счета, списания и табели привязаны к проекту и проведены. План-факт по бюджетам даёт Budget Variance Report.

Почему в проекте нулевые затраты по закупкам, хотя заказы оформлены?

Total Purchase Cost считается только из проведённых счетов поставщика (Purchase Invoice, docstatus = 1) с заполненным полем Project в строке. Заявка, заказ и приёмка в эту сумму не входят. Проверьте, что счёт проведён, а не сохранён черновиком, и что проект указан в строке, а не только в заказе.

Попадает ли доставка материалов в себестоимость объекта?

Через Landed Cost Voucher расходы на доставку и разгрузку распределяются на материалы по количеству или по сумме и входят в цену материала на складе. Но документ сам не создаёт долг перед перевозчиком: нужен счёт или платёжный документ, иначе в отчёте о прибылях и убытках появится минус.

Как ERPNext считает стоимость часа работы бригады?

В Timesheet фиксируются часы по задаче и виду работ. Ставка себестоимости берётся из Activity Cost для пары «сотрудник и вид работ», при её отсутствии из вида работ. Costing Amount из проведённого табеля идёт в Total Costing Amount проекта. Нулевая ставка даёт нулевую себестоимость труда.

Можно ли в ERPNext считать рентабельность по российской смете?

Смету по ФЕР и ТЕР ERPNext не ведёт: её считают во внешних программах, а в систему загружают итоговую ресурсную спецификацию. Рентабельность в ERPNext сравнивает факт затрат с выставленными счетами; бюджет можно задать через Budget с привязкой к проекту, проверьте поведение в своей версии.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи