# Спецификация 05: «Профиль», «Библиотека», «Настройки», ZoneCalculator Источники: Ф-6 (профиль/зоны), Ф-4 (библиотека), tech-req 4.1 (настройки+валидация), ОВ-1 (Танака). Единицы 7–9 + сервис 11. ## 1. «Профиль» (Ф-6) `ProfileViewModel`: `profile` (Flow), сохранение — `WorkoutCore.saveProfile(sex, birthYear, weightKg, hrMax)` → sync-сущность `profile` (guid-стабильность — m09). | Поле | Ввод | Диапазон | |---|---|---| | Пол | сегмент м/ж/не указан | — | | Год рождения | пикер | 1930–2010 | | Вес | пикер шаг 0,5 кг | 30–300 | | Макс. пульс | пикер 120–240 или «считать по формуле» (Танака) | 120–240 / авто | `ZoneCalculator` (чистая): `maxHr = hrMax ?: 208 − 0,7 × возраст` (округление до целого; возраст = текущий год − birthYear; без birthYear и hrMax — зоны не показываются); зоны по Ф-6.2: z1 < 50 %, z2 50–69 %, z3 70–84 %, z4 ≥ 85 % (границы в уд/мин, таблица; те же единицы, что на часах). Зоны — показ; на часы — m12. ## 2. «Библиотека» (Ф-4) `LibraryViewModel`: `exercises` (Flow), переключатель «показать исключённые». Операции: - «Добавить» — диалог: название (уникальность проверяет БД, UNIQUE `exercise.name` — сообщение «уже есть»), группа (сегмент из 5), базовое/изоляция (переключатель); - «Переименовать» — диалог с текущим названием (UUID не меняется 🛡 Ф-4.3); - «Исключить» — подтверждение «История тренировок сохранится; упражнение исчезнёт из выбора» → `deactivateExercise` (🛡 только логическое удаление, Ф-4.2; восстановления в v1 нет — решение З-41/m02); Запись — `WorkoutCore.saveExercise`; is_active=false уходит на сервер повторной отправкой упражнения (контур З-41(а)). ## 3. «Настройки» (tech-req 4.1) `SettingsViewModel`: чтение/сохранение `AppSettings` (расширение m09: + `hrMaxBpm`, `hrStrengthMax`); валидация перед сохранением («Сохранить» неактивна, ошибки — построчно): | Поле | Правило | |---|---| | `baseUrl` | абсолютный `https://` URL (host непустой); http допустим только для отладки — предупреждение текстом (docker-сервер) | | `apiKey` | непустой; поле-пароль (маска), 🛡 не логируется и не выводится полностью | | `wifiOnly` | — | | `hrMaxBpm` | 120–240 или пусто (авто-Танака) | | `restDefaultSec` | 30–600 | | `bpCancelSys/bpCancelDias` | 120–200 / 70–130 | | `bpCardioSys/bpCardioDias` | 120–200 / 70–130; ≤ соответствующего Cancel (tech-req 4.1) | | `restingHrSitting` | 40–120 (умолчание 90) | | `hrStrengthMax` | 100–180 (умолчание 120, З-20; применяется в m12) | Секция «Синхронизация»: счётчик очереди, кнопка «Синхронизировать сейчас» → `SyncWorker.scheduleNow` (spec 01 п. 4), результат — баннер (spec 01 п. 3). Первый запуск — экран открывается гейтом (spec 01 п. 2). ## 4. Тесты (unit, pure JVM) | # | Проверка | Ожидание | |---|---|---| | 1 | `ZoneCalculator`: 1980 г.р. в 2026, без hrMax | maxHr 176 (208 − 0,7×46 = 175,8, математическое округление) | | 2 | `ZoneCalculator`: ручной hrMax 190 | границы 95/133/162; ручной приоритетнее формулы | | 3 | `ZoneCalculator`: нет данных | зоны не показываются (null) | | 4 | Валидация baseUrl: `https://host/path` ок; `http://…` предупреждение; `ftp://`, `host`, пусто | ошибка/предупреждение по правила | | 5 | Валидация Safety: bpCardioSys > bpCancelSys | ошибка «кардио-порог выше порога отмены» | | 6 | Валидация диапазонов (restDefaultSec 10; hrMaxBpm 250; hrStrengthMax 90) | построчные ошибки |