Направление работ: Back Код раздела: 2.5 Объекты и предложения > Дополнительные позиции Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 22.07.26


1. Назначение

Дополнительные позиции расширяют состав заказа товарами и услугами партнера и могут увеличивать его итоговую стоимость.

Система хранит параметры позиции, определяет область ее доступности, рассчитывает доступный остаток и применяет правила валидации при сохранении.

2. Ключевые понятия

Дополнительная позиция — товар или дополнительная услуга партнера, которую можно привязать к заказу.

Тип позиции — классификация дополнительной позиции как оборудования или услуги; услуга-позиция не является услугой объекта.

Группа позиций — значение системного справочника, объединяющее дополнительные позиции по назначению.

Доступный остаток — количество позиции, доступное для привязки к заказу с учетом позиций, уже связанных с заказами на тех же объектах или площадках.

3. Scope

Входит

S1 Позиции: дополнительные элементы состава заказа и их связь с заказом.

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

S3 Валидация: запрет сохранения позиций с полностью идентичными параметрами.

Не входит

4. Требования

Правила и ограничения бизнеса на уровне данных, источник истины в системе.

IDТребованиеКомментарийТип требованияВесСтатус
S1: BR2.5-1Дополнительная позиция может быть привязана к заказу как дополнительный элемент его составаПозиция может увеличивать итоговую стоимость заказа. Формирование состава раскрывается в Back 3.1 Состав заказа, расчет стоимости — в Back 4.2 Стоимость заказа и позиций.Бизнес1Реализовано
OQ2.5-16
S2: BR2.5-2Карточка дополнительной позиции должна хранить описательные данныеСостав данных:
- название;
- ссылка на фото в формате PNG или JPG (логика хранения раскрывается в Back 9.6.1 Загруженные файлы);
- текстовое описание.
Системное1Частично
OQ2.5-13
S2: BR2.5-3Для дополнительной позиции должны задаваться тип и группаДопустимые типы:
* оборудование;
* услуга, не являющаяся услугой объекта.
Группа выбирается из справочника Back 8.2.4 Типы фильтров, позиций, шаблонов и расходов.
Бизнес1Реализовано
OQ2.5-17
S2: BR2.5-4Дополнительная позиция должна быть связана минимум с одним объектом партнера и минимум одной площадкой этого объекта.Список объектов формируется из Back 2.1 Объекты партнеров.Бизнес1Не реализовано
OQ2.5-6
S2: BR2.5-5Дополнительная позиция может быть ограничена списком площадок/услуг выбранного объектаЕсли ограничения не заданы, позиция доступна для всех площадок/услуг выбранного объекта.
Логика связанных сущностей раскрывается в Back 2.2 Площадки партнеров и Back 2.3 Услуги партнеров (в релиз не входит).
Бизнес1Реализовано
OQ2.5-7 (Не входит)
S2: BR2.5-6Дополнительная позиция может быть ограничена диапазоном доступных дат и времени суток (после релиза реализация)Если ограничения не заданы, позиция доступна для всех диапазонов дат и времени.Бизнес3Реализовано
OQ2.5-14 (Реализовано)
S2: BR2.5-7Для дополнительной позиции должно задаваться общее количество, привязанное к объектам и площадкам, а система должна рассчитывать доступный остаток на выбранное времяОстаток уменьшают позиции в заказах, время которых пересекается с выбранным периодом, кроме заказов в статусе «Отменено». Если на выбранное время занято все количество позиции, позиция недоступна для привязки к заказу.Системное1Частично
OQ2.5-8, OQ2.5-9, OQ2.5-18
S2: BR2.5-8Стоимость дополнительной позиции после привязки к заказу должна рассчитываться по заданным стоимости и типу расчетаСтоимость может составлять 0 рублей. Допустимые типы расчета:
* за заказ — стоимость рассчитывается за весь заказ;
* за часы — стоимость рассчитывается по продолжительности заказа в часах с округлением в большую сторону.
Детальная логика расчета раскрывается в Back 4.2 Стоимость заказа и позиций.
Системное1Частично
OQ2.5-4, OQ2.5-5
S2: BR2.5-9Доступность дополнительной позиции для привязки к заказу должна ограничиваться выбранными источниками заказаИсточники выбираются из справочника Back 8.2.2 Источники заказа.Бизнес1Не реализовано
OQ2.5-12
S2: BR2.5-10Архивирование должно деактивировать дополнительную позициюСистемное2Частично
OQ2.5-10
S3: BR2.5-11Система должна отклонять сохранение позиции, если ее название, объект и цена совпадают с параметрами существующей позицииСистемное2Не реализовано
OQ2.5-11

5. Открытые вопросы

Сверка с кодом: BRD 2.5 v0.1 · репозиторий booking-monorepo · commit 83423b21 / main · 26.07.26 · якорь src/services/booking.positions Сводка: 15 вопросов (Open 12 · Подтверждено 3) · строки OQ2.5-1…3 — вопросы аналитика к гайдбуку, оставлены без изменений Маршрутизация без сверки с гайдбуком (гайдбук не передавался).

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
OQ2.5-1ГайдбукПодтвердить модель доступного остатка: количество задается общее для позиции или отдельно по объекту/площадке, а также какие статусы заказов и пересечения по времени уменьшают остаток.ПродуктClosedПодтверждено сверкой кода. PositionSetting привязан к PositionId и содержит один Amount — количество действительно общее на позицию, разбивки по объектам и площадкам в модели нет. Остаток = Setting.Amount - currentlyUsedAmount, занятость считается через PositionInUse с диапазоном From/To, то есть по пересечению по времени; отменённые заказы остаток освобождают (OQ2.5-18). Черновой ответ аналитика верен — принять как есть, доработки не требуется.Количество задается общее для позиции и привязывается к объектам и площадкам. Остаток на выбранное время уменьшают все пересекающиеся по времени заказы, кроме заказов в статусе «Отменено». Если все количество занято, позиция недоступна для заказа.

ОТ БИЗНЕСА: ок
OQ2.5-2ГайдбукПодтвердить источник параметра «Количество часов» для почасового расчета: он задается при привязке позиции к заказу или определяется из продолжительности заказа.ПродуктClosedПодтверждено сверкой кода. CoefficientPerHour хранится в настройках самой позиции (Settings.CoefficientPerHour) и при привязке к заказу не задаётся, часы берутся из продолжительности заказа. Семантика совпадает с зафиксированной по вебЛК в OQ4.5-3: 0 — за весь заказ, 1 — за час, 2 — за два часа. Черновой ответ аналитика верен — принять как есть.Количество часов определяется из продолжительности заказа.

ОТ БИЗНЕСА: ок
OQ2.5-3ГайдбукПодтвердить, какие параметры участвуют в проверке полного совпадения позиций и в какой области выполняется сравнение: в пределах партнера, партнерской зоны или всей системы.ПродуктClosedОтвет дополнен по уточнению заказчика. К названию, объекту и цене добавляется площадка: одинаковая позиция на разных площадках допустима (флейта на комнате 1 и флейта на комнате 2 — не дубли). Область сравнения — в пределах партнёрской зоны. Требует правки BR2.5-11 и сверки с фактической проверкой в коде — см. OQ2.5-11.Совпадение позиции проверяется по названию, объекту, площадке и цене — в пределах партнерской зоны. Одинаковая позиция на разных площадках дублем не считается.

ОТ БИЗНЕСА: ок
OQ2.5-4Код BR2.5-8Требование говорит: стоимость дополнительной позиции может составлять 0 рублей. В коде сохранение позиции с нулевой ценой запрещено: при создании и при любом изменении настройки позиции проверяются, и цена, равная нулю или меньше нуля, отклоняется с ошибкой. Завести бесплатную позицию (например, подарочную услугу или оборудование «в подарок» к заказу) нельзя. \ Settings.cs:21 (проверка «цена не ноль и не отрицательная»), вызывается из PositionAggregate.cs:94 и :161БизнесOpen(А) Привести код к BRD (рекоменд.): разрешить нулевую цену — заменить проверку «не ноль и не отрицательная» на «не отрицательная», как уже сделано для количества и коэффициента в той же строке кода. Требование о бесплатных позициях выглядит осмысленным: партнёры дарят позиции в составе заказа. (Б) Привести BRD к коду: убрать из BR2.5-8 фразу про 0 рублей, если бесплатных позиций быть не должно.Разрешить нулевую цену. Привести код к BR2.5-8: допускать цену 0 и запрещать только отрицательную — сейчас Settings.cs:21 отклоняет любое значение «ноль или меньше», и вызывается эта проверка и при создании, и при каждом изменении настроек, поэтому бесплатную позицию («в подарок» к заказу) завести нельзя вовсе. При правке проверить, что нулевая цена корректно проходит дальше по цепочке расчёта стоимости заказа и не ломает почасовой расчёт. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-5Код BR2.5-8Требование говорит: при почасовом расчёте стоимость считается по продолжительности заказа в часах с округлением в большую сторону. В коде почасовой расчёт устроен иначе, и расхождений три. Первое: у позиции хранится не признак «за заказ / за часы», а числовой «коэффициент часов»: если он равен нулю — цена берётся за весь заказ, иначе — за блоки длиной в коэффициент (коэффициент 2 = блок из двух часов). Второе: продолжительность заказа усекается до целых часов вниз: заказ на 1 час 45 минут считается как 1 час, а заказ короче часа даёт нулевую длительность — и тогда позиция вообще помечается недоступной с причиной «неверная длительность». Третье: число блоков округляется к ближайшему по банковскому правилу, а не вверх: при коэффициенте 2 заказ на 5 часов даёт 2 блока вместо 3 (round(2,5) = 2). В сумме короткие и «половинчатые» заказы систематически считаются дешевле, чем требует BRD, а заказ короче часа не может получить позицию вовсе. \ CalculatePositionsCommandHandler.cs:63 (усечение длительности), :91-97 (отказ при нулевой длительности), :176-184 (блоки и округление); поле — Settings.cs:15БизнесOpen(А) Привести код к BRD (рекоменд.): считать продолжительность без потери минут и округлять итог вверх; коэффициент часов либо задокументировать как отдельный параметр, либо свести к 1. (Б) Признать целевой модель «коэффициент часов» — тогда дополнить BR2.5-8 описанием блоков и правилом округления. Независимо от выбора отдельно решите судьбу заказов короче часа: сейчас они не просто дешевле — позиция к ним не привязывается, и это ближе к дефекту, чем к продуктовому решению.Округлять вверх, как требует BRD. Привести почасовой расчёт к BR2.5-8: не усекать продолжительность заказа до целых часов вниз ((int)(dateRange.To - dateRange.From).TotalHours превращает 1 час 45 минут в 1 час) и округлять число блоков вверх через Math.Ceiling вместо Math.Round, который без указания режима округляет к чётному по банковскому правилу. Сейчас партнёр недополучает деньги на неполных часах. Правка затрагивает расчёт денег — обязателен регресс по действующим заказам и согласование с клиентскими поверхностями (вебЛК, мобильное приложение, виджет), где стоимость показывается до оплаты. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-6Код BR2.5-4Требование говорит: дополнительная позиция должна быть связана минимум с одним объектом партнёра. В коде проверки нет: если при создании не передать ни объектов, ни площадок, служба иерархии возвращает пустой список областей действия, и позиция всё равно создаётся. Последствие серьёзнее пропущенной валидации: партнёрская зона позиции вычисляется как первая зона из списка её областей, поэтому у позиции с пустым списком зона оказывается пустой — такая позиция не попадёт ни в один список кабинета (все выборки фильтруют по зоне) и останется висеть в базе. \ PartnerZoneScopeService.cs:36-39 (пустой список), CreatePositionCommandHandler.cs:82-97 (создание без проверки), PositionAggregate.cs:35-38 (зона = первая из областей)РазработкаOpenЗавести тикет в Bitrix: при создании и обновлении позиции отклонять пустой список объектов/площадок с понятной ошибкой. Продуктового выбора нет: BR2.5-4 однозначен. Заодно проверить, есть ли в базе позиции с пустой партнёрской зоной, — их придётся чинить отдельно.Добавить валидацию непустой области действия. Отклонять создание и изменение позиции, у которой не задано ни одного объекта и ни одной площадки, как требует BR2.5-4. Сейчас проверки нет, и последствие тяжелее пропущенной валидации: партнёрская зона позиции вычисляется как первая зона из списка её областей (PositionAggregate.cs:35-38), поэтому у позиции с пустым списком зона оказывается пустой, она не попадает ни в одну выборку кабинета (все фильтруют по зоне) и остаётся в базе навсегда. При постановке проверить, есть ли такие записи в боевой базе, — если есть, нужна миграция. Тикет в Bitrix.

ОТ БИЗНЕСА: Ок, вес 2
OQ2.5-7Код BR2.5-5Требование говорит: позиция может быть ограничена списком площадок или услуг выбранного объекта. В коде ограничение по услугам отсутствует полностью: область действия позиции состоит только из объектов и площадок, услуг в ней нет ни как поля, ни как проверки при расчёте. Часть требования про площадки работает, включая правило «список не задан → доступна для всего объекта»: при расчёте позиция считается подходящей, если совпал объект или площадка. \ CalculatePositionsCommandHandler.cs:80-82; состав области — CreatePositionCommand.cs:26-28 (только Rooms и Bases)БизнесClosed(А) Подтвердить требование (рекоменд., если позиции действительно должны привязываться к конкретным услугам объекта) — тогда это доработка: добавить услуги в область действия позиции и в проверку при расчёте. (Б) Снять услуги из BR2.5-5, если на практике достаточно объекта и площадки. Вопрос стоит решить до правок в клиентах: кабинет партнёра и виджет показывают позиции по той же области.Реализовать ограничение по услугам. Добавить услуги в область действия позиции — и как поле (сейчас в команде создания только Rooms и Bases), и как проверку при расчёте доступности. Часть требования про площадки уже работает, включая правило «список не задан → позиция доступна для всего объекта». Правка затрагивает и кабинет партнёра (форма позиции), и клиентские поверхности, где позиции подбираются к заказу. Тикет в Bitrix (бэкенд + вебЛК).

ОТ БИЗНЕСА: Услуги в релиз не входят, пропускаем. Вес 3
OQ2.5-8Код BR2.5-7Требование говорит: если на выбранное время занято всё количество позиции, позиция недоступна для привязки к заказу. В коде это зависит от того, каким путём идёт клиент. При расчёте (предпросмотр состава заказа) нехватка не блокирует: система считает цену, возвращает предупреждение «Недостаточно доступного количества. Рассчитана цена без проверки доступности» и продолжает — в комментарии кода это названо сознательным повторением поведения старой системы. Жёсткий запрет срабатывает только на шаге резервирования: там позиция не бронируется и весь пакет откатывается. \ CalculatePositionsCommandHandler.cs:130-147 (предупреждение вместо отказа), PositionAggregate.cs:274-276 и ReservePositionsBulkCommandHandler.cs:84-109 (отказ и откат)БизнесClosed(А) Подтвердить текущее поведение (рекоменд.): расчёт показывает цену и предупреждение, отказ — на резервировании; тогда дополнить BR2.5-7 фразой о том, что недоступность проявляется при сохранении заказа, а не при расчёте. (Б) Ужесточить расчёт до отказа — но тогда клиенты (кабинет, виджет, мобильное) перестанут показывать цену недоступной позиции, это заметное изменение UX. Проверьте, что клиенты вообще показывают приходящее предупреждение: если нет, пользователь увидит доступную позицию и получит ошибку только при сохранении.Нужен реалтайм доступного количества при создании заказа. Уточнение заказчика: в рамках создания заказа доступность должна отражаться в реальном времени — доступное количество позиций уменьшается прямо в интерфейсе по мере того, как их разбирают. То есть текущего разделения «предупреждение при расчёте, запрет при резервировании» недостаточно: партнёр и клиент должны видеть актуальный остаток до того, как упрутся в отказ на шаге резервирования. Работы: канал обновления остатка в реальном времени (по аналогии с уже используемым SignalR) + отражение в интерфейсе создания заказа. Жёсткий запрет на шаге резервирования сохраняется как последняя линия защиты. Переписать BR2.5-7 под эту модель. Тикет в Bitrix (бэкенд + фронты).

ОТ БИЗНЕСА: ок, но для МВП можно отложить, Вес 3-1

Уточнение 16.09.26 (из WebLk 5.3.1, OQ5.3.1-26). Механику реального времени делаем подпиской: кабинет подписывается на дату и время заказа, площадку и базу, обновления остатка приходят сообщениями, подписка закрывается вместе с формой заказа. Это заменяет прежнюю формулировку «после MVP»: доработка ставится вместе с доработкой формы создания заказа. Первый шаг фронта — вычитать из показанного остатка уже выбранное количество — не отменяется.
OQ2.5-9Код BR2.5-7Проблема: занятое количество считается по двум разным правилам. При бронировании из подсчёта исключаются архивированные брони и интервал считается полуоткрытым (заказ, который начинается ровно в момент окончания предыдущего, не считается пересечением). При расчёте архивированные брони не исключаются — учитываются все записи. Одна и та же ситуация даёт разный ответ: расчёт покажет «недостаточно», а резервирование пройдёт успешно. \ PositionAggregate.cs:268-274 (фильтр по архивности при бронировании) против CalculatePositionsCommandHandler.cs:117-128 (без фильтра)РазработкаOpenЗавести тикет в Bitrix: свести подсчёт занятого количества к одному правилу — вынести его в доменный метод и вызывать из обоих путей. Продуктового выбора нет: расхождение внутреннее. Нужно только уточнить у разработки, когда бронь становится архивированной, — если этого не происходит никогда, расхождение сейчас не проявляется, но останется миной.Эталон — правило бронирования. Привести подсчёт занятого количества в расчёте к тому же правилу, что в бронировании: исключать архивные брони (ArchiveInfo.IsArchived is false) и считать интервал полуоткрытым [From, To), чтобы заказ впритык к предыдущему не считался пересечением. Сейчас расчёт учитывает все записи без фильтра, поэтому одна и та же ситуация даёт разный ответ — расчёт скажет «недостаточно», а резервирование пройдёт. Вынести подсчёт в один общий метод и вызывать из обоих мест, иначе правила разъедутся снова. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-10Код BR2.5-10Требование говорит: архивирование должно деактивировать дополнительную позицию. В коде архивирование выключает позицию только в списках: все запросы кабинета и виджета отдают архивные записи лишь по явному флагу «показывать архив». Но путь заказа архивность не проверяет: выборка позиций по идентификаторам, которой пользуются и расчёт стоимости, и резервирование, фильтра по архивности не имеет. Зная идентификатор, архивную позицию можно рассчитать и привязать к заказу. Косвенный признак того, что так не задумывалось: при резервировании отсутствующая позиция возвращает текст «Позиция не существует или архивирована», хотя проверки архивности рядом нет. \ PositionRepository.cs:66-77 (выборка без фильтра), потребители — CalculatePositionsCommandHandler.cs:51 и ReservePositionsBulkCommandHandler.cs:48; сообщение — ReservePositionsBulkCommandHandler.cs:65РазработкаOpenЗавести тикет в Bitrix: отсекать архивные позиции на пути заказа — либо фильтром в выборке по идентификаторам, либо явной проверкой в расчёте и резервировании с понятной ошибкой. Продуктового выбора нет: BR2.5-10 однозначен. Перед правкой стоит проверить, не опирается ли на текущее поведение перерасчёт старых заказов, где позиция уже архивирована, — иначе такие заказы перестанут пересчитываться.Фильтровать архивность на пути заказа. Добавить проверку архивности в выборку позиций по идентификаторам, которой пользуются и расчёт стоимости, и резервирование — сейчас фильтра нет, и, зная идентификатор, архивную позицию можно рассчитать и привязать к заказу. Текст ошибки для этого случая уже есть («Позиция не существует или архивирована»), что подтверждает: так и задумывалось. Требование BR2.5-10 закрывается. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-11Код BR2.5-11Требование говорит: система должна отклонять сохранение позиции, если её название, объект и цена совпадают с существующей позицией (область сравнения подтверждена ответом на OQ2.5-3). В коде такой проверки нет нигде: ни при создании, ни при обновлении сравнения с другими позициями не выполняется, а в базе нет ни одного уникального индекса или ограничения — у сервиса позиций вообще нет папки миграций, схема создаётся из моделей. Полные дубли создаются свободно. \ CreatePositionCommandHandler.cs:39-101 и UpdatePositionCommandHandler.cs (сравнения нет), Positions.Persistence/ApplicationDbContext.cs:29-62 (только ключи и связи, ни одного уникального индекса)РазработкаClosedЗавести тикет в Bitrix: реализовать проверку дубля по названию, объекту и цене при создании и обновлении. Бизнес-ответ уже есть в OQ2.5-3, продуктового выбора не осталось. Уточнить у разработки два момента: (1) регистр и пробелы в названии при сравнении, (2) нужен ли уникальный индекс в базе — без него параллельные запросы всё равно создадут дубль.Проверка дублей на уровне приложения. Добавить сравнение при создании и обновлении позиции по четырём атрибутам, подтверждённым в OQ2.5-3 — название, объект, площадка, цена — в пределах партнёрской зоны, с понятным текстом отказа для партнёра. Уникальный индекс в базе не заводим: у сервиса позиций нет папки миграций вообще, схема создаётся из моделей, и введение миграций — отдельная работа. Оговорка: проверка в коде не ловит гонку двух одновременных сохранений; если такие случаи появятся, вернуться к вопросу индекса. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, тикет касается фронта, тут закрываю
OQ2.5-12Код BR2.5-9Требование говорит: доступность позиции для привязки к заказу должна ограничиваться выбранными источниками заказа (сайт, виджет, мобильное приложение и т. п.). В коде у позиции нет ни поля источников, ни фильтра по ним — ни при создании, ни при расчёте, ни при выдаче списков. Слово «источник» в сервисе встречается только как источник вызова API: шлюз определяет, откуда пришёл запрос, и по умолчанию подставляет «Веб», но с доступностью позиции это никак не связано. \ CreatePositionCommand.cs:25-38 (поля источников нет), RpcProxyMiddleware.cs:71-96 и :152-183 (источник вызова, не ограничение)БизнесOpen(А) Подтвердить требование (рекоменд., если партнёры действительно должны прятать позиции от отдельных каналов продаж) — тогда это доработка: хранить список источников у позиции и фильтровать выдачу и расчёт. (Б) Снять BR2.5-9, если ограничение по каналам не нужно. Требование помечено весом 1, а в коде отсутствует целиком, — стоит решить до планирования работ по позициям.Реализовать ограничение по источникам заказа. Добавить позиции список допустимых источников и фильтровать по нему при расчёте и при выдаче списков, как требует BR2.5-9. Сейчас ни поля, ни фильтра нет: слово «источник» в сервисе означает только источник вызова API (шлюз определяет, откуда пришёл запрос) и с доступностью позиции не связано. Источник самого заказа уже есть в модели заказа (Back 3.1), поэтому сопоставлять есть с чем. Тикет в Bitrix (бэкенд + вебЛК для настройки списка).

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-13Код BR2.5-2Требование говорит: карточка позиции хранит название, фотографию в формате PNG или JPG и текстовое описание. В коде название и описание хранятся у позиции, а фотография — нет: изображения лежат в сервисе ресурсов и запрашиваются по идентификатору позиции с типом файла «иконка» (Ico), то есть отдельного типа «фото позиции» не заведено. Ограничение формата PNG/JPG в сервисе позиций не проверяется — если оно есть, то в сервисе ресурсов (Back 9.6.1, вне этой сверки). Отдельно: название не проверяется на пустоту — позицию можно сохранить с пустым именем. \ ImageService.cs:26-33 (тип Ico), Info.cs:14-18 (нет проверок), PositionAggregate.cs:92РазработкаOpenЗавести тикет в Bitrix: добавить проверку непустого названия позиции. По фотографии — вопрос к разработке на уточнение: тип файла «иконка» для фото позиции выбран осознанно или это заимствование из соседнего сценария; проверка PNG/JPG должна быть подтверждена при сверке Back 9.6.1, здесь её ставить некорректно.Добавить проверку непустого названия; хранение фото признать целевым. Сейчас позицию можно сохранить без имени — добавить валидацию. Хранение изображения в сервисе ресурсов (запрос по идентификатору позиции) признаётся целевым: описать это в BR2.5-2 со ссылкой на Back 9.6.1, где и должно проверяться ограничение формата PNG/JPG. Отмечу на будущее: фото позиции хранится под типом файла Ico («иконка») — тот же тип, который дал путаницу с логотипом партнёрской зоны в OQ3.3-4; если будете разводить типы файлов, эти два случая стоит решать вместе. Тикет в Bitrix.

ОТ БИЗНЕСА: ок ок, валидация не пустого названия вес 2. Требование 2 обновлено.
OQ2.5-14Код вне BRDЧто нашли: у позиции есть два ограничения доступности, которых нет в BRD, — период (даты «с» и «по») и время суток («с» и «по»). Оба задаются при создании, хранятся в настройках позиции и проверяются при расчёте: если заказ выходит за период или за окно времени, позиция помечается недоступной с причиной «неподходящая дата» / «неподходящее время». При пустом периоде подставляются минимальная и максимальная даты, то есть ограничение снимается. \ CreatePositionCommandHandler.cs:62-92 (значения по умолчанию), Settings.cs:16-17 (хранение), CalculatePositionsCommandHandler.cs:85-97 (проверка)БизнесClosed(А) Признать поведение целевым (рекоменд.) и внести в BRD 2.5 требование о периоде и времени доступности позиции — сейчас существенная часть модели данных не описана, и при следующей сверке она снова всплывёт как расхождение. (Б) Если ограничения не нужны — ставить задачу на их удаление, но это повлияет на уже заведённые позиции. Отдельно отметьте для разработки: проверка времени написана несимметрично (для «по» есть особый случай нулевого значения, для «с» — нет), логику стоит перечитать при любом решении.Внести оба ограничения в BRD. Период (даты «с» и «по») и время суток («с» и «по») — работающая функциональность: задаются при создании, хранятся в настройках позиции и проверяются при расчёте, при выходе заказа за рамки позиция помечается недоступной с причиной «неподходящая дата» / «неподходящее время»; при пустом периоде подставляются минимальная и максимальная даты, то есть ограничение снимается. Описать оба как требования раздела. Учесть связку с OQ2.5-15: при резервировании период сейчас не проверяется вовсе.

ОТ БИЗНЕСА: ок
OQ2.5-15Код вне BRDПроблема: в домене позиций объявлена проверка «дата брони попадает в период доступности позиции», но она не вызывается ни из одного места сервиса — мёртвый код. Фактически период при бронировании не проверяется вовсе: проверка периода есть только в расчёте (см. OQ2.5-14), а резервирование её не повторяет, поэтому вызовом резервирования напрямую можно забронировать позицию вне её периода доступности. \ PositionInUseValidationHelper.cs:15-25 (объявлена, вызовов нет)РазработкаOpenЗавести тикет в Bitrix: либо задействовать проверку при бронировании, либо удалить её как мёртвый код. Выбор зависит от ответа на OQ2.5-14: если период доступности — целевое правило, проверку нужно включить в резервирование, а не только в расчёт.Задействовать объявленную проверку. Подключить PositionInUseValidationHelper к пути резервирования — код написан, но у него ноль вызовов, поэтому вызовом резервирования напрямую можно забронировать позицию вне её периода доступности. Это тот же сюжет, что в OQ2.5-10 (архивность) и OQ2.5-9 (подсчёт занятого): резервирование не повторяет проверок расчёта. Рекомендация при постановке: делать одной задачей с OQ2.5-9 и OQ2.5-10 — собрать единый набор проверок доступности (период, время суток, архивность, остаток) и вызывать его и при расчёте, и при резервировании, вместо того чтобы чинить три расхождения по отдельности. Тикет в Bitrix.

ОТ БИЗНЕСА: ок, вес 1
OQ2.5-16Код BR2.5-1Что подтверждено: дополнительная позиция привязывается к заказу как отдельный элемент состава — у позиции есть записи «занято под заказ» со ссылкой на заказ, количеством и периодом; привязка выполняется пакетно на заказ, в одной транзакции, с откатом всего пакета при любой неудаче; стоимость позиций рассчитывается отдельной операцией и возвращается по каждой позиции. \ PositionInUse.cs, ReservePositionsBulkCommandHandler.cs:52-119, CalculatePositionsCommandHandler.cs:29-171—ClosedРасхождения нет.ОТ БИЗНЕСА: шик, блеск!
OQ2.5-17Код BR2.5-3Что подтверждено: тип позиции ограничен двумя значениями — оборудование и услуга (Equipment, Service), группа позиции хранится ссылкой на справочник и поддерживается в актуальном состоянии подписками на изменение и архивацию группы. \ PositionType.cs:9-13, PositionAggregate.cs:52, PositionGroupUpdatedPositionsConsumer.cs, PositionGroupArchivedPositionsConsumer.cs—ClosedРасхождения нет.ОТ БИЗНЕСА: шик, блеск!
OQ2.5-18Код BR2.5-7Что подтверждено: отменённые заказы не уменьшают остаток — на событие отмены заказа сервис позиций удаляет все записи «занято под заказ» по этому заказу, то же самое делается при архивации заказа и при неудачном создании заказа. Количество задаётся общим для позиции, а остаток считается вычитанием пересекающихся по времени броней, как и описано в BRD. \ OrderCancelledPositionsConsumer.cs:22-33, OrderArchivedPositionsConsumer.cs, OrderCreationFailedPositionConsumer.cs, PositionAggregate.cs:230-233—ClosedРасхождения нет.ОТ БИЗНЕСА: шик, блеск!
OQ2.5-19Код вне BRDЧто нашли: позиции хранятся в сервисе позиций, но в базе партнёрской зоны лежат ещё две позиционные сущности. Первая — справочник групп позиций (30 записей на стенде): он администрируется в сервисном кабинете вместе с прочими справочниками (Back 8.2.4 Типы фильтров, позиций, шаблонов и расходов), а позиция ссылается на него идентификатором через границу базы — целостность связи не поддерживается ничем. Вторая — слепок области позиции (позиция → зона, объект, площадка, признак архива, время синхронизации), который наполняют консьюмеры событий создания и изменения позиции; единственный его потребитель — выдача площадок и слотов, флаг «у площадки есть дополнительные позиции». Проблема: слепок расходится с источником и не умеет себя чинить — событий на сокращение области нет, сверки при старте нет, метрики расхождения нет. На стенде stg-zeus у двух позиций слепок держит область, которой в сервисе позиций уже нет (синхронизация от 30.04 и 03.05), одна незаархивированная позиция в слепке отсутствует вовсе; итого 97 строк слепка против 95 строк источника. Партнёр видит это как «у площадки показываются или не показываются дополнительные позиции» без видимой причины. Дополнительно: четыре таблицы позиционных данных (allow_positions_on_bases, allow_positions_on_rooms в партнёрской зоне, position_history, partner_zone_view_model в позициях) не упоминаются в коде ни одной ветки и пусты. \ PartnerZones.Persistence/Position/Models/Position.cs:10, PartnerZones.Persistence/Position/PositionRepository.cs:27-45, Consumers/Position/PositionCreatedPartnerZoneConsumer.cs, PartnerZones.Persistence/Rooms/Repositories/RoomReadOnlyRepository.cs:437 (и ещё пять таких же мест)РазработкаOpen(А) Убрать слепок, оставив владение справочником групп за партнёрской зоной (рекоменд.): флаг «есть позиции» получать у сервиса позиций отдельным методом с кэшем. Тогда исчезает единственная причина дублирования, а вместе с ней класс ошибок «показывает не то». (Б) Признать слепок целевым и достроить его до честной проекции: обрабатывать сокращение области и архивацию, добавить сверку при старте (как сделано для каталога доступов) и метрику расхождения. Дороже (А), оправдано только если флаг нужен в тяжёлых выборках слотов и синхронный вызов не проходит по бюджету. (В) Перенести и справочник групп в сервис позиций. Разрывает раздел 8.2.4, где группы администрируются вместе с типами фильтров, шаблонов и расходов, поэтому без отдельного решения по сервисному кабинету не делать. Мёртвые таблицы удалить в любом из вариантов.

0 items under this folder.