Направление работ: WebLk (фронт, кабинет партнёра) Код раздела: 4.4 Цены Версия: 0.5 Обновлено: 27.07.26 Зависит от: Back 2.6
UI-слой кабинета партнёра. Описывает экран настройки цен, список цен, матрицу стоимостей, вкладки доп. цен, фильтры, состояния интерфейса.
Ключевые понятия
- Цена, Стоимость.
- Матрица стоимостей — столбцы: дни недели Пн–Вс; строки: временные слоты в рамках режима работы.
- Условия применения цены — период, объект, площадка, опции заказа, диапазон посетителей, «День в день»; день недели и время — в матрице.
- Временной слот.
- Стандартная цена — первая цена для площадки, автоназвание «Стандартная цена».
- Дополнительная цена — уточнение стандартной, отдельная вкладка, автоназвание («Условие 1» / «Цена от 5 до 10 посетителей»).
- Экран настройки цен.
- Доплата за превышение количества посетителей.
- Список цен — группировка по статусам: действующие / будущие / архивные.
- Пара цен «Заранее/День в день», Цена «Заранее», Цена «День в день».
- Статус цены — действующая / будущая / архивная.
Scope
Входит
- S1 — Создание/заполнение стандартной цены (матрица)
- S2 — Условия стандартной цены + доплата
- S3 — Создание доп. цен в вкладках
- S4 — Список цен и фильтрация
- S5 — Редактирование (матрица, площадки, период, будущая стоимость, архивация, восстановление, удаление)
- S6 — Групповые действия
- S7 — Состояния интерфейса
Функциональные требования
| ID | Сценарий | Функциональное требование | BR | Вес | Статус |
|---|---|---|---|---|---|
| S1: FR4.4-1 | Создание цены начинается с выбора площадки. | Без площадки создать/сохранить нельзя. | BR2.6-2, BR2.6-13 | 1 | Частично \ #OQ4.4-15 |
| S1: FR4.4-2 | Начало настройки. | Настройка начинается со стандартной матрицы, автоназвание «Стандартная цена». | BR2.6-3 | 2 | Реализовано |
| S1: FR4.4-3 | Заполнение матрицы. | Выбор одной/нескольких ячеек матрицы (одиночно, по дню недели, по времени, вся матрица), задать общее значение, Enter/переход закрепляет. | BR2.6-3 | 2 | Реализовано |
| S1: FR4.4-4 | Сохранение стандартной цены. | Проверка незаполненных стоимостей, сохранение при успехе или причина отказа. | BR2.6-15 | 1 | Частично \ #OQ4.4-8 |
| S2: FR4.4-5 | Условия применения стандартной цены. | Период, объект(ы), площадку(и), опции заказа, диапазон посетителей, доплата за превышение. Незаданное условие = все допустимые значения. | BR2.6-2, BR2.6-4 | 1 | Реализовано |
| S2: FR4.4-6 | Вариант «День в день» для стандартной цены. | «День в день» сохраняется только при наличии «Заранее» с теми же условиями. «Заранее» может без «День в день». Стоимости в матрицах могут отличаться. | BR2.6-16 | 2 | Частично \ #OQ4.4-3 |
| S2: FR4.4-7 | Доплата за превышение количества посетителей. | При заданном диапазоне — включить доплату за каждого сверх лимита, сумма за единицу времени. Без диапазона нельзя. | BR2.6-5 | 2 | Реализовано |
| S2: FR4.4-8 | Сохранение стандартной цены с доплатой. | При отключении доплаты базовая стоимость сохраняется, правило деактивируется. | BR2.6-5 | 2 | Реализовано |
| S3: FR4.4-9 | Добавление дополнительной цены. | Уточнение стандартной, отдельная вкладка, автоназвание. Может копировать параметры и матрицу из предыдущей цены. | BR2.6-1 | 2 | Частично \ #OQ4.4-2 |
| S3: FR4.4-10 | Условия и матрица доп. цены. | Для покрытия сценариев, не покрытых стандартной (другие диапазоны, опции, периоды, длительность). | BR2.6-2, BR2.6-4 | 2 | Частично \ #OQ4.4-2 |
| S3: FR4.4-11 | Изменение матрицы доп. цены. | До сохранения можно отменить несохранённые изменения. | BR2.6-3 | 2 | Частично \ #OQ4.4-10 |
| S3: FR4.4-12 | Несколько доп. цен во вкладках. | Если не помещаются — скрытие/сворачивание, доступ к скрытым сохраняется. Индикатор блокирующей ошибки в скрытой цене. | — | 3 | Реализовано |
| S4: FR4.4-13 | Список цен карточками. | В карточке: название, матрица, условия. Группировка по статусам действующие/будущие/архивные со счётчиком. Пара «Заранее/День в день» — сгруппированно. | BR2.6-7 | 1 | Частично \ #OQ4.4-4 |
| S4: FR4.4-14 | Переключение статуса. | «Все»/«Действующие»/«Будущие»/«Архивные». | BR2.6-7 | 2 | Не реализовано \ #OQ4.4-5 |
| S4: FR4.4-15 | Фильтры по условиям применения. | Объект/площадка, опции заказа (вид работ/услуг), диапазон посетителей и доплата, признак «День в день», длительность заказа. Фильтрация уже загруженного списка. | BR2.6-2 | 2 | Частично \ #OQ4.4-13 |
| S4: FR4.4-16 | Добавление цены из списка. | Переход в flow создания. | — | 3 | Реализовано |
| S5: FR4.4-17 | Открытие существующей цены на экране настройки. | По умолчанию редактируемая цена и связанные версии по статусу. Возможность отобразить все цены площадки/объекта (стандартная + доп. во вкладках). Набор действий зависит от статуса. | BR2.6-6 | 2 | Частично \ #OQ4.4-14 |
| S5: FR4.4-18 | Редактирование стоимостей матрицы. | После изменения доступны сохранение и отмена. | BR2.6-6 | 2 | Частично \ #OQ4.4-10 |
| S5: FR4.4-19 | Управление площадками. | Мин. одна площадка. Цена может применяться к нескольким площадкам. У площадки несколько цен если условия не совпадают. Конфликт при добавлении — предупреждение и подтверждение. Удаление, нарушающее обязательную привязку — отказ с причиной. | BR2.6-13, BR2.6-14 | 1 | Частично \ #OQ4.4-11 |
| S5: FR4.4-20 | Изменение периода действия. | Включая бессрочный. Проверки по BR. Успех — обновление карточки, ошибка — причина отказа. | BR2.6-2 | 2 | Реализовано |
| S5: FR4.4-21 | Добавление будущей стоимости. | Период начинается после завершения текущего; после успеха — в группе будущих цен. | BR2.6-6 | 2 | Частично \ #OQ4.4-6 |
| S5: FR4.4-24 | Восстановление архивной. | Проверки по BR. Конфликт с активными — блокировка, причина отказа. | BR2.6-8 | 2 | Частично \ #OQ4.4-12 |
| S5: FR4.4-25 | Сохранение изменений. | Проверка BR, сохранение или отказ. После успеха — возврат к списку. | BR2.6-6 | 1 | Реализовано |
| S6: FR4.4-26 | Режим выбора цен. | Одна/несколько/все. «Все» = все статусы. Показ количества выбранных. | BR2.6-10 | 2 | Реализовано |
| S6: FR4.4-27 | Массовое действие. | Набор действий зависит от статусов выбранных. Обработка каждой отдельно, результат по каждой. Только в рамках одной партнёрской зоны. | BR2.6-10, BR2.6-11, BR2.6-12 | 2 | Частично \ #OQ4.4-16 |
| S7: FR4.4-28 | Состояния списка. | Список загружается/отсутствует/не загрузился — состояние загрузки, пустой список с созданием, ошибка с повтором. | — | 2 | Не реализовано \ #OQ4.4-7 |
| S7: FR4.4-29 | Действие, требующее ответа системы. | Предотвращение дублирования, результат после ответа, при ошибке остаётся в контексте с повтором. | — | 2 | Реализовано |
| S7: FR4.4-30 | Изменение условий применения. | Проверка конфликта применимости. Конфликт = совпадение/пересечение условий и слотов. Одинаковые стоимости по разным условиям не конфликт. При конфликте — пометка цены/вкладки блокирующей ошибкой и расшифровка. | BR2.6-14 | 1 | Не реализовано \ #OQ4.4-1 |
| S7: FR4.4-31 | Изменение матрицы. | Проверка заполненности слотов. Пустые значения — пометка блокирующей ошибкой и расшифровка. | BR2.6-15 | 1 | Частично \ #OQ4.4-9 |
Открытые вопросы
| ID | Источник | Вопрос | Кому | Статус | Рекомендации |
|---|---|---|---|---|---|
| #OQ4.4-1 | Код FR4.4-30 | Требование говорит: при изменении условий применения интерфейс проверяет конфликт применимости и помечает цену/вкладку блокирующей ошибкой с расшифровкой. В коде проверки конфликта на фронте нет ни на одном экране: метод бэкенда validateUniqueness есть в сервисе и сторе, но у него ноль вызовов в компонентах; сетка умеет рисовать конфликтные ячейки (conflictCells), но для цен их никто не передаёт. Конфликт обнаружится только ответом бэкенда при сохранении. \ prices.ts:366; PricesService.ts:282; PriceGrid.vue:169 | Бизнес/Разработка | Open | Требование веса 1 не реализовано. Метод и отрисовка уже есть — не хватает связки. (А, рекоменд.) вызывать validateUniqueness при изменении условий/матрицы и передавать conflictCells в сетку с расшифровкой; (Б) если проверка целиком делегирована бэкенду при сохранении — переформулировать FR4.4-30 (не «до сохранения», а «по ответу»), но это хуже по UX. Правила конфликта — Back 2.6/BR2.6-14. |
| #OQ4.4-2 | Код FR4.4-9, FR4.4-10 | Требование говорит: «Дополнительная цена» — отдельный тип (уточнение стандартной, своё автоназвание «Условие 1», для непокрытых сценариев). В коде отдельного типа нет: кнопка «+» клонирует текущую пару в новую вкладку тем же компонентом с тем же базовым именем — это ещё одна обычная стандартная пара, а не типизированное уточнение. \ create.vue:139; pricesActions.ts:913 | Бизнес (Герман) | Open | Решение: нужен ли отдельный концепт «дополнительной цены» или достаточно клонированных вкладок. (А) ввести тип «дополнительная цена» с собственным автоназванием и семантикой уточнения (ближе к BRD); (Б) если различие только визуальное — привести BRD к коду, описав доп. цену как дополнительную вкладку-клон без отдельного типа. Модель цен — Back 2.6/BR2.6-1. |
| #OQ4.4-3 | Код FR4.4-6 | Требование говорит: «День в день» сохраняется только при наличии «Заранее»; «Заранее» допустима без «День в день» (асимметрия). В коде цена всегда пара (pre+today): проверки требуют обе стороны (PRICE_MISSING_TODAY/PRICE_MISSING_PRE), то есть «Заранее» в одиночку не разрешена — асимметрия нарушена. \ pricesActions.ts:497-507,913 | Бизнес/Разработка | Open | Связь «today только при наличии pre» соблюдена, но код требует обе стороны, а BRD — нет. (А, рекоменд.) разрешить «Заранее» без «День в день» (сделать today-сторону опциональной в модели и валидации); (Б) если пара обязательна по бизнесу — привести BRD к коду и убрать асимметрию из FR4.4-6. Правило — Back 2.6/BR2.6-16. |
| #OQ4.4-4 | Код FR4.4-13 | Требование говорит: список группируется по статусам действующие/будущие/архивные со счётчиком. В коде основной список не сгруппирован по статусам и без счётчиков-заголовков: архивные показаны пунктиром с префиксом «Архив:», а Прошлые/Будущие — секции внутри раскрытой карточки, а не группы списка. \ PriceCard.vue:5,149 | Бизнес/Разработка | Open | (А, рекоменд.) сгруппировать список по трём статусам с заголовками-счётчиками, как требует FR4.4-13; (Б) если продукт согласен на текущий визуал (пунктир + секции в карточке) — переформулировать требование. Связано с OQ4.4-5 (фильтр по статусу) — решать вместе. Статусы — Back 2.6/BR2.6-7. |
| #OQ4.4-5 | Код FR4.4-14 | Требование говорит: переключатель статуса «Все / Действующие / Будущие / Архивные». В коде такого переключателя нет: только бинарный тумблер «Только активные» (причём displayedPrices его фактически не читает — фильтр не применяется), а выбор по категориям используется для выделения, не фильтрации; список всегда грузится с ShowArchived: true. \ PricesToolbar.vue:15; index.vue:197,543 | Бизнес/Разработка | Open | Требование по сути не реализовано, плюс существующий тумблер не работает (мёртвый фильтр). (А, рекоменд.) сделать полноценный фильтр по 4 статусам, применяемый к displayedPrices; (Б) если достаточно «Все/Активные» — привести BRD к коду и починить нерабочий тумблер. Связать с OQ4.4-4. |
| #OQ4.4-6 | Код FR4.4-21 | Требование говорит: «добавить будущую стоимость» — период начинается после завершения текущего; после успеха попадает в группу будущих цен. В коде отдельного flow нет: будущие цены создаются обычным flow с более поздними датами, цепочки сезонности определяются по соседству дат постфактум; проверки «новый период начинается после завершения текущего» нет. \ usePriceContinuations.ts:46; settings/create.vue | Бизнес/Разработка | Open | (А, рекоменд.) если проверка «после завершения текущего» — бизнес-правило, добавить её (на фронте до сохранения) и явный вход «добавить будущую стоимость»; (Б) если достаточно обычного создания с поздними датами — переформулировать FR4.4-21. Правило периодов — Back 2.6. |
| #OQ4.4-7 | Код FR4.4-28 | Требование говорит: состояния списка — загрузка, пустой с созданием, ошибка с повтором. В коде стор знает loading/error/isEmpty, но главная страница списка не рендерит ни индикатор загрузки, ни баннер ошибки, ни кнопку повтора; пустое состояние без CTA «создать» (кнопка создания живёт в тулбаре). \ prices.ts:15-17; index.vue | Разработка (Влад) | Open | Флаги в сторе есть, не хватает их отрисовки. Продуктового выбора нет. Рекомендация: отрендерить на странице списка состояние загрузки, баннер ошибки с «Повторить» и пустое состояние с CTA «создать цену» (используя существующие loading/error/isEmpty). |
| #OQ4.4-8 | Код FR4.4-4 | Требование говорит: при сохранении стандартной цены при отказе показывается причина. В коде проверка незаполненных стоимостей есть, но на пути сохранения настройки при ошибке бэкенда причина пользователю не показывается — только console.warn; структурный разбор ошибки (extractErrorMessage) подключён лишь к действиям списка, не к сохранению на экране настройки. \ PriceEditPanel.vue:887-889; index.vue:496 | Разработка (Влад) | Open | Пользователь не видит причину отказа при сохранении — требование веса 1. Продуктового выбора нет. Рекомендация: подключить extractErrorMessage к пути сохранения на экране настройки и показывать причину отказа пользователю вместо console.warn. Связать с OQ4.4-7 (единый механизм показа ошибок). |
| #OQ4.4-9 | Код FR4.4-31 | Проблема: проверка заполненности матрицы (PRICE_EMPTY_GRID/PRICE_PARTIAL_GRID) реализована и блокирует сохранение с per-tab подсказкой, но работает только для новых черновиков (isNew); при редактировании существующей цены проверка заполненности матрицы не выполняется. \ pricesActions.ts:460-484; TabsContainer.vue:41 | Разработка (Влад) | Open | Требование веса 1 частично: при редактировании можно сохранить неполную матрицу. Продуктового выбора нет. Рекомендация: применять проверку заполненности слотов и к режиму редактирования существующей цены, не только к isNew. |
| #OQ4.4-10 | Код FR4.4-11, FR4.4-18 | Требование говорит: до сохранения можно отменить несохранённые изменения; после изменения матрицы доступны сохранение и отмена. В коде «Сбросить» доступна только в режиме создания (id начинается с new) и делает жёсткий сброс черновика к заводскому состоянию; для редактируемой существующей цены нет «отмены», возвращающей к последним сохранённым значениям — есть только сохранение. \ PriceEditPanel.vue:210-217; pricesActions.ts:1054 | Бизнес/Разработка | Open | (А, рекоменд.) добавить «отмену» для существующих цен, возвращающую к последним сохранённым значениям (а не заводской wipe); (Б) если отмена для существующих не нужна — уточнить FR4.4-11/-18. Затрагивает два FR веса 2. |
| #OQ4.4-11 | Код FR4.4-19 | Требование говорит: при добавлении площадки — предупреждение и подтверждение при конфликте; удаление, нарушающее обязательную привязку — отказ с причиной; мин. одна площадка. В коде подтверждение при изменении площадок есть, но это общий confirm, а не проверка конфликта по условиям применения; «мин. одна площадка» обеспечивается косвенно (PRICE_NO_OBJECTS) и только для новых черновиков; логики «удаление нарушает обязательную привязку → отказ с причиной» не найдено. \ PriceEditPanel.vue:164; pricesActions.ts:485-487 | Бизнес/Разработка | Open | Требование веса 1 закрыто частично. (А, рекоменд.) заменить общий confirm на проверку конфликта по условиям (связать с OQ4.4-1) и реализовать отказ с причиной при удалении обязательной привязки; распространить «мин. одна площадка» на редактирование; (Б) переформулировать FR4.4-19 под текущее поведение. Правила привязки — Back 2.6/BR2.6-13/-14. |
| #OQ4.4-12 | Код FR4.4-24 | Требование говорит: при восстановлении архивной — проверки по BR; конфликт с активными — блокировка и причина отказа. В коде проверки конфликта при восстановлении на фронте нет; комментарий в коде явно указывает, что проверка дат при восстановлении была убрана («можно восстанавливать с любыми датами»); UI требует лишь задать период, конфликт всплывёт только ответом бэкенда. \ pricesActions.ts:1733,1750; PriceEditPanel.vue:939 | Бизнес/Разработка | Open | Проверку осознанно сняли — нужно подтверждение, правильно ли это. (А, рекоменд.) вернуть проверку конфликта при восстановлении (блокировка + причина) на фронте, как требует FR4.4-24; (Б) если проверка целиком на бэкенде — зафиксировать это и обеспечить показ причины отказа из ответа. Правила восстановления — Back 2.6/BR2.6-8. |
| #OQ4.4-13 | Код FR4.4-15 | Требование говорит: фильтры по условиям, в т.ч. признак «День в день» и доплата; фильтрация уже загруженного списка. В коде клиентская фильтрация есть по виду работ, диапазону посетителей, приоритету, мин. длительности; фильтра по «День в день» и по доплате нет; фильтр по объекту/площадке вызывает повторный запрос на бэкенд (не чисто клиентский); сегменты диапазона людей захардкожены (1-5/5-10/10-15). \ index.vue:203-247,359; PricesToolbar.vue:108 | Бизнес/Разработка | Open | (А, рекоменд.) добавить недостающие фильтры «День в день» и «доплата» и перевести фильтрацию по объекту/площадке на клиентскую (по требованию «уже загруженного списка»); захардкоженные сегменты вынести в данные; (Б) если серверная фильтрация по объекту допустима — уточнить FR4.4-15. |
| #OQ4.4-14 | Код FR4.4-17 | Проблема: открытие цены строит вкладки только из цепочки группы открытой цены (single / pair / сезонная мульти-пара), а не из всех цен площадки/объекта; «все цены площадки» широко не подгружаются. \ settings/[id].vue:223-392 | Бизнес (Герман) | Open | Нужно подтверждение: показ ограничен группой намеренно или FR4.4-17 требует все цены площадки/объекта. (А) если нужны все цены площадки — подгружать и строить вкладки из них; (Б) если ограничение группой осознанно (иначе вкладок слишком много) — уточнить FR4.4-17. Продуктовое решение по объёму отображения. |
| #OQ4.4-15 | Код FR4.4-1 | Проблема: создание не начинается с шага выбора площадки: create.vue сразу создаёт черновик стандартной пары с пустыми bases/rooms, площадка выбирается позже. Запрет «сохранить без площадки» при этом обеспечивается (PRICE_NO_OBJECTS, блокировка кнопки). \ create.vue:101-105; pricesActions.ts:485-487 | Бизнес (Герман) | Open | Инвариант «без площадки не сохранить» соблюдён, но «начинается с выбора площадки» — нет. (А) сделать выбор площадки первым обязательным шагом создания (по FR4.4-1); (Б) если порядок шагов не важен (лишь бы нельзя было сохранить без площадки) — переформулировать FR4.4-1. Продуктовое решение о порядке шагов. |
| #OQ4.4-16 | Код FR4.4-27 | Требование говорит: набор массовых действий зависит от статусов выбранных; каждая обрабатывается отдельно, результат по каждой. В коде набор действий статичен (всегда Архивировать/Удалить/Изменить период), результат — агрегатный тост («Архивировано: N»), отчёта по каждой цене нет, одна ошибка прерывает цикл (привязка к одной партнёрской зоне соблюдена). \ PricesBulkActionsPanel.vue:22; index.vue:649,620 | Бизнес/Разработка | Open | (А, рекоменд.) сделать набор действий зависимым от статусов выделенных цен и собирать результат по каждой (не прерывать цикл на первой ошибке, показывать пер-элементный отчёт); (Б) если агрегатного результата достаточно — уточнить FR4.4-27. Правила массовых операций — Back 2.6/BR2.6-10/-11/-12. |
Вопросы по сверке с кодом
BRD 4.4 v0.5 · musbooking_personal_account · commit baef956/test · 2026-06-23 · якорь pages/content/prices, components/prices, widgets.fsd/prices-list, stores/prices*.ts, classes/api/PricesService.ts Сводка: вопросов 16 (бизнесовых 6 / программных 10) · подтверждено без вопросов 8 (FR4.4-2, FR4.4-3, FR4.4-7, FR4.4-8, FR4.4-16, FR4.4-22, FR4.4-23, FR4.4-26) · отрезано находок обратного прохода 11 Под особым вниманием: FR4.4-30 (проверка конфликта на фронте), FR4.4-9/10 (доп. цена как тип), FR4.4-6 (асимметрия пары), FR4.4-13/14 (группировка/фильтр по статусу), FR4.4-31 (проверка только новых).
Бизнесовые
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| Б1 | FR4.4-30 | При изменении условий применения интерфейс должен проверять конфликт применимости и помечать цену/вкладку блокирующей ошибкой с расшифровкой. | Проверки конфликта на фронте нет ни на одном экране. Метод бэкенда validateUniqueness есть в сервисе и сторе, но у него ноль вызовов в компонентах/страницах. Сетка умеет рисовать конфликтные ячейки (conflictCells), но для цен их никто не передаёт. Конфликт обнаружится только при сохранении ответом бэкенда. — нужно решение: должна ли проверка конфликта идти на фронте до сохранения. | stores/prices.ts:366 (validateUniqueness без потребителей); classes/api/PricesService.ts:282; components/UI/PriceGrid.vue:169 (conflictCells не заполняется для цен) |
| Б2 | FR4.4-9 | «Дополнительная цена» — отдельный тип: уточнение стандартной, со своим автоназванием («Условие 1»), для непокрытых сценариев. | Отдельного типа «дополнительная цена» в коде нет. Кнопка «+» клонирует текущую пару в новую вкладку тем же компонентом (PairPriceSettingsMainTab) с тем же базовым именем; это ещё одна обычная стандартная пара, а не типизированное уточнение. — нужно решение: нужен ли отдельный концепт «дополнительной цены» или достаточно клонированных вкладок. | pages/content/prices/settings/create.vue:139; stores/pricesActions.ts:913 (createPairGroup) |
| Б3 | FR4.4-6 | «День в день» сохраняется только при наличии «Заранее»; «Заранее» допустима без «День в день» (асимметрия). | Цены моделируются всегда парой (pre+today). Проверки требуют наличия обеих сторон (PRICE_MISSING_TODAY/PRICE_MISSING_PRE) — то есть «Заранее» в одиночку не разрешена, в отличие от BRD. Связь «today только при наличии pre» соблюдена, но асимметрия нарушена: код требует пару. — нужно решение: разрешать ли «Заранее» без «День в день». | stores/pricesActions.ts:497-507 (требуются обе стороны); stores/pricesActions.ts:913 (createPairGroup всегда создаёт обе) |
| Б4 | FR4.4-13 | Список группируется по статусам действующие/будущие/архивные со счётчиком. | Основной список не сгруппирован по статусам и не имеет счётчиков-заголовков. Архивные показываются пунктирным стилем и префиксом «Архив:», а Прошлые/Будущие — секции внутри раскрытой карточки, а не группы списка. — нужно решение: нужна ли группировка списка по статусам со счётчиками. | components/prices/PriceCard.vue:5 (стиль архива); components/prices/PriceCard.vue:149 (Прошлые/Будущие внутри карточки) |
| Б5 | FR4.4-14 | Переключатель статуса «Все»/«Действующие»/«Будущие»/«Архивные». | Переключателя из четырёх статусов нет. Есть только бинарный тумблер «Только активные» (причём displayedPrices его фактически не читает — фильтр не применяется), а выбор по категориям статусов используется для выделения, а не фильтрации. Список всегда грузится с ShowArchived: true. — нужно решение: нужен ли полноценный фильтр по 4 статусам. | components/prices/PricesToolbar.vue:15 (onlyActive); pages/content/prices/index.vue:197 (displayedPrices не читает onlyActive), :543 (ShowArchived:true) |
| Б6 | FR4.4-21 | Будущая стоимость: период начинается после завершения текущего; после успеха — в группе будущих цен. | Отдельного flow «добавить будущую стоимость» с проверкой «период начинается после текущего» нет. Будущие цены создаются обычным flow создания с более поздними датами; цепочки сезонности определяются по соседству дат постфактум. Проверки «новый период начинается после завершения текущего» нет. — нужно решение: нужен ли отдельный flow и эта проверка на фронте. | composables/usePriceContinuations.ts:46 (определение по соседству дат); pages/content/prices/settings/create.vue (отдельного flow «будущая стоимость» не найдено) |
Программные
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| П1 | FR4.4-31 | При изменении матрицы проверяется заполненность слотов; пустые — блокирующая ошибка с расшифровкой. | Проверка пустой/частичной сетки реализована (PRICE_EMPTY_GRID, PRICE_PARTIAL_GRID), блокирует сохранение и показывает per-tab подсказку. Но проверка работает только для новых черновиков (isNew); при редактировании существующей цены проверка заполненности матрицы не выполняется. | stores/pricesActions.ts:460-484 (только isNew); components/UI/layout/TabsContainer.vue:41 |
| П2 | FR4.4-4 | Сохранение стандартной цены: при отказе показывается причина. | Проверка незаполненных стоимостей есть (см. П1), но на пути сохранения настройки при ошибке бэкенда пользователю не показывается причина — только console.warn. Структурный разбор ошибки (extractErrorMessage) подключён лишь к действиям списка, не к сохранению на экране настройки. | components/prices/PriceEditPanel.vue:887-889 (console.warn); pages/content/prices/index.vue:496 (extractErrorMessage только на списке) |
| П3 | FR4.4-11 / FR4.4-18 | До сохранения можно отменить несохранённые изменения; после изменения матрицы доступны сохранение и отмена. | «Сбросить» доступна только в режиме создания (id начинается с new) и делает жёсткий сброс черновика к заводскому состоянию. Для редактируемой существующей цены нет «отмены», возвращающей к последним сохранённым значениям — есть только сохранение. — нужно решение: нужна ли отмена изменений для существующих цен. | components/prices/PriceEditPanel.vue:210-217 (reset только для new); stores/pricesActions.ts:1054 (onResetDraft — wipe) |
| П4 | FR4.4-19 | Конфликт при добавлении площадки — предупреждение и подтверждение; удаление, нарушающее обязательную привязку — отказ с причиной. | Подтверждение при изменении площадок есть, но это общий confirm, а не проверка конфликта по условиям применения. «Мин. одна площадка» обеспечивается косвенно (PRICE_NO_OBJECTS) и только для новых черновиков; для редактирования такого ограничения нет. Логики «удаление нарушает обязательную привязку → отказ с причиной» не найдено за обход. | components/prices/PriceEditPanel.vue:164 (require-confirm — общий); stores/pricesActions.ts:485-487 (PRICE_NO_OBJECTS только isNew); не найдено за обход: отказ при удалении обязательной привязки |
| П5 | FR4.4-24 | Восстановление архивной: проверки по BR; конфликт с активными — блокировка и причина отказа. | На фронте проверки конфликта при восстановлении нет; комментарий в коде явно указывает, что проверка дат при восстановлении была убрана («можно восстанавливать с любыми датами»). UI требует лишь задать период; конфликт всплывёт только ответом бэкенда (e?.message). | stores/pricesActions.ts:1733 (restorePrice), :1750 (комментарий об удалённой проверке); components/prices/PriceEditPanel.vue:939 |
| П6 | FR4.4-27 | Набор массовых действий зависит от статусов выбранных; каждая обрабатывается отдельно, результат по каждой. | Набор действий не меняется в зависимости от статусов (всегда Архивировать/Удалить/Изменить период). Результат — агрегатный тост («Архивировано: N» / «Удалено: N»), отчёта по каждой цене нет; одна ошибка прерывает цикл. Привязка к одной партнёрской зоне есть. | components/prices/PricesBulkActionsPanel.vue:22 (статичный набор); pages/content/prices/index.vue:649 (цикл без результата по каждой), :620 (агрегатный тост) |
| П7 | FR4.4-28 | Состояния списка: загрузка, пустой с созданием, ошибка с повтором. | Стор знает loading/error/isEmpty, но главная страница списка не рендерит ни индикатор загрузки, ни баннер ошибки, ни кнопку повтора; пустое состояние без CTA «создать» (кнопка создания живёт в тулбаре). | stores/prices.ts:15-17 (флаги есть); pages/content/prices/index.vue (на странице нет спиннера/ошибки/retry) |
| П8 | FR4.4-15 | Фильтры по условиям, в т.ч. признак «День в день» и доплата; фильтрация уже загруженного списка. | Клиентская фильтрация есть по виду работ, диапазону посетителей, приоритету, мин. длительности. Фильтра по признаку «День в день» и по доплате не найдено. Фильтр по объекту/площадке вызывает повторный запрос на бэкенд, то есть не чисто клиентский. Сегменты диапазона людей захардкожены (1-5/5-10/10-15). | pages/content/prices/index.vue:203-247 (фильтры); :359 (object/room → fetch); components/prices/PricesToolbar.vue:108 (захардкоженные сегменты) |
| П9 | FR4.4-17 | Возможность отобразить все цены площадки/объекта (стандартная + доп. во вкладках). | Открытие строит вкладки только из цепочки группы открытой цены (single / pair / сезонная мульти-пара), а не из всех цен площадки/объекта. «Все цены площадки» широко не подгружаются. — нужно подтверждение, что показ ограничен группой намеренно. | pages/content/prices/settings/[id].vue:223-392 |
| П10 | FR4.4-1 | Создание цены начинается с выбора площадки. | Создание не начинается с шага выбора площадки: create.vue сразу создаёт черновик стандартной пары с пустыми bases/rooms, площадка выбирается позже. Запрет «сохранить без площадки» при этом обеспечивается (PRICE_NO_OBJECTS, блокировка кнопки). — нужно решение: должен ли выбор площадки быть первым обязательным шагом. | pages/content/prices/settings/create.vue:101-105 (пустые bases/rooms); stores/pricesActions.ts:485-487 |
Находки обратного прохода (код сверх BRD)
| № | Что делает код | В BRD | Где в коде |
|---|---|---|---|
| О1 | Проверка покрытия диапазонов посетителей по периоду — флаг «Пропущен диапазон кол-ва человек», если диапазоны не покрывают 1..∞. | Нет требования | stores/pricesActions.ts:409-457 |
| О2 | Авто-инкремент диапазона людей при клонировании пары (следующая начинается с prevTo+1, имя перестраивается). | Нет требования | stores/pricesActions.ts:811-834, :968-991 |
| О3 | Движок автоназвания с распознаванием кастомных/залоченных имён и суффиксами диапазона. | Частично (автоназвание упомянуто) | composables/usePriceAutoName.ts:60-83 |
| О4 | Учёт правил бронирования в сетке: объединение ячеек, подсказки мин. длительности/строгих интервалов поверх матрицы. | Нет требования | components/UI/PriceGrid.vue:197-276; components/prices/PriceSettingsMainTab.vue:1192 |
| О5 | Нерабочие «перерывы» в сетке — неинтерактивные ячейки, исключённые из выбора и из подсчёта заполненности. | Нет требования | components/UI/PriceGrid.vue:333-357 |
| О6 | Деление стоимости по объединённой группе ячеек (floor + распределение остатка). | Нет требования | stores/priceSelection.ts:283-369 |
| О7 | «Повысить всё» — аддитивное и процентное повышение по матрице с живым превью. | Нет требования | stores/priceSelection.ts:483-503; components/prices/PriceEditPanel.vue:99 |
| О8 | Сезонные супергруппы — цепочки соседних по датам пар сворачиваются в одну раскрываемую карточку с индикатором пропусков. | Частично (будущие цены) | composables/usePriceContinuations.ts:674-809 |
| О9 | Анти-мерцание пары: при отфильтрованной одной стороне недостающая половина пары подтягивается из полного стора. | Нет требования | composables/usePriceContinuations.ts:192-231 |
| О10 | Оптимистично-безопасное обновление площадок: применяются к карточке только после успеха сервера, со сценарием отката. | Нет требования | pages/content/prices/index.vue:448-493 |
| О11 | OPEN_RANGE_SENTINEL (2147483647) для кодирования открытого диапазона посетителей, нормализуется в null при чтении. | Нет требования | stores/pricesActions.ts:21; stores/prices.ts:222-230 |