> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yume.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Метрика прибыли

> Фоновый пересчёт прибыли по одной завершённой ремонтной задаче: сборка строк витрины прибыли по расходникам и услугам и аккуратная синхронизация (вставка/обновление/удаление) без дублей.

Полный перечень полей — в [глоссарии](/ru/logic/workshop/profit-glossary).

Эта страница описывает, как модуль «Мастерская» переносит доход от завершённых ремонтных задач в общую денежную модель компании — витрину **«Прибыль (по дням)»**. Сама модель прибыли и правила распределения денег описаны отдельно; здесь речь только о фоновом пересчёте по одной задаче и о том, как он аккуратно синхронизирует строки прибыли, не создавая дублей.

<Info>
  Как устроена сама витрина «Прибыль (по дням)» и почему для мастерской ожидаемая сумма, плановая сумма и фактическая прибыль равны полной цене позиции (оплата их не ограничивает) — описано на странице [«Прибыль и окупаемость»](/ru/logic/profit-payback), раздел «Прибыль по задаче мастерской». Здесь эти формулы не повторяются.
</Info>

## Что делает пересчёт

Пересчёт прибыли по задаче — это **фоновая задача** (очередь по умолчанию), которая получает на вход идентификатор одной ремонтной задачи и приводит строки витрины прибыли по этой задаче в соответствие с её текущим содержимым: расходниками и услугами.

Сама фоновая задача — тонкая обёртка: она лишь передаёт идентификатор в метод агрегации прибыли, где и находится вся логика. Разделение сделано намеренно — тот же метод можно вызвать напрямую из кода (например, при сохранении или удалении задачи), не обязательно через очередь.

| Вход                            | Тип   | Назначение                                                    |
| ------------------------------- | ----- | ------------------------------------------------------------- |
| Идентификатор задачи мастерской | число | Ремонтная задача, по которой нужно пересчитать строки прибыли |

## Условия, при которых прибыль вообще считается

Прежде чем что-либо менять, пересчёт проверяет две вещи:

1. **Задача существует.** Если задачи с таким идентификатором нет, пересчёт молча завершается, ничего не трогая.
2. **Задача завершена.** У задачи должна быть проставлена дата завершения. Если её нет (задача ещё в работе), пересчёт также молча выходит без изменений.

<Warning>
  Если задача не завершена, пересчёт **ничего не удаляет и не меняет** — он выходит на проверке даты завершения, ещё до того как вычислит и применит какие-либо изменения (существующие строки прибыли к этому моменту уже прочитаны, но с ними ничего не делается). Это значит, что откат задачи из завершённого состояния в незавершённое сам по себе не убирает ранее созданные строки прибыли: их снятие обеспечивается отдельной логикой, вызывающей пересчёт (или удаление) в нужный момент.
</Warning>

Дата, к которой относится вся прибыль по задаче, берётся из даты завершения задачи. Для строк мастерской начало периода, конец периода и дата строки совпадают — это всегда один день, а порядковый день и общая длина периода равны единице. Прибыль мастерской не размазывается по дням, в отличие от аренды.

## Из чего складываются строки

После проверок пересчёт собирает две группы источников по задаче:

* **Расходники задачи** — каждый даёт отдельную строку прибыли, где заполнена ссылка на расходник, а ссылка на услугу пустая.
* **Услуги задачи** — каждая даёт отдельную строку прибыли, где заполнена ссылка на услугу, а ссылка на расходник пустая.

В каждой строке плановая сумма, ожидаемая сумма и фактическая прибыль равны цене соответствующей позиции (расходника или услуги). Скидки и ограничение по оплате к строкам мастерской не применяются — подробнее в разделе «Прибыль по задаче мастерской» на странице [«Прибыль и окупаемость»](/ru/logic/profit-payback).

<Note>
  Каждая строка прибыли по мастерской всегда привязана либо к одному расходнику, либо к одной услуге — но не к обоим сразу. Ссылка на саму задачу заполнена в любом случае, что позволяет находить и пересобирать все строки задачи целиком.
</Note>

## Как работает синхронизация (diff)

Пересчёт не удаляет всё и не создаёт заново. Вместо этого он сравнивает то, что **должно быть**, с тем, что **уже есть**, и применяет только необходимые изменения. Это защищает от лишней нагрузки на базу и сохраняет стабильные идентификаторы строк.

### Ключ строки

Каждая строка прибыли по задаче однозначно определяется тройкой: **расходник + услуга + дата строки**. Поскольку у строки расходника пустая услуга, а у строки услуги пустой расходник, эти тройки не пересекаются. Дата у всех строк одной завершённой задачи одинакова (дата завершения), поэтому фактически ключ разводит строки по конкретным расходникам и услугам.

### Три множества изменений

Пересчёт строит по ключу два набора — «что нужно» (по текущим расходникам и услугам) и «что есть» (существующие строки прибыли этой задачи) — и делит работу на три части:

| Операция   | Когда применяется                                               |
| ---------- | --------------------------------------------------------------- |
| Обновление | Ключ есть и там, и там, но значимые поля различаются            |
| Удаление   | Ключ есть среди существующих строк, но отсутствует среди нужных |
| Вставка    | Ключ есть среди нужных, но отсутствует среди существующих       |

Строка попадает в **обновление** только если реально изменилось хотя бы одно из значимых полей: ожидаемая сумма, фактическая прибыль или дата строки. Если ничего из этого не поменялось, строка не трогается вовсе — даже когда она формально присутствует в обоих наборах.

Все три операции выполняются пакетно: пакетное обновление, пакетное удаление и пакетная вставка. При обновлении переписываются все поля строки, кроме связей-«якорей» (ссылок на задачу, расходник и услугу) — их менять не нужно, ведь именно они образуют ключ и остаются постоянными.

<Tip>
  Практический смысл diff-подхода: если у завершённой задачи изменили цену одного расходника, пересчёт обновит ровно одну строку прибыли; если расходник удалили — уберёт одну строку; если добавили услугу — добавит одну строку. Остальные строки останутся нетронутыми.
</Tip>

## Как это используется

Пересчёт по задаче — это точечная операция «пересобрать прибыль одной ремонтной задачи». Он вызывается в момент, когда состав или стоимость завершённой задачи мог измениться. Именно поэтому строки прибыли по мастерской появляются только после завершения задачи и держатся в актуальном состоянии по мере правок её расходников и услуг.

Дальше эти строки живут в общей витрине прибыли наравне с доходом от аренды, продаж, доставки и штрафов и попадают во все сводные отчёты и метрики, которые строятся поверх неё. Подробнее о самой витрине и о том, что происходит при удалении задачи, — на странице [«Прибыль и окупаемость»](/ru/logic/profit-payback).

## Связанные страницы

<CardGroup cols={2}>
  <Card title="Глоссарий: Метрика прибыли" icon="book" href="/ru/logic/workshop/profit-glossary">
    Полный перечень полей строки прибыли и источников её значений.
  </Card>

  <Card title="Мастерская (ремонтные задачи)" icon="screwdriver-wrench" href="/ru/logic/workshop">
    Обзор всего модуля мастерской и переход к остальным разделам.
  </Card>

  <Card title="Воронка и задачи" icon="diagram-project" href="/ru/logic/workshop/pipeline">
    Kanban-воронки, этапы и ремонтные задачи с их статусами, порядком и агрегатами по списку.
  </Card>

  <Card title="Ресурсы и склад" icon="boxes-stacked" href="/ru/logic/workshop/resources">
    Складской справочник расходников и их списание и возврат при привязке к задачам с контролем остатков.
  </Card>

  <Card title="Услуги" icon="screwdriver" href="/ru/logic/workshop/services">
    Привязка услуг из каталога к ремонтным задачам с тарифом, ценой и длительностью.
  </Card>

  <Card title="Стоимость и оплаты" icon="money-bill-transfer" href="/ru/logic/workshop/pricing">
    Автоагрегация цены задачи из ресурсов и услуг и суммы оплат из платежей, плюс статусы оплаты.
  </Card>

  <Card title="Автосмена состояния инвентаря" icon="arrows-rotate" href="/ru/logic/workshop/inventory-state">
    Автоматический перевод инвентаря в целевое состояние при движении задачи по воронке.
  </Card>
</CardGroup>
