Skip to content

Финансы — бухгалтерия владельца, книги отделов, выплаты по задачам ​

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

Живёт внутри backend-модуля Персонал (apps/core-api/src/modules/staffing/finance.service.ts, task-payouts.service.ts), но это своя область прав, намеренно не часть workspace:staffing:* — см. запись от 2026-07-28 в docs/iam/PERMISSIONS_CATALOG.md. Деньги конфиденциальны: каждое изменение пишется в журнал аудита, но никогда — в TeamEvent, ленту /team/activity, которую видит любой обладатель workspace:staffing:read — гораздо более широкий круг, чем те, кому доступны финансы.

Как сделать... ​

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

Бухгалтерия владельца ​

Записать выделение бюджета или расход — открой «Финансы» в левом меню → Добавить запись (справа вверху) → выбери Тип (Доход/Бюджет/Расход), Сумму, Валюту, Дату, Описание, при желании привяжи к Проекту/Отделу → Создать. См. гайд по полям.

Отредактировать или удалить запись бухгалтерии — строка записи → ⋯ → Редактировать запись или Удалить запись.

Сравнить свои записи с книгой руководителя отдела — «Финансы» → вкладка Сверка. По умолчанию показаны только расхождения; включи Показать совпадающие, чтобы увидеть всё, включая совпадающие строки.

Записать/отредактировать запись бухгалтерии — гайд по полям ​

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

  • Тип — обязательно: Доход (реально полученные деньги), Бюджет (выделенные, но ещё не потраченные деньги) или Расход (реально потраченные деньги). Эти три никогда не смешиваются в один общий итог.
  • Сумма — обязательно, положительное число. Прибавляется сумма к итогу или вычитается из него — определяется целиком Типом, а не знаком числа.
  • Валюта — обязательно: AMD / USD / EUR / RUB. Каждый итог считается по каждой валюте отдельно, без конвертации — одна и та же трата в двух валютах даёт две отдельные суммы.
  • Дата — обязательно, по умолчанию сегодняшняя. Именно эта дата, а не момент сохранения записи, определяет, в каком месяце она сгруппирована и какие фильтры периода её захватывают.
  • Описание — обязательно, от 1 до 2000 символов.
  • Проект — необязательно (скрыто на собственной вкладке «Финансы» проекта, где он и так подразумевается). Привязывает запись к проекту для разбивки на вкладке «Отчёты»; оставь «Без проекта» для остального.
  • Отдел — необязательно (скрыто или заранее заполнено там, где книга уже привязана к одному отделу). Та же идея, что и у Проекта, но для сравнения на вкладке «Сверка».

Собственная книга отдела ​

Записать в собственную книгу своего отдела (только руководители отделов) — открой свой отдел → вкладку Финансы → Добавить запись. Это отдельная книга от бухгалтерии владельца выше — см. Собственная книга отдела. Те же поля, что и в гайде по полям бухгалтерии владельца, минус Отдел.

Отредактировать или удалить запись в книге отдела — строка записи → ⋯ → Редактировать запись или Удалить запись (только у руководителя).

Вкладка «Финансы» проекта ​

Посмотреть, как выглядят траты по своему проекту — открой проект → его вкладку Финансы. Это та же бухгалтерия владельца, просто заранее отфильтрованная по этому проекту.

Записать, отредактировать или удалить запись из вида проекта — те же Добавить запись / ⋯ → Редактировать запись / Удалить запись, что и в бухгалтерии владельца, просто заранее отфильтровано по этому проекту. Тот же гайд по полям.

Выплаты по задачам ​

Предложить выплату исполнителю задачи — открой задачу → карточку Оплата за задачу → Добавить выплату → выбери Исполнителя, Сумму, Валюту → Создать (это черновик, исполнитель его ещё не видит) → ⋯ у этой строки → Отправить исполнителю. См. гайд по полям.

Ответить на предложенную тебе выплату — открой задачу; если ты исполнитель, кнопки Принять/ Отклонить видны прямо на твоей выплате, без меню.

Записать ответ исполнителя без аккаунта — в том же меню ⋯ вместо ожидания его собственного клика — пункты «Отметить как принято/отклонено (договорились вне платформы)».

Отредактировать или удалить выплату — ⋯ у выплаты → Редактировать запись или Удалить запись. Изменение суммы/валюты уже отправленного офера сбрасывает его в черновик — его нужно отправить заново. Удаление заблокировано, если по выплате уже есть реальный платёж или ожидающий отчёт.

Добавить/отредактировать выплату — гайд по полям ​
  • Исполнитель — обязательно, задаётся только при создании. В списке показаны только уже назначенные на эту задачу люди, у кого ещё нет выплаты; как только у всех уже есть выплата, кнопка «Добавить выплату» пропадает с карточки.
  • Сумма — обязательно, положительное число.
  • Валюта — обязательно: AMD / USD / EUR / RUB, тот же список, что и в бухгалтерии.
  • Полей «Описание» или «Дата» нет — выплата пока просто причитающаяся сумма, ещё не строка бухгалтерии; они появятся позже, в отчёте о платеже.
  • Новая выплата всегда начинается как Черновик, невидима для исполнителя, пока явно не нажмёшь Отправить. Изменение Суммы/Валюты уже отправленного офера сбрасывает его обратно в Черновик — его нужно отправить заново, прежде чем исполнитель увидит изменения.
  • Удалить заблокировано, если по выплате уже записан реальный платёж или пока отчёт о платеже ещё ожидает подтверждения — сначала отмени отчёт (см. Подтверждение платежа ниже).
  • Над этим списком появляется предупреждение о несоответствии оценке задачи, если сумма всех принятых выплат превышает поле «Стоимость» задачи — это лишь мягкое предупреждение, ничего им не блокируется. Выплаты в другой валюте, чем у задачи, помечаются отдельным предупреждением, поскольку сравнить их напрямую нельзя.

Подтверждение платежа ​

Записать, что ты отправил(а) кому-то выплату — на принятой выплате: ⋯ → Сообщить о переводе → сумма/дата/описание уже заполнены → отправить. Это ещё не трогает бухгалтерию — получатель должен сам подтвердить, что деньги дошли. См. гайд по полям.

Подтвердить, что платёж получен — на задаче появится баннер («Владелец сообщает об отправке X числа Y — дошло?») с кнопками Подтвердить получение / Не получил(а).

Записать итог платежа за контрактора без аккаунта — в том же меню ⋯ — пункты «Отметить как полученное/не полученное (договорились вне платформы)» для менеджера.

Отменить ожидающий отчёт о платеже — ⋯ у выплаты → Отменить сообщение о переводе (например, чтобы исправить опечатку в сумме, не дожидаясь, пока получатель оспорит).

Сообщить о переводе — гайд по полям ​
  • Сумма — обязательно, заранее заполнена суммой, которая ещё причитается (не полной суммой выплаты) — частичный платёж не нужно вручную вычитать из общей суммы. Ввести больше, чем реально ещё причитается, невозможно.
  • Дата — обязательно, заранее заполнена сегодняшней датой.
  • Описание — обязательно, заранее заполнено сгенерированной строкой, полностью редактируемо, до 2000 символов.
  • Одновременно может ожидать подтверждения только один отчёт по выплате — «Сообщить о переводе» пропадает из меню ⋯, пока текущий отчёт не подтверждён, не оспорен или не отменён.
  • Ничего не попадает в бухгалтерию, пока отчёт не подтверждён — именно подтверждение и создаёт реальную запись расхода.

Баннер на дашборде ​

Увидеть, что ждёт твоего ответа, по всем задачам сразу — /team/dashboard → баннер «Мои выплаты», когда у тебя есть предложенный офер или ожидающий отчёт о платеже. Настраивать нечего — он появляется только когда действительно есть что решить.

Уведомления ​

Деньги — единственная область, где каждое событие требует чьего-то ответа, поэтому все они Важные: и в колокольчик, и на почту. Полное объяснение в Уведомлениях.

Выплаты ​

Сделали предложение — исполнителю приходит уведомление с суммой. Пока он не ответит, ничего дальше в этом процессе сдвинуться не может.

Предложение принято или отклонено — тому, кто его сделал, вместе с комментарием, если он был. Следующий шаг теперь за ним.

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

Получение подтверждено или оспорено — тому, кто отметил выплату, и автору предложения. Спор в любом случае решать им.

Сама бухгалтерия — записи о доходах, бюджетах и расходах — не уведомляет никого. Это ваши собственные записи, и никто их не ждёт.

Модель прав ​

Два права из каталога плюс одно данные-ориентированное правило, друг над другом:

КтоЧто может
workspace:finance:readЧитать бухгалтерию владельца и книгу любого отдела (последнюю — только на чтение)
workspace:finance:manageПисать в бухгалтерию владельца и сообщать/подтверждать реальный платёж — единственное право, двигающее деньги в бухгалтерии
workspace:finance:payouts:readВидеть все выплаты по задачам команды, включая черновики, — и ничего не предлагать
workspace:finance:payouts:manageПредлагать, изменять, отправлять и отменять выплаты по задачам команды
Текущий руководитель отдела (Department.leaderId, без строки права)Читать и писать только книгу своего отдела; управлять выплатами по задачам своего отдела — но не может сообщать/подтверждать реальные платежи (см. ниже)

Проверка руководителя — тот же данные-ориентированный паттерн, что и везде в Персонал (вкладки «Финансы»/«Роли» отдела) — никакого права из каталога, просто «является ли leaderId этого отдела текущим пользователем». workspace:finance:manage намеренно исключено из набора прав уровня Team Admin в P4P Internal — финансы доступны только уровню Team Manager (docs/staffing/ADR-002-org-rbac-migration.md; платформенные роли SUPER_ADMIN/P4P Owner/P4P Admin с этой проверкой с 2026-08-18 не связаны напрямую — см. Персонал о том, как платформенная роль всё же может дотянуться до уровня Team Manager через linkedOrgRoleId, но это удобство поверх, а не сама проверка).

Оба права по выплатам выделены из workspace:finance:manage 09.09.2026, потому что до этого «видеть, сколько платят команде» и «менять это» были одним и тем же — то есть дать обзор можно было, только отдав контроль над чужими выплатами. Теперь это отдельные переключатели на Team-ролях, и payouts:manage подразумевает payouts:read (право предлагать выплату, на которую нельзя смотреть, никому не пригодилось бы). Сообщение и подтверждение реального платежа остались на workspace:finance:manage: это запись в бухгалтерию.

В момент разделения ни у кого ничего не изменилось — Team Manager получил оба ключа, а всем кастомным ролям, у которых уже было workspace:finance:manage, их проставила миграция.

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

Более узкое исключение внутри выплат по задачам (этап 5 финансов, 2026-07-30): создание черновика выплаты, её отправка и ответ исполнителя (принять/отклонить) доступны на уровне владельца или руководителю отдела задачи (TaskPayoutsService.canManage). Но фактическое сообщение или подтверждение реального платежа (reportPayment/confirmPayment-от-имени-менеджера/ markPayment/cancelPaymentReport) требует именно workspace:finance:manage (hasFinanceManage/assertCanRecordPayment) — руководитель отдела, который может предлагать и управлять офером, всё равно не может трогать реальные деньги в бухгалтерии за ним. Найдено в аудите 2026-07-30: у старого действия «Отметить как оплачено» такой проверки вообще не было.

Бухгалтерия владельца (/team/finances) ​

Простыми словами: единственное место, где владелец отслеживает движение денег по всей компании — три вкладки на одной странице, один общий переключатель периода над всеми ими.

  • Records tab («Записи») — сама лента записей: сводная плашка по валютам (доход/потрачено/к прошлому периоду), затем журнал, сгруппированный по месяцам. «Добавить запись» (:manage) открывает диалог: тип (INCOME/BUDGET/EXPENSE — три разных экономических события, никогда не смешиваются; суммы всегда положительные, направление определяется type), сумма, валюта (AMD/USD/EUR/RUB, курируемый список — добавление новой валюты — это изменение кода, поскольку суммы считаются по каждой валюте отдельно, без конвертации). Редактирование/удаление строки (:manage).
  • Reports tab («Отчёты») — без собственных данных, тот же отфильтрованный набор, что и Records: график бюджет-против-расходов по месяцам/кварталам/годам (с зумом) и разбивка «куда уходят деньги» по проекту и по отделу, каждая строка кликабельна — переносит в Records с готовым фильтром. Доход намеренно исключён из обоих — он есть только в сводной плашке на Records.
  • Reconciliation tab («Сверка», в интерфейсе — «Reconciliation», внутри кода — comparison) — одна строка на пару отдел × валюта, собственные записи владельца, привязанные к отделу, рядом с книгой этого отдела, бюджет и расход сравниваются независимо. Ненулевая разница — это информация, а не ошибка: две книги никогда не связаны и не синхронизированы. Совпадающие строки по умолчанию скрыты (переключатель «Показать совпадающие (N)» открывает их), так что таблица показывает только то, что реально требует внимания; сама вкладка получает бейдж только когда есть на что смотреть.
  • Переключатель периода (Этот месяц/Прошлый месяц/Этот квартал/.../Свой диапазон) общий для всех трёх вкладок и сохраняется в URL (?view=&period=) — ссылка на «Сверку за прошлый квартал» переживает перезагрузку. Специально НЕ запоминается между визитами: открыв страницу через недели на старом окне, легко принять устаревшие цифры за текущие.

Собственная книга отдела (/team/departments/[id]/finances) ​

Простыми словами: личная бухгалтерия руководителя отдела для своего отдела — независимая от владельческой, намеренно, чтобы вкладке «Сверка» выше было что реально сравнивать.

  • Видна только текущему руководителю этого отдела (чтение и запись) и обладателям workspace:finance:read (только чтение, через владельческий список — маршрут только для руководителя вернёт им 403). Тем, у кого есть только workspace:staffing:read без доступа к финансам, сама вкладка «Finances» вообще не появляется в списке вкладок; при прямом переходе по её URL всё равно показывается сообщение «недоступно» вместо книги (защитная проверка на самой странице, та же идея, что и на вкладке Finance проекта ниже).
  • Та же форма записи, что и в бухгалтерии владельца (тип/сумма/валюта/дата/описание, опциональная привязка к проекту — отдел подразумевается сам собой). Плитки по каждой валюте (Доход/Бюджет/ Потрачено/Остаток) над таблицей.
  • Владелец читает эту книгу на вкладке «Сверка» выше, но никогда не может в неё писать — весь смысл двух независимых книг — в их сравнении; правки владельца здесь испортили бы это.

Вкладка «Финансы» в карточке проекта (/team/projects/[id]/finances) ​

Простыми словами: бухгалтерия владельца, отфильтрованная по одному проекту — срез общекорпоративных финансов по этому проекту, а не отдельная бухгалтерия.

  • Отображается и доступна только обладателям workspace:finance:read — сама вкладка скрыта без этого права, а страница дополнительно перепроверяет при прямом переходе по URL.
  • Та же форма, что и Records на /team/finances (сводная плашка, журнал, переключатель периода, фильтры по отделу/валюте/типу минус «Проект», который здесь подразумевается сам собой) — запись всё равно идёт через эндпоинты бухгалтерии владельца (:manage), просто заранее отфильтрованные по этому проекту.

Ставки и счета (/team/invoices) ​

Проще говоря: укажите клиента и ставку проекта, превратите записанные часы в счёт, выставьте его, отметьте оплату — и посмотрите, окупается ли проект.

  • Клиент и ставка — на вкладке «Финансы» проекта (изменять — workspace:finance:manage). Одна ставка в час на проект; новая ставка влияет только на новые счета.
  • Счёт по часам — там же: выберите период и одну строку на человека или на задачу. Берутся часы проекта за период, которых ещё нет ни в одном счёте, × ставка — получается черновик. Эти часы заняты: их нельзя выставить повторно и нельзя изменить их длительность и день, пока черновик не удалён или счёт не аннулирован.
  • Черновик → выставлен → оплачен, или аннулирован — черновик можно менять и удалять. При выставлении счёт получает следующий номер (2026-0001) и больше не меняется; для этого нужны данные компании в Настройках счетов (меню на /team/invoices). Выставленный счёт отмечают оплаченным (в книге появляется доход проекта) или аннулируют с причиной — он остаётся в списке, а номер больше не используется. «Просрочен» — выставлен, не оплачен и срок прошёл.
  • Печать / PDF — кнопка «Печать / PDF» открывает чистую страницу; в окне печати выберите «Сохранить как PDF».
  • Прибыльность — на вкладке «Финансы» проекта: выставлено, оплачено, ждёт оплаты, стоимость (часы × себестоимость часа каждого человека плюс согласованные выплаты за задачи) и маржа, по валютам.
  • Себестоимость часа — сколько компании стоит час каждого человека: меню «…» на /team/finances → «Себестоимость часа». Только для финансов; кого нет в списке ставок, проект называет по имени — их часы считаются бесплатными.

Выплаты по задачам (карточка «Оплата за задачу» на странице задачи) ​

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

Жизненный цикл: DRAFT (создано, исполнителю не видно) → Отправить исполнителю → PROPOSED → исполнитель Принять/Отклонить → ACCEPTED/DECLINED. Редактирование суммы/валюты уже отправленного офера сбрасывает его обратно в DRAFT — изменённый офер нужно явно отправить заново, никогда не подменяется молча под уже данным ответом. Отклонённый (DECLINED) офер можно предложить снова.

  • Отображается только для тех, кто управляет выплатами (уровень владельца или руководитель отдела задачи), и для исполнителя, у которого на этой задаче есть хотя бы одна не-DRAFT выплата.
  • Контракторы без аккаунта (запись из кадрового резерва, не привязанная к User): не могут ответить в интерфейсе, поэтому менеджер фиксирует итог вручную — «Записать как принято/отклонено (договорились вне платформы)». Категорически недоступно для любого исполнителя, у которого есть аккаунт — менеджер никогда не может говорить от имени реального аккаунта (taskPayout.assigneeCanRespond). assigneeHasAccount/isMine в ответе сервера вычисляются специально для того, чтобы аккаунт, связанный через кадровый резерв (чей userId иначе нигде не раскрывается), всё равно видел свои кнопки «Принять»/«Отклонить» (исправлено 2026-07-30 — раньше этот случай был виден только через баннер на дашборде).
  • Удаление выплаты заблокировано, если по ней уже есть записанный реальный платёж или ожидающий ответа отчёт о платеже — и то, и другое иначе молча оставило бы без привязки реальную транзакцию или открытый вопрос к получателю.

Подтверждение платежа (этап 5 финансов) ​

«Я отправил деньги» от менеджера и то, что деньги реально пришли, — два разных факта: реальная запись EXPENSE в бухгалтерии создаётся только когда получатель сам подтвердит получение, никогда только со слов менеджера (исправлено 2026-07-30: старое «Отметить как оплачено» создавало запись сразу же, только на основании отчёта менеджера).

  1. Сообщить о платеже (только workspace:finance:manage — не руководители отделов, см. таблицу прав выше) — сумма/дата/описание, не больше остатка к оплате по этой выплате. Фиксирует только заявление; FinanceEntry пока не создаётся. В моменте может быть только один открытый отчёт по выплате.
  2. Исполнитель видит баннер («Владелец сообщает об отправке X числа Y — дошло?») и либо Подтверждает получение (создаёт реальную запись EXPENSE, приписанную тому, кто сообщил о переводе, а не тому, кто подтвердил), либо Я не получил(а) (оспаривает, запись не создаётся).
  3. Для контрактора без аккаунта: Записать как полученное/не полученное (договорились вне платформы) — то же разделение «только менеджер, только для исполнителя без аккаунта», что и в пути принятия офера.
  4. Менеджер, подавший отчёт, может Отменить отчёт о платеже, чтобы отозвать своё же заявление (например, опечатку в сумме), не дожидаясь, пока получатель его оспорит.
  5. paidAmount/lastPaymentConfirmedAt у выплаты вычисляются из её подтверждённых строк FinanceEntry — выплата может быть оплачена несколькими частичными подтверждёнными платежами, никогда не считается оплаченной полностью только потому, что она ACCEPTED.

Баннер на дашборде (/team/dashboard) ​

«Мои выплаты» — показывается только когда у текущего пользователя есть что-то, ожидающее именно его ответа, сразу по всем задачам: предложенный (PROPOSED) офер (принять/отклонить условия) или принятая (ACCEPTED) выплата с ожидающим отчётом о платеже (подтвердить/оспорить получение) Закрывает случай, когда человек ещё не знает, какую именно задачу открывать (исправлено 2026-07-30 — раньше это было видно только при открытии конкретной задачи).

Бухгалтерия владельца, книги отделов, финансы проекта ​

Бухгалтерия не уведомляет никого. Запись дохода, бюджета или расхода — это ваш собственный учёт: никто его не ждёт, а деньги намеренно держатся вне общей ленты активности.

Исключение — выплаты, и они не на этой странице: они живут на карточке самой задачи, где каждое предложение и ответ сообщают другой стороне. См. Выплаты.

Тестирование этого модуля ​

Доступ к книге отдела только у её руководителя (включая отрицательный случай — «не руководитель») покрыт вживую постоянным набором scripts/e2e/menu/finance.mjs (4/4, можно перезапускать когда угодно — см. scripts/e2e/README.md).

Работа с книгой владельца, «Сверка», срез по проекту и полный цикл выплаты по задаче — задокументированный пробел живого покрытия по состоянию на 2026-08-21, реально не проверяются этим прогоном, хотя и написаны — миграция RBAC 2026-08-18/19 сделала workspace:finance:read/:manage доступными только уровню Owner в P4P Internal, без обхода для SUPER_ADMIN (осознанное решение — см. docs/staffing/IMPLEMENTATION_PLAN.md), а e2e-test-admin (выделенный аккаунт, от имени которого работает весь этот набор) держит только уровень «Team Admin», никогда «Team Manager»/Owner. finance.mjs сам проверяет это в начале прогона (hasFinanceAccess()) и чисто пропускает закрытые проверки одной явной записью вместо каскада запутанных таймаутов, а не молча делает вид, что покрывает их. Дать тестовому аккаунту уровень Owner рассматривалось и было отклонено — см. собственный комментарий у проверки (второй держатель роли платформы "P4P Owner" рискует сломать неупорядоченный поиск "текущего владельца" в PlatformRolesService.transferOwnership() для того, кто владеет им по-настоящему, на реальном локальном окружении разработки). Разветвление логики чтения на уровне компонента (руководитель vs workspace:finance:read), которое проверяют эти сценарии, по-прежнему покрыто юнит-тестами ниже — независимо от этого пробела в живом покрытии. У собственных вкладок Records, «Отчёты»/аналитика И «Сверка» книги владельца (/team/finances, все три блока 3-блочного разбиения раунда для этой страницы на 991 строку теперь готовы), у собственной книги отдела и у среза по проекту есть и свои юнит-тест-файлы (apps/portal/pages/team/finances/index.test.ts, apps/portal/pages/team/departments/[id]/finances.test.ts, apps/portal/pages/team/projects/[id]/finances.test.ts), детально покрывающие ветвление маршрута чтения по роли (руководитель vs workspace:finance:read), агрегацию бюджета по отделу (BUDGET/ EXPENSE суммируются по валюте, INCOME исключён), дельту «к прошлому периоду» в сводке по валютам, синхронизацию таба+периода с URL, создание/редактирование/удаление, собственные данные графика тренда (какая валюта выбирается по умолчанию, границы зума, INCOME исключён и здесь тоже), таблицы разбивки по проекту/отделу (INCOME исключён, запись без привязки попадает в «остаточный» бакет, а не молча пропадает из суммы), переход из разбивки в Records (клик по строке переключает вкладку и подставляет фильтр, сбрасывая фильтр другого измерения, а не оставляя его устаревшим), и собственную логику данных «Сверки» (comparisonRows группирует записи владельца и отдела по отделу × валюте, исключая INCOME с обеих сторон; сводка departmentCount/currencyCount/matchCount/ diffCount; переключатель «показать совпадения», по умолчанию скрывающий совпавшие строки; бейдж на табе, отслеживающий число расхождений и исчезающий, когда всё совпало; и то, что «Сверка» остаётся в рамках общего периода, но игнорирует фильтры Records — поиск/валюту/тип/проект/отдел). Само визуальное отображение графика (геометрия столбцов, всплывающие подсказки, зум как реально перетаскивается/кликается в браузере), отображение нескольких валют, пути отклонения/оспаривания и ручное подтверждение для контрактора без аккаунта — достаточно визуальные или разветвлённые, чтобы вместо этого числиться ручным проходом — см. docs/MANUAL_TESTING.md.