# Спецификация рабочего сервиса ресурсного планирования > Редакция 13.10.2026 по дополнению NEW-01–09 и реализации прототипа `5213150`. Расчётное поведение ниже принято для прототипа; инфраструктура, авторизация, интеграции и хранение рабочего сервиса остаются предложениями. QA прототипа завершена 13.10.2026; проверка рабочего сервиса выполняется после его реализации. [Расчётный контракт](calculation-contract.md), [матрица покрытия](addendum-coverage.md). Статус: **предложение к утверждению**, редакция 13.10.2026 по дополнению. Это спецификация поведения, а не разрешение начать реализацию. Принятые формулы и NEW-01–09 не требуют повторного согласования; рабочий сервис начинается после утверждения его архитектуры и относящихся вопросов подключения из `tasks.md`. Код в этой задаче не создаётся. ## Как читать действующие требования Это единая спецификация: изменения от 13.10.2026 включены в REQ-008/009/011/018/024/026/027 и уточнения ниже. Читать отдельное дополнение не требуется. При противоречии с историческим ТЗ v1.7 применяется эта редакция. Расчётные детали находятся в calculation-contract.md, проверки — в testing.md. Идентификаторы NEW сохранены только для прослеживаемости. ## Цель и источники РП запрашивает ресурсы; ресурсный менеджер (РМ) назначает сотрудников и принимает решения; согласованные назначения образуют единый план. Рабочий контур добавляет вход, серверные права, справочники, факт и журнал без изменения смысла формул прототипа. База: ТЗ v1.7 от 09.10.2026, фактические заголовки §1–7, §13–30; `../../tz-v1.7/tz.md`; анализ `../../analysis/{tz-issues,gap,plan}.md`; прототип `../../prototype-v2/js/{calc,api,seed}.js`, commit `5213150`. Отсутствующие §8–12 и AC 01–40 не считаются восстановленными: критерии ниже выведены из FR. Ошибочные ссылки ТЗ на §31–32 читаются по таблице в `tz-issues.md`. Документы не требуют доступа к исходным путям для чтения требований. Участники: РП (`pm`) — свои заявки и назначения разрешённых проектов; РМ (`rm`) — весь пул и решения. Технический оператор обслуживает подключения, но не получает автоматически роль РМ или расходные ставки. Должностная роль специалиста и право доступа пользователя — разные понятия. ## Требования и проверяемые критерии Каждая строка — отдельное требование; имя будущего теста содержит `[REQ-NNN]`. Уточнения рабочего контура помечены «предложение», окончательное решение — `open-questions.md`. | ID | Источник | Требование и критерий приёмки | | --- | --- | --- | | REQ-001 | §3–4, FR 01–02 | Заявка на один проект: 1–50 строк, 1–24 уникальных месяца на строку; форма предлагает 1–12. При пустом проекте, строках или повторе месяца сохранения нет. Создатель и РП определяются из разрешённого контекста, подмена имени не меняет владельца. | | REQ-002 | FR 01–04 | Роль из согласованного справочника; сотрудник необязателен, но соответствует роли; R > 0 и ≤ 1 000 000 ₽/ч; месячный FTE 0…1 и хотя бы один > 0. При смене роли сотрудник сброшен; совпавшие месяцы сохраняются, новые равны 0; комментарий ≤ 1000 символов. Новая заявка pending не резервирует часы. | | REQ-003 | §5, FR 05–06, 10 | Только pending допускает решение. РМ может изменить сотрудников, FTE и R, но не набор ролей; без сотрудника в любой строке согласование невозможно. approved/returned/rejected повторно не рассматриваются. | | REQ-004 | FR 07 | Согласование проверяет сумму FTE каждого рабочего дня по всем проектам, включая невидимые РП. Значение > 1 + 0,00001 блокируется; ровно 1 допускается. Две одновременные заявки на одного человека не могут обе превысить предел. | | REQ-005 | FR 08, 12–13 | При C > R нужен явный allowLoss; изменение сотрудника, FTE или ставки снимает подтверждение. Показываются текст, Δ и сумма превышения. C = R не требует подтверждения. Неизвестная C не подменяется нулём и блокирует решение до получения ставки. | | REQ-006 | FR 09–10 | Возврат и отказ требуют непустую причину ≤ 1000 символов. Возвращённая заявка исправляется созданием новой pending; исходная сохранена, в плане обе ничего не добавляют до согласования. | | REQ-007 | FR 11, 19; NFR 02 | Решение, назначение, версия и запись журнала сохраняются вместе. Устаревшая версия получает конфликт без частичной записи. При сбое введённые поля остаются в форме, повтор запроса с тем же ключом не дублирует решение. | | REQ-008 | Дополнение NEW-03; §1, §30; FR 34,37 | H сотрудника = Hкал − объединённые рабочие часы отпуска/больничного. P = FTE × H; для days — Σ(дневной FTE × доступные часы дня), явный 0 важнее месяца. Октябрь: Hкал=176, отпуск=16, больничный=8 → H=152, 0,5 FTE → 76 ч. Производственный календарь 2026–2027 учитывает праздники/переносы/7-часовые дни/рабочую субботу. Для «На подбор» до выбора сотрудника используется Hкал. Полнота и сводные часы — calculation-contract.md; отдельной нормы продажи нет. | | REQ-009 | Дополнение NEW-01/02/07; FR 35,40–41 | I=P×R, расход=P×C, разница=I−расход; все используют персональные часы REQ-008. Расход после дохода в списке и месячных строках; форма/карточка также показывают разницу периода. Отрицательная разница красная, нулевая/положительная зелёная в итогах формы/карточки; табличный расход красный только при отрицательной разнице. 88 ч, R=4000,C=4300 → 352000/378400/−26400 ₽. Неизвестная ставка или зависимые часы → прочерк в UI, пустая ячейка в CSV; при известном нулевом объёме ставки «На подбор» не становятся нулевыми. | | REQ-010 | §1, §6, §18 | Rср=Σ(P×R)/ΣP; Cср аналогично. При 40 ч ×4000 и 48 ч ×5000 Rср=4545,45 на экране. Старые месячные R сводятся с сохранением дохода; округление только при отображении/денежном результате, не до суммирования. | | REQ-011 | Дополнение NEW-05/06; FR 14–17,33 | План: сотрудник → проекты, 6 месяцев, единицы FTE/ч/%, поиск и раскрытие. Σ меняет единицу: часы — ΣP, FTE·мес — сумма месячных FTE, % — ΣP/ΣH×100. Нижний «Итог» учитывает каждого видимого сотрудника один раз, проекты — только расшифровка; свёртка не меняет итог. Поиск проекта оставляет полную загрузку найденных сотрудников, Итог равен этим строкам. Фильтры свободного ресурса/имени и область РП соблюдены; неизвестный FTE не считается свободным. Только approved первичных заявок и применённые изменения. Первый месяц в демо не позже 2027-07. | | REQ-012 | FR 16, 32, 38; NFR 01 | РП получает лишь свои заявки в разрешённых проектах и соответствующие назначения; общий сотрудник или проект не раскрывает заявки другого РП. Фильтры, суммы, CSV, история и детализация подчиняются той же области. РМ видит весь пул. | | REQ-013 | FR 18, 35, 57 | Отсутствующий факт — прочерк. После подключения подтверждённого источника рабочий план показывает его данные с датой актуальности; ручной факт прототипа не притворяется автоматически полученным фактом. Семантика покрытия Time требует Q-05. | | REQ-014 | FR 20–23 | Изменение относится к одному действующему назначению: release/reduce/replace; новых месяцев/ролей нет. Дата ≥ сегодня по Москве, причина непустая ≤ 1000. Для reduce/replace 0 < FTE ≤ 1; замена — другой активный сотрудник той же роли. После даты есть ненулевая загрузка. | | REQ-015 | FR 24 | До даты изменения дневной план неизменен. release обнуляет остаток; reduce берёт min(старый FTE, новый); replace освобождает исходного и задаёт новый FTE замене до конца исходного периода. Нулевые месяцы остаются. В эталонном ноябре 2026 вывод 0,5 FTE с 16.11 даёт 35,5 ч и 35,5/159 FTE при подтверждённых нулевых отсутствиях. | | REQ-016 | FR 21, 25–28 | На назначение не больше одного pending-изменения; создание не меняет план. У решения целевые параметры неизменяемы. Отказ/возврат освобождает возможность новой заявки, исходная история сохраняется. Завершённое назначение доступно только для просмотра. | | REQ-017 | FR 29–31 | При решении сверяется версия исходного назначения и снимок; изменение исходника блокирует устаревшее решение. Замена наследует R, C берётся с сервера для нового сотрудника; итоговый план повторно проверяется по дням, включая allowLoss. | | REQ-018 | Дополнение NEW-04/08/09; FR 36–38 | Отчёты: Утилизация, Финансы, Распределение, Некоммерческие проекты, Статистика (последние два перенесены внутрь Отчётов). Утилизация: общий фильтр месяц/роль/проект/имя для трёх показателей: план P/N, коммерческий план Pком/N, факт A/N. Коммерческие проекты — категория «Коммерческий» ИЛИ тип commercial; ограничивается только числитель. При неполном факте, неизвестной/нулевой N — прочерк с пояснением. Октябрь N=1040, P=432, Pком=256; ноябрь N=954,P=A=477 → 50%. Существующая пустая месячная сводка planSummary даёт 0%, новые кольца при N=0 дают прочерк. 12 недель: неделя с датами до 2026 целиком неизвестна, 2025 не достраивается. | | REQ-019 | FR 39–41, AC 44 | Финансы детализированы до назначений; средние взвешены часами; превышения считаются по строкам с C>R, а не по средней. 80ч×4000 и 40ч×6000 при C=4300 дают Rср=4666,67 и одну строку превышения. | | REQ-020 | FR 42–45 | Распределение объединяет назначения сотрудника на проекте; состояние имеет допуск 0,001 п.п.; дата освобождения — следующий календарный день за последним ненулевым рабочим днём всего плана проекта. Последний день 27.11.2026 → 28.11.2026. ЦФО и подразделение показывают введённые значения, при отсутствии — прочерк. Это принятый отдельный фикс. | | REQ-021 | FR 38, 45; AC 48 | CSV поддержанных отчётов соответствует отфильтрованной таблице, UTF-8 BOM, разделитель ;, десятичная запятая, даты YYYY-MM-DD; значения с разделителем экранируются. Выгрузка не расширяет права. | | REQ-022 | FR 46–49 | Некоммерческий отчёт: план/факт, 3/6/12 месяцев, подразделение и имя, синхронное раскрытие. Итоги FTE суммируются. Нет типов проектов → сообщение о настройке; есть проекты, нет плана → 0. Любой пропуск факта делает итог неполным, известная детализация остаётся. | | REQ-023 | FR 50–53 | Статистика считает уникальных людей, не FTE; человек на двух проектах входит в обе группы, но один раз в общий итог. Текущие подразделения/должности/навыки/ЦФО/состояние/дата выхода не объявляются историческим кадровым срезом. Пустые значения → «Не указано». | | REQ-024 | Дополнение NEW-03; FR 54–56,63–64 | РМ меняет локальные сведения и ручной факт/отпуск/больничный. Пустое поле удаляет запись, 0 сохраняется. Факт — одно из fte 0…2 либо hours 0…2Hкал (при известной H также ≤2H); часы сохраняются напрямую, FTE умножается на H. Отсутствие — hours 0…Hкал либо dates (полные календарные дни месяца); поля взаимоисключающие. Объединение рабочих дат вычитается один раз, календарные выходные/праздники дают 0. Положительную сводку и датированные отсутствия одного месяца нельзя смешивать. Сумма отсутствий ≤Hкал; дубли записей и неверные даты отклоняются атомарно. До 10000 записей на поле. Импортированный факт и приоритеты — вопрос рабочего подключения, не реализованное поведение мок-API. | | REQ-025 | FR 58–60 | Категории: «Для внутреннего клиента», «Коммерческий», «НИОКР», «Служебные»; подробная категория выше общего типа. Без неё noncommercial → «Некоммерческий (без уточнения)», без типа → «Тип не указан». Одинаковая норма N используется для каждой категории и не суммируется между категориями. | | REQ-026 | Дополнение NEW-03; NEW-05/06; FR 60 | N=Σ персональных H выбранной группы, без повторного вычета отсутствий; колонки с Nбез удалены. Hкал отображается отдельно. Накопительные часы/нормы сначала суммируются, потом делятся. 80+132 при 160+176 → 63,1%. Для периода 0,5/1/0,75 FTE: ΣFTE·мес=2,25, 388/512×100=75,78125%, не сумма или среднее процентов. Категории не меняют состав знаменателя, нормы категорий не складывают. | | REQ-027 | Дополнение NEW-03/09; FR 61–64 | Факт полон при записи для каждого выбранного сотрудника×проекта×месяца, явный 0 — запись. Нет проектов выбранного типа → часы=0. Для H нужны обе записи отпуск/больничный: пропуск любой делает H, N и зависимые проценты неизвестными; известная календарная норма и явные часы факта остаются. Известный нулевой FTE даёт 0 часов работ даже при неизвестной H. H=0 → 0 плановых часов, процент/FTE от H — прочерк. Отсутствия не входят в A и не вычитаются повторно. Полнота и объединение дат — calculation-contract.md. | | REQ-028 | INT 01 | У проекта стабильный ID и явная связь с РП. Переименование не создаёт дубль. Смена сущности Сферы меняет сопоставление источника, а не идентификаторы заявок; выбор владельца и сущности блокирует живой импорт (Q-01). | | REQ-029 | INT 02 | Сотрудники/подразделения/роли обновляются без удаления согласованных назначений. Деактивация запрещает новый вход и новые назначения; закрытый проект запрещает новые заявки. Частичный ответ источника не считается массовым удалением. | | REQ-030 | INT 03 | Один мастер заявок/назначений; предложение — сервис. Повтор синка справочников не меняет решения. Запись в Сферу отсутствует до отдельного решения Q-02. | | REQ-031 | INT 04 | Секреты доступны только серверному подключению; их нет в странице, журнале ошибок и выгрузке. Повтор синка идемпотентен; недоступность источника сохраняет последний целый снимок и показывает устаревание. | | REQ-032 | NFR 01 | Неавторизованный пользователь не получает бизнес-данных. Роль РМ не выводится из должности. Недоступный объект не раскрывается; операции решений доступны только РМ. Расходные ставки и производные суммы скрыты до явного права (Q-03). | | REQ-033 | NFR 02 | Журнал каждого создания/решения/изменения содержит неизменяемые actorId, время, причину, before/after, версии и признак принятого превышения; первичный запрос не теряется. Нельзя сохранить назначение без записи журнала. Автор не принимается из тела запроса. | | REQ-034 | NFR 03 | Перед рабочим запуском утверждены объём данных, срок хранения, удаление, RPO/RTO и нагрузка (Q-09); восстановление проверено на отдельной БД. Проверка мониторинга обнаруживает искусственный сбой синка и показывает последнее успешное обновление. Числовой SLA пока не установлен. | | REQ-035 | §30, требование STT-232 | Повтор импорта одного набора часов не удваивает факт; корректировка/удаление исходной записи отражается после успешной сверки. Неопознанный сотрудник/проект не сопоставляется по имени автоматически и не попадает в итог как 0. | | REQ-036 | §1, §30, рабочий контур | Календарь и ставки имеют источник и версию. Изменение текущей ставки не переписывает снимок согласованного назначения. Невалидный/неполный календарь не активируется. H использует версии календаря и отсутствий; политика исторического пересчёта рабочего сервиса вне пакета прототипа, её нельзя считать утверждённой дополнением. | | REQ-037 | требование STT-232 | Расчёты, переходы и синк воспроизводимы с фиксированными временем, ID и входными данными без живых внешних систем; каждый модуль проверяется через публичный интерфейс. | | REQ-038 | требование STT-232 | Тестовый вход позволяет проверить обе роли без настоящего входа только в тестовом окружении; в рабочей сборке тестовый маршрут отсутствует даже при переданных флагах включения фейков. | ## Жизненные циклы ```mermaid stateDiagram-v2 [*] --> pending: create или change pending --> approved: РМ согласует pending --> returned: РМ возвращает с причиной pending --> rejected: РМ отклоняет с причиной returned --> [*]: новая исправленная заявка с другим ID rejected --> [*] approved --> [*]: решение неизменно ``` Назначение появляется только при approved первичной заявки. `active`/`ended` — вычисляемые состояния по последнему ненулевому дню и Clock; `changePending` — отдельный признак. Решение по change атомарно заменяет текущую версию назначения и сохраняет прошлую. Истечение периода не удаляет запись. ## Переходы назначения для L1 | Состояние | Событие и guard | Результат | | --- | --- | --- | | Не существует | approved первичной заявки, все проверки пройдены | Первая версия назначения; active/ended определяется датой и планом | | active, без pending-change | proposeChange, есть будущая ненулевая загрузка | Та же версия плана + changePending; часы не меняются | | active, changePending | approve change, снимок/версии совпали | Новая версия плана, changePending снят; состояние вычисляется заново | | active, changePending | return/reject с причиной | Версия плана прежняя, changePending снят | | active | Clock прошёл последний ненулевой день | ended, данные и история не меняются | | ended | proposeChange без будущей ненулевой загрузки | Отказ INVALID_TRANSITION, запись не меняется | При выводе с будущей даты статус остаётся active до последнего ненулевого дня; решение не означает немедленного завершения. Если согласование запоздало относительно effectiveDate, оно применяется к сохранённому запросу по правилам снимка FR 29; политика запрета ретроактивного решения, если потребуется, должна быть отдельно согласована — ТЗ ограничивает дату при создании запроса. ## Граница утверждения Нерешённые вопросы рабочего подключения блокируют только перечисленные в `open-questions.md` рабочие подключения/запуск, а не подготовку документов. До ответов разрешены проектирование и будущие тесты на синтетических данных после утверждения спеки. Исторические дефекты публичного прототипа (имена вместо ID, отсутствие прав, локальное хранилище) не становятся требованиями к сервису. ## Уточнения календаря, норм и расчётов **Правила, выведенные из документов (вопросами к автору не являются)** | Правило | Источник | |---|---| | Hкал(месяц) — часы производственного календаря РФ для 40-часовой пятидневки. Праздники и переносы исключены, предпраздничный день на 1 ч короче, рабочие субботы по переносу считаются. Праздники повторно не вычитаются | DOCX п.3; ТЗ `tz.md:14` («праздники и отпуска должны быть учтены»); `tz.md:726` («норма часов с праздниками и сокращёнными днями») | | H сотрудника = Hкал − часы отпуска − часы больничного за месяц. Отсутствия вводятся только в рабочих часах (≤ Hкал), день, в который совпали оба вида, учитывается один раз. Больничные не прогнозируются: только введённые значения | DOCX п.3; границы задачи | | Часы дня в дневном плане: P = Σ(доступные часы дня сотрудника × дневной FTE). «× 8» из FR 37/FR 24 — это частный случай дня по 8 ч | FR 34, FR 37 с календарём `tz.md:726` | | N = ΣH выбранных сотрудников. N − V не считается: отпуск уже внутри H. Колонки «без отпусков» объединяются с «по норме», доля каждого вида отсутствия считается от ΣHкал; общую долю считают по объединению дат, не по V + B | DOCX п.3; формулы FR 60/§26 при новой H | | Пустое значение отсутствия не равно 0. Если у выбранного сотрудника за месяц нет записи об отпуске или больничном, его H, N группы и проценты по N показываются прочерком. ΣHкал («норма по календарю») и явно введённые часы факта остаются видимыми; зависимые плановые часы неизвестны. После ввода 0 значение становится известным. В демо явно заполнены подтверждённые значения, включая нули | FR 62, AC 61 (видимая величина — календарная норма, как раньше видимой была N) | | Накопительно и за период: сначала сумма часов, сумма норм, затем процент | `tz.md:41`, FR 60, AC 60 | | Σ в плане: FTE — сумма месячных FTE, % — ΣP / ΣH × 100, часы — ΣP | Глоссарий Σ `tz.md:32`; `tz.md:41` | | «Итог» плана — сумма строк сотрудников (строки проектов — их расшифровка, повторно не прибавляются). По FTE — сумма, по % — ΣP / ΣH. При поиске по проекту строки сотрудников, как и сейчас, показывают полную загрузку, Итог — их сумма | DOCX п.6 «суммируются значения по строкам»; текущее поведение `plan.js` | | NEW-08 — плановые часы проектов группы «Коммерческий» (категория или общий тип commercial) / N | DOCX п.8 «по форме плановой утилизации»; AC 64; FR 36 | | NEW-09 — A / N × 100; при неполном факте прочерк, при N = 0 прочерк | DOCX п.9; FR 61, AC 62 | | Финансы: расход = P × C, разница = I − расход. Неизвестная C — прочерк, не 0. Цвета: в форме и карточке отрицательная разница красная, остальное зелёное; в таблицах при отрицательной разнице расход красный, иначе без цвета | DOCX п.1, 2, 7; FR 35 | **Календарь в данных прототипа (часы на 40-часовую неделю)** | | янв | фев | мар | апр | май | июн | июл | авг | сен | окт | ноя | дек | год | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | 2026 | 120 | 152 | 168 | 175 | 151 | 167 | 184 | 168 | 176 | 176 | 159 | 176 | 1972 | | 2027 | 120 | 151 | 176 | 175 | 152 | 167 | 176 | 176 | 176 | 168 | 159 | 176 | 1972 | Ловушки реализации: - 2026 — 31.12 выходной, 03.11 сокращён. - 2027 — суббота 20.02 рабочая и сокращённая; выходные 22.02, 05.11, 31.12. - Праздник, выпавший на выходной (09.05.2026; 01.05 и 09.05.2027), переносится на ближайший рабочий день по ст. 112 ТК. - Границы календаря 2026-01…2027-12 едины для API и UI: - первый месяц плана ограничить ≤ 2027-07; - месяцы заявок — ≤ 2027-12; число месяцев формы уменьшается у границы; первый месяц некоммерческого отчёта ≤ 2027-01 для полного периода до 12 месяцев; - на месяц без календаря `api.js` отвечает 400. ## Принятая формула P и денег Действующая постановка: P = FTE × H сотрудника после вычета отсутствий; доход = P × R, расход = P × C. Повторного согласования не требуется. При дневных изменениях используется сумма дневного FTE × доступные часы дня, исходные месячные ставки сохраняются. Отдельной нормы продажи нет; новый процесс пересогласования не вводится. Анна, октябрь 2026: календарь 176 ч, отпуск 16 ч, больничный 0 → H = 160. При 0,5 FTE и R/C = 3500/2800: P = 80 ч, доход 280 000, расход 224 000 ₽, утилизация 50 %. Больничный 8 ч → H = 152, P = 76 ч, доход 266 000, расход 212 800 ₽. Изменение введённых отсутствий меняет расчёты прототипа во всех представлениях. У назначенного сотрудника пустой отпуск или больничный означает неизвестную H: зависимые P, деньги и проценты — прочерк. Явно введённые фактические часы остаются видимыми. Для строки «На подбор» персональной нормы ещё нет: до назначения используется календарь; после выбора сотрудника расчёт использует его H. Отпуск/больничный: запись `{employee, month, hours}` — подтверждённые рабочие часы без пересечений; `{employee, month, dates}` — полные дни с точным объединением дат. Даты праздников/выходных дают 0 ч, сокращённые — 7 ч. Для сводных часов без дат дневная доступность распределяется пропорционально календарным часам: это допущение демо, точные дни задаются датами. Положительные сводные часы и датированные отсутствия в одном месяце не смешиваются: API просит указать оба отсутствия датами. Пустое поле удаляет запись, 0 или пустой массив дат подтверждают отсутствие. API атомарно отклоняет сумму свыше календаря и неверные даты. Мок принимает фактический `fte` либо явные `hours` (одно из двух). FTE преобразуется через персональную H; явные часы не теряются при неизвестных отсутствиях. При согласовании требуется подтверждённая H назначенных сотрудников; сохранение заявки с неизвестной H допускается. Проверка перегрузки использует рабочие дни, в том числе рабочую субботу, и исключает подтверждённые дни без доступных часов. Источники календаря, проверенные 13.10.2026: [2026](https://www.consultant.ru/law/ref/calendar/proizvodstvennye/2026/), [2027](https://www.consultant.ru/law/ref/calendar/proizvodstvennye/2027/), [постановление №1187](https://www.consultant.ru/document/cons_doc_LAW_544706/). Федеральная пятидневка на 40 ч; региональные календари в демо не включены.