Финансы — бухгалтерия владельца, книги отделов, выплаты по задачам
Простыми словами: денежная сторона Персонал — конфиденциальная бухгалтерия, которую ведёт владелец платформы, отдельная независимая бухгалтерия, которую ведёт каждый руководитель отдела для своего отдела, переговоры о выплате по каждой задаче с исполнителем, и сверка, которая сравнивает записи владельца с записями руководителей, никогда их не синхронизируя.
Живёт внутри 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: старое «Отметить как оплачено» создавало запись сразу же, только на основании отчёта менеджера).
- Сообщить о платеже (только
workspace:finance:manage— не руководители отделов, см. таблицу прав выше) — сумма/дата/описание, не больше остатка к оплате по этой выплате. Фиксирует только заявление;FinanceEntryпока не создаётся. В моменте может быть только один открытый отчёт по выплате. - Исполнитель видит баннер («Владелец сообщает об отправке X числа Y — дошло?») и либо Подтверждает получение (создаёт реальную запись
EXPENSE, приписанную тому, кто сообщил о переводе, а не тому, кто подтвердил), либо Я не получил(а) (оспаривает, запись не создаётся). - Для контрактора без аккаунта: Записать как полученное/не полученное (договорились вне платформы) — то же разделение «только менеджер, только для исполнителя без аккаунта», что и в пути принятия офера.
- Менеджер, подавший отчёт, может Отменить отчёт о платеже, чтобы отозвать своё же заявление (например, опечатку в сумме), не дожидаясь, пока получатель его оспорит.
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.