Corp/docs/guides/COMPANY_ANALYTICS.md

240 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Аналитика компании
Аналитика встроена во вкладку «Аналитика компании» на странице `/finances`.
Внутренние вкладки: проекты, продажи, сотрудники, производство и журнал фактов.
Выбранные вкладки запоминаются для пользователя; обычный и широкий режимы
используют то же переключение, что и управление зарплатой. Фильтры открываются модальным окном по кнопке в шапке финансов;
под внутренними вкладками остаётся короткая строка применённых фильтров.
Отмена закрывает окно без изменения отчёта. Карточки адаптируются к ширине
рабочей области. В обычном режиме и при ширине рабочей области меньше 928px
списки по умолчанию свёрнуты до заголовка со счётчиком и стрелкой.
После открытия списка табличные строки представлены карточками с раскрывающимися
подробностями. В широком режиме списки всегда открыты и показывают таблицы.
Это относится к сводным отчётам, документам выплат и журналу фактов.
В широком режиме при ширине рабочей области от 928px основной блок сочетает
график и таблицу в двух колонках, если таблица помещается без горизонтальной
прокрутки. Иначе график располагается сверху, а таблица — снизу на всю ширину.
Компоновка пересчитывается при изменении размера области и данных таблицы.
Дополнительные блоки располагаются попарно.
Таблицы имеют собственную прокрутку и закреплённые заголовки.
В обычном и узком режиме графики открываются только модальным окном по кнопке
в заголовке списка, в том числе когда список свёрнут.
У каждого списка есть график с выбором показателя: разные единицы не смешиваются.
Месячные данные представлены линией с разрывами на неизвестных значениях;
остальные — сравнением до семи позиций. Высота области всех графиков — 280px.
Для явно отмеченных суммируемых показателей при числе позиций больше семи
показываются шесть крупнейших по модулю и «Прочее» — сумма остальных с учётом
знака. Число объединённых позиций видно в подписи и подсказке. Неизвестные
значения исключаются и отмечаются отдельно. Проценты, средние, возраст и стаж
не суммируются; для них показываются семь крупнейших значений.
Месячные ряды сохраняют все месяцы. Полные списки и CSV не сокращаются.
Графики сводных списков учитывают поиск по всем страницам результата.
Для документов и журнала используется только текущая загруженная страница,
что явно подписано над графиком; аннулированные записи журнала исключены.
Поиск, страницы, CSV и права на действия сохраняются в обоих режимах. Методика и инструкции открываются по вопросику в шапке,
пояснения к отдельным таблицам — по вопросику рядом с названием. Предупреждения
о качестве данных остаются в отчётах. Старый адрес `/finances/analytics`
перенаправляет на `/finances?tab=analytics`, открывая встроенную вкладку.
Используются визуальные компоненты и темы портала. Изображения из локальных
материалов служат примерами; их показатели, числа и оформление не являются ТЗ.
## Возможности
- Продажи по товарам, каналам, сочетанию товара и канала, клиентам и месяцам.
- Выручка, себестоимость, привязанные расходы, прибыль по учтённым расходам и маржа.
- Зарплата каждого сотрудника, изменение зарплаты, дни работы, отпуска и больничного,
уплаченные налоги, возраст, стаж и динамика численности команды.
- Выпуск, продажи произведённых товаров, производственные закупки и графики
с переключением между закупками, выручкой и прибылью. Прибыль по товарам и каналам повторно доступна
в производственном отчёте и рассчитывается тем же сервисом.
- Журнал подтверждённых фактов: выпуск, производственные закупки, уплаченные налоги
сотрудника, общие расходы, поступления и выплаты денег.
- Локальные проекты объединяют продажи, расходы и платежи связанных внешних
объектов; подтверждённые часы получают предварительную оценку до закрытия
зарплатного периода и точную оценку по его итоговому начислению после закрытия.
- Выбор дат включительно, валюты и источника продаж. Поиск, страницы таблиц и CSV
всех строк текущей группировки с учётом поиска. Диапазон ограничен 367 днями.
## Источники и методика
Продажи и возвраты берутся из `commerce.Sale` и `SaleLine`, расходы — из `Cost`.
Операции с `counts_as_sale=False` и связанные с ними расходы исключаются.
Возвраты уменьшают количество, выручку и себестоимость. Расходы учитываются по
собственной дате; их знак сохраняется, в том числе для компенсаций.
Выручка равна сумме после скидок и возвратов в денежной базе источника. Сервис не
пересчитывает НДС. Прибыль по учтённым расходам равна выручке минус себестоимость
минус привязанные расходы. Общие расходы и налоги не распределяются автоматически.
Этот показатель не следует принимать за окончательную чистую прибыль компании.
Правила распределения общехозяйственных расходов и налогов требуют согласования.
Себестоимость с `metadata.cost_source="unavailable"` считается неизвестной.
Нулевая себестоимость без подтверждения `manual` или `known_zero` также считается
неизвестной. Для затронутых групп прибыль и маржа возвращаются `null`, в UI — «—».
API дополнительно возвращает арифметический результат по известным расходам
`contribution_profit`, но интерфейс не показывает его как подтверждённую прибыль.
Клиенты имеют внешний ID и имя на продаже. Миграция восстанавливает их из
сохранённого `SourceRecord.payload.agent`, если информация присутствует. Новая
синхронизация МойСклад запрашивает контрагента вместе с документом. ID клиента
дополняется ID источника, поэтому одинаковые внешние ID разных систем не
объединяются. Когда имеется только имя, используется имя внутри источника;
для надёжного сопоставления необходимо заполнить внешний ID.
Расходы без привязки к продаже остаются в группе неизвестного клиента.
Валюты не суммируются и автоматически не конвертируются. Продажи разных систем
не дедуплицируются по предполагаемым совпадениям: при дублирующем импорте одного
оборота следует выбрать один источник.
Зарплата берётся из опубликованного расчёта каждого периода. Новая черновая
ревизия не заменяет опубликованную. Учитываются только периоды, целиком входящие
в выбранные даты. Начисления нескольких периодов одного месяца складываются по
месяцу окончания расчётного периода. Средняя зарплата сотрудника — начисленная
сумма до удержаний, делённая на число месяцев с расчётами. Среднее компании — по
парам «сотрудник / месяц» с расчётами. Неопубликованные и отсутствующие расчёты
не заменяются нулями. Рост сравнивает первый и последний месяц с опубликованными расчётами внутри
диапазона. Границы сравнения едины для всех сотрудников и показаны в интерфейсе.
Сотрудник включается в сравнение только при наличии обоих расчётов и ненулевой
начальной зарплате.
Рабочие дни — записи успешного анализа посещаемости с положительными часами и
без признака отсутствия. Отпуска и больничные — календарные дни по кадровым
событиям с существующим приоритетом пересекающихся событий. Стаж учитывает
увольнения и повторный приём, а не дату создания учётной записи. При отсутствии
кадровой истории текущая численность использует активность учётной записи;
историческая динамика включает только известную кадровую историю. Неизвестные
даты рождения и найма исключаются из средних и учитываются в счётчиках пропусков.
Налоги берутся только из фактически оплаченных записей журнала. Удержания
расчётного листка не означают уплаты налога. Производственный отчёт включает
продажи товаров с неаннулированным фактом выпуска не позже конца периода.
Количество выпуска вводится в единицах соответствующего товара и не зависит от выбранной валюты. Закупка может
относиться к конкретному конечному товару либо оставаться общей. Производственные
закупки не вычитаются повторно из прибыли поверх себестоимости продаж.
Фильтр проекта использует локальную карточку и применяется к продажам через её
внешние привязки, а к ручному журналу и трудозатратам — напрямую. Фильтр источника
влияет на продажи и связанные расходы; журнал остаётся общим.
Денежный поток считается только по поступлениям и выплатам из журнала. Нулевое
число записей не подтверждает отсутствие фактических выплат, налогов или выпуска.
## Доступ и журнал
Все endpoints требуют авторизации и существующего доступа к управлению финансами,
включая allow-list `FinanceSettings.management_users`. Ответы имеют `private,
no-store`; фронтовое скрытие ссылки не подменяет серверную проверку.
Для добавления фактов нужно `finance.add_reportingentry`, для аннулирования —
`finance.change_reportingentry`. Действует существующий временный административный
доступ `SIMPLE_ADMIN_ACCESS`. Права на просмотр финансов сами по себе не дают права
изменять журнал.
Номер документа уникален внутри вида записи. Записи не редактируются и не удаляются:
исправление выполняется аннулированием и новой записью с новым номером. Автор и дата
создания, автор и дата аннулирования сохраняются. Повторное аннулирование идемпотентно.
Будущие факты, отрицательные суммы и несовместимые с видом записи поля отклоняются.
## API
- `GET /api/v1/finance/analytics/report/?section=sales|people|production|projects`
- `GET /api/v1/finance/analytics/options/?search=...`
- `GET|POST /api/v1/finance/analytics/entries/`
- `POST /api/v1/finance/analytics/entries/{id}/void/`
Параметры отчёта и списка фактов: `date_from`, `date_to`, `currency`, локальный
`project` либо `unassigned`; параметр `source` применяется к продажам и внешним
привязкам. Список фактов использует стандартную пагинацию API. `options`
возвращает локальные проекты и первые 100 товаров и сотрудников по строке поиска.
CSV экранирует кавычки, переносы строк и потенциальные формулы в текстовых полях.
## Подключение и проверка
Перед запуском обновлённого backend выполните `python manage.py migrate`:
миграции Commerce 0012–0014, Projects 0001 и Finance 0032–0034 добавляют клиента
продажи, внешний справочник, локальные проекты и журнал фактов.
Для старых документов без имени клиента может потребоваться повторная синхронизация.
```bash
cd backend
pytest tests/finance/test_analytics.py tests/finance/test_projects.py tests/commerce -q
python manage.py makemigrations --check --dry-run
python manage.py check
```
```bash
cd frontend
node --import tsx --test src/lib/analytics.test.js
npx tsc --noEmit
```
Исходная книга учёта оплат автоматически не импортируется: в ней есть внешние
ссылки с ошибками и лист с учётными данными. Импорт этой книги требует отдельного
сопоставления колонок, подтверждения исходных сумм и подготовки безопасной копии.
Фраза «всё из годового финансового отчёта» требует отдельного перечня показателей
или самого годового отчёта: книга оплат не задаёт полный состав такого отчёта.
Индекс здоровья, планы и прогнозы из примерных изображений не включены в объём
реализации текстового ТЗ.
## Локальные проекты и внешние источники
Раздел «Проекты» использует локальную карточку портала как основную строку. В неё
по точным внешним привязкам собираются оплаченные расходы, документы платежей,
продажи и прямые расходы всех подключённых сервисов. Ручные выплаты, закупки и
общие расходы журнала ссылаются на локальную карточку напрямую. Подтверждённые
распределения сотрудников добавляют трудозатраты в часах, число дней учёта и
участников. Пока расчётный период открыт, стоимость часа предварительная: система
использует текущий расчёт зарплаты, а до его появления — действующую утверждённую
базовую ставку, разделённую на норму часов периода. После закрытия периода
стоимость становится точной: итоговое начисление из опубликованного расчётного
листка, включая премии, доплаты и корректировки, распределяется по фактически
отработанным часам. Полученная стоимость часа умножается только на подтверждённые
часы проекта. Черновые ставки не используются. Закрытый период без итогового
листка, а также часы без периода, нормы или фактических часов сохраняются как
неоценённые и не превращаются в ноль.
Фильтр локального проекта работает в продажах, производстве, проектах и журнале.
Вариант «Без привязки» объединяет внешние документы, для которых ещё не выбрана
локальная карточка, а также документы без проекта источника. В списке платежей
сохраняются и локальное имя, и исходное имя проекта для проверки происхождения.
Синхронизация получает `/entity/project` (включая архив), `/entity/paymentout`
и `/entity/cashout` с проектом, статьёй расходов, получателем и валютой. Продажи
получают связь `project` из отгрузки или возврата. Для старых продаж связь
восстанавливается из сохранённого исходного документа, если поле присутствует.
Проекты идентифицируются сочетанием источника и внешнего ID, а не названием.
«Оплачено по источникам» — сумма проведённых исходящих платежей и расходных ордеров
за период в выбранной валюте. Проект определяется по явному полю документа;
из связанных заказов и приёмок он не угадывается. Непроведённые документы и
документы, исчезнувшие после полностью успешного обхода источника, исключаются.
Повторный импорт обновляет ту же запись. Последние полученные исходные документы сохраняются; отсутствующие платежи
помечаются флагом и не удаляются. Импортированные платежи доступны только для чтения.
При ошибке обхода оставшиеся документы не помечаются отсутствующими.
Платёж может быть переводом между счетами, авансом, погашением долга, закупкой или возвратом: его нельзя
автоматически вычитать из прибыли поверх себестоимости. Поэтому оплаченные
суммы, ручные выплаты, закупки и общие расходы показаны отдельными колонками.
Ручные записи не следует создавать повторно для уже импортированных платежей.
При фильтре источника ручные факты относятся к нему через выбранный проект;
ручные факты без проекта не включаются в конкретный источник.
Дополнительный API: `GET /api/v1/finance/analytics/project-payments/` с обычной
пагинацией. `section=projects` возвращает сводку, строки проектов, статьи и месяцы.
Параметр `project` — локальный ID проекта либо `unassigned`. Поле `project`
доступно в ручной записи, продаже и прямом расходе. Справочник проектов возвращается
в `analytics/options/`. Права и `no-store` совпадают с остальной аналитикой.
Миграции: Commerce 0014, Projects 0001 и Finance 0033–0034. Миграция 0034
сохраняет прежнюю внешнюю связь ручной записи для аудита и заполняет локальный
проект там, где уже существовала точная привязка. Обычная синхронизация включает
проекты и исходящие платежи. Для отдельной загрузки без товаров и изображений:
```bash
python manage.py sync_moysklad_projects <UUID-подключения>
```
Контракты API: [проекты](https://github.com/moysklad/remap-1.2-java-sdk/blob/master/docs/Project.md),
[исходящие платежи](https://github.com/moysklad/remap-1.2-java-sdk/blob/master/docs/PaymentOut.md),
[расходные ордера](https://github.com/moysklad/remap-1.2-java-sdk/blob/master/docs/CashOut.md).