Направление работ: Back Код раздела: 1.3.2 Аккаунты и пользователи > Клиенты > Характеристики клиента Статус: черновик Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 17.07.26
1. Назначение
Раздел описывает характеристики клиента как партнерские данные, которые отражают историю и условия взаимоотношений клиента с партнерами.
Система хранит эти данные в связке с Карточкой клиента и группирует их по партнерским зонам.
Карточка клиента, клиентская зона доступа и подробная логика скидок, штрафов и условий оплаты раскрываются в соседних разделах.
2. Ключевые понятия
Характеристики клиента — сгруппированная по партнерским зонам информация о взаимоотношениях клиента с партнерами.
Карточка клиента — сущность, с которой связаны характеристики клиента. Подробнее описана в Back: 1.3.
Партнерская зона — зона партнера, в рамках которой партнер ведет данные по клиенту.
Персональный промокод — ручная скидка, связанная с Карточкой клиента.
Блокировка клиента — ограничение, при котором клиент не может совершать заказы в конкретной партнерской зоне.
3. Scope
Входит
S1 Характеристики клиента: партнерские данные Карточки клиента, которые заполняются партнерами и группируются по партнерским зонам.
S2 Состав характеристик: персональные промокоды, непогашенные штрафы, блокировки, условия оплаты и заметки партнера по клиенту.
S3 Правила применения характеристик: привязка характеристик к партнерской зоне, заказам, срокам, статусам и ограничениям применения условий оплаты.
Не входит
- Back: 1.3 Клиенты, Карточка клиента, персональные и технические данные
- Back: 1.3.1 Клиентская зона доступа
- Back: 1.3.3 Группы деятельности / группы клиентов
- Back: 1.3.4 Расширение клиентских прав доступа через подписки
- Back: 2.7.1 Акции и промокоды
- Back: 3.2.1 Сборы за заказ
- Back: 2.4.2 Условия оплаты
- Mobile: 5.8, 5.9, 5.9.1, 5.9.2 Скидки и ограничения клиента
- WebLk: 3.4.3, 3.4.6, 4.6, 4.6.1, 4.6.2, 6.4, 6.4.1 Условия оплаты, сборы, скидки и карточка клиента
- Widget: 5.2, 6.1 Скидки, комментарий, правила и информация о клиенте
- AdmLk: 2.2, 3.2, 3.2.1 Клиентские справочники, управление клиентами и карточка клиента
- Catalog: 2.4, 4.5, 4.6 Баллы, промокоды, способ оплаты, скидки и ограничения
4. Требования
Правила и ограничения бизнеса на уровне данных, источник истины в системе
| ID | Требование | Комментарий | Тип требования | Вес | Статус |
|---|---|---|---|---|---|
| S1: BR1.3.2-1 | Характеристики клиента должны хранить партнерские данные, связанные с Карточкой клиента | Характеристики отражают результаты взаимоотношений клиента с партнерами. Подробнее Карточка клиента описана в https://wiki.yandex.ru/homepage/d750da42741c/texnicheskaja-dokumentacija/brd/dokumentacija-po-funkcionalurazdelam/1-akkaunty-i-polzovateli/1.3-klienty/ . | Бизнес | 1 | Реализовано |
| S1: BR1.3.2-2 | Характеристики клиента должны заполняться в рамках конкретной партнерской зоны | Бизнес | 1 | Реализовано | |
| S2: BR1.3.2-3 | Характеристики клиента должны хранить список персональных промокодов клиента | Состав данных: • партнерская зона • название промокода • описание промокода • размер или номинал скидки в рублях или процентах • количество доступных для применения заказов • количество заказов, к которым применен промокод • срок действия промокода • статус промокода Подробная логика акций и промокодов раскрывается в https://wiki.yandex.ru/homepage/d750da42741c/texnicheskaja-dokumentacija/brd/dokumentacija-po-funkcionalurazdelam/55f6f0b0f201/2.7-skidki/2.7.1-akcii-i-promokodyavtomaticheskie-i-ruchnye-s/ . | Системное | 1 | Частично \ #OQ1.3.2-3 \ #OQ1.3.2-4 |
| S2: BR1.3.2-4 | Характеристики клиента должны хранить список непогашенных штрафов клиента | Состав данных: • партнерская зона • сумма штрафа • ID заказа, по которому был начислен штраф • дата начисления Подробная логика сборов раскрывается в https://wiki.yandex.ru/homepage/d750da42741c/texnicheskaja-dokumentacija/brd/dokumentacija-po-funkcionalurazdelam/c4a24f042617/3.2-processing-zakazov/3.2.1-sbory-za-zakaz/ . | Системное | 1 | Частично \ #OQ1.3.2-12 |
| S2: BR1.3.2-5 | Характеристики клиента должны хранить список блокировок клиента по партнерским зонам | Состав данных: • партнерская зона • причина блокировки • дата блокировки | Бизнес | 1 | Частично \ #OQ1.3.2-1 |
| S2: BR1.3.2-6 | Характеристики клиента должны хранить условия оплаты клиента по партнерским зонам | Состав данных: • наименование • описание • способы оплаты (канал оплаты зоны) Подробная логика условий оплаты раскрывается в https://wiki.yandex.ru/homepage/d750da42741c/texnicheskaja-dokumentacija/brd/dokumentacija-po-funkcionalurazdelam/55f6f0b0f201/2.4-pravila-raboty-s-zakazami/2.4.1-uslovija-sozdanija/ . | Системное | 1 | Частично \ #OQ1.3.2-5 \ #OQ1.3.2-10 |
| S2: BR1.3.2-7 | Характеристики клиента должны хранить заметки партнера по клиенту | Состав данных: • партнерская зона • текст • ID заказа, по которому была создана запись • дата создания записи • флаг переноса в каждое бронирование | Системное | 1 | Частично \ #OQ1.3.2-6 \ #OQ1.3.2-11 |
| S3: BR1.3.2-8 | Блокировка клиента должна ограничивать возможность клиента совершать заказы только в той партнерской зоне, где она установлена | Бизнес | 1 | Частично \ #OQ1.3.2-7 | |
| S3: BR1.3.2-9 | Для клиента в партнерской зоне должно применяться одно из допустимых условий оплаты | • По умолчанию Описание: Действуют стандартные условия внесения предоплаты. Условия оплаты: не заполняется • Доверять Описание: Условия внесения предоплаты игнорируются. Условия оплаты: не заполняется • Только по предоплате: Описание: Создание заказа возможно только по предоплате. Условия оплаты: ID условий оплаты с установленным значением поля ”% от итога”. • Персональные условия: Описание: Действуют особые условия предоплаты. Способы оплаты: ID условий оплаты указанных партнером вручную из добавленных в партнерской зоне. | Бизнес | 1 | Частично \ #OQ1.3.2-2 \ #OQ1.3.2-14 \ #OQ1.3.2-9 |
| S3: BR1.3.2-10 | Изменение условий оплаты клиента доступно только в партнерских зонах с подключенным терминалом | Если в партнерской зоне не указана платежная информация, для клиента применяется условие оплаты по умолчанию без возможности редактирования партнером. | Системное | 2 | Не реализовано \ #OQ1.3.2-8 |
5. Открытые вопросы
| ID | Источник | Вопрос | Кому | Статус | Рекомендации | Итоговое решение |
|---|---|---|---|---|---|---|
| #OQ1.3.2-1 | Код BR1.3.2-5 | Требование говорит: блокировка хранит партнёрскую зону, причину и дату блокировки. В коде блокировка — это один булев флаг IsBanned на записи клиента в зоне. Причины нет вообще; дата отдельным полем не хранится — момент бана попадает только в событие ClientBannedEvent (аудит), но не в саму запись. Зона есть. \ ClientPartAggregate.cs:16; ClientPartMapping.cs:23-29; ChangeClientBanStatusCommand.cs:11 | Разработка | Закрывает PM | Гайдбук 1.3.2 прямо перечисляет состав блокировки: партнёрская зона, причина блокировки, дата блокировки — значит это недоделка, а не спорный вопрос. Закрываю сам: добавить «причину» и «дату» в запись блокировки (сейчас дата живёт только в событии, в данных её нет) → тикет в Bitrix. Обе нужны для разбора спорных ситуаций. | Добавить в запись блокировки клиента по партнерской зоне поля «Причина блокировки» и «Дата блокировки». При установке блокировки сохранять причину и дату в данных клиента, при снятии блокировки сохранять историю через аудит/события без удаления контекста предыдущей блокировки. В характеристиках клиента отображать блокировку вместе с партнерской зоной, причиной и датой блокировки. |
| #OQ1.3.2-2 | Код BR1.3.2-9 | Требование говорит: «Персональные условия» — действуют особые условия предоплаты, партнёр вручную указывает ID условий оплаты, добавленных в зоне. В коде режим PersonalConditions есть в перечислении, но ни одной ветки логики по нему нет ни в заказах, ни в расчёте оплаты — клиент с этим режимом ведёт себя как «По умолчанию». Список выбранных партнёром ID условий оплаты нигде не хранится. \ ClientPayKind.cs:32; ClientPartAggregate.cs:15 (нет поля под список ID) | Разработка (Влад) | Open | Гайдбук 1.3.2 явно описывает «Персональные условия» и то, что они ссылаются на ID условий оплаты — значит режим нужен, вариант «упростить BRD до трёх режимов» гайдбуку противоречит. Продуктового выбора нет, но есть архитектурный: вопрос к Владу — где хранить выбранные ID условий оплаты у клиента (на записи клиента в зоне сейчас только PayKind, справочник живёт в другом сервисе) и как реализовать ветку. Общий корень с OQ1.3.2-14, OQ1.3.2-9, OQ1.3.2-10. | Добавить хранение выбранных ID условий оплаты для клиента в рамках партнерской зоны и связать их с режимом оплаты «Персональные условия». При установке режима PersonalConditions сохранять список выбранных условий оплаты, валидировать, что они относятся к доступным условиям оплаты этой партнерской зоны, и использовать их при создании заказа и расчете оплаты вместо поведения «По умолчанию». Если список условий оплаты не передан или невалиден, режим PersonalConditions не должен сохраняться. |
| #OQ1.3.2-3 | Код BR1.3.2-3 | Требование говорит: персональный промокод — ручная скидка, связанная с Карточкой клиента. В коде сущности «персональный промокод на одну карточку» нет: ручная скидка адресуется на список клиентов и/или групп (многие-ко-многим), партнёрская зона — коллекция scope-ов, а не атрибут промокода клиента. «Промокоды клиента» — это выборка скидок, в чьём списке клиентов он есть. \ ManualDiscountAggregate.cs:64-65, :43-50 | Бизнес (Герман) | Open | Гайдбук 1.3.2 говорит «список выданных персональных промокодов — ручных скидок, связанных с карточкой клиента» и даёт «Партнёрская зона» в составе записи, но эксклюзивность промокода для одной карточки в гайдбуке не оговорена — подтвердить нечем, не додумываю. Решение бизнеса: целевая модель «персональный промокод клиента» — 1:1 к карточке или ручная скидка на список клиентов? зона как атрибут записи? Разрыв концептуальный, закрыть до правок кода; влияет на состав и на клиентские выборки. Связать с OQ1.3.2-4. | |
| #OQ1.3.2-4 | Код BR1.3.2-3 | Требование говорит: в составе промокода есть статус. В коде статуса как хранимого поля нет: ни перечисления, ни колонки — только булев IsArchived, а «активен / просрочен / исчерпан» вычисляется на лету в каждом запросе. На клиентских выборках промокодов статус не возвращается вовсе. \ ArchiveInfo.cs:9-11; GetMyPromocodesQuery.cs:194-214; ManualPromocodeReadOnlyRepository.cs:39-45 | Бизнес/Разработка | Open | Гайдбук 1.3.2 задаёт статус ровно двумя значениями — активен / неактивен, а код вычисляет три (активен / просрочен / исчерпан). То, что статус должен быть в списке, гайдбук подтверждает → выборки обязаны его отдавать (сейчас не отдают — это дефект). Спорно другое: (А) оставить статус вычисляемым (архив + срок + лимит) и маппить на два значения гайдбука; (Б) ввести явный хранимый статус. Формат — с продуктом; зависит от модели промокода (OQ1.3.2-3). | Согласовать с бизнесом целевую модель статуса персонального промокода: статус хранится отдельным полем или вычисляется по признаку архивации, сроку действия и лимиту применений. Для пользовательского отображения статус промокода должен возвращаться в клиентских выборках. Если выбирается вычисляемая модель, состояния кода необходимо привести к бизнес-значениям «Активен» и «Неактивен»; если выбирается хранимый статус, добавить отдельное поле статуса и правила его изменения. |
| #OQ1.3.2-5 | Код BR1.3.2-6 | Справочник условий оплаты (PaymentFiltersAggregate) хранит наименование и описание, но способы оплаты в нём не хранятся — они привязаны к платёжному каналу зоны (AllowPaymentMethodOnPayChannel), уровнем выше. Запись условия оплаты сама по себе перечня способов оплаты не несёт. — нужно решение: считать способы оплаты частью условия оплаты или признать, что они принадлежат каналу оплаты зоны \ PaymentFiltersAggregate.cs:26; PaymentMethod.cs:3-11; AllowPaymentMethodOnPayChannel.cs:12-20 | Бизнес | Closed | БРД приведен к коду | |
| #OQ1.3.2-6 | Код BR1.3.2-7 | Требование говорит: заметка несёт флаг переноса в каждое бронирование. В коде такого флага нет. Вместо него у заметки есть поле Admission — это видимость заметки (кому показывать: сотрудникам / только мне / только сервису / другим партнёрам), а не перенос в бронирования. \ ClientMessageAggregate.cs:17; ClientMessageAdmissionEnum.cs:3-9 | Разработка | Закрывает PM | Гайдбук 1.3.2 прямо перечисляет «Флаг переноса в каждое бронирование» в составе записи — правило есть, продуктового выбора нет, вариант «заменить на видимость» гайдбуку противоречит (у Admission другой смысл, под это поле не подходит). Закрываю сам: добавить отдельный булев флаг переноса → тикет в Bitrix. Оговорка: семантику самого переноса (как заметка попадает в бронирование) гайдбук 1.3.2 не описывает — уточнить в разделе заказов при реализации. | Добавить для заметки партнера по клиенту отдельный булев флаг «Переносить в каждое бронирование». Поле Admission оставить только для управления видимостью заметки, так как оно не отвечает за перенос. При создании и обновлении заметки сохранять значение нового флага, отображать его в характеристиках клиента и использовать при формировании бронирований, где заметка должна переноситься автоматически. |
| #OQ1.3.2-7 | Код BR1.3.2-8 | Требование говорит: блокировка ограничивает заказы только в той зоне, где установлена. В коде при создании заказа проверка корректна: запись клиента берётся по (клиент, зона), бан режет заказ только в этой зоне. Но обработчик события бана отменяет/архивирует уже существующие брони и заявки клиента, фильтруя их только по clientId, без зоны — хотя событие несёт PartnerZoneId. В итоге бан в одной зоне снимает ожидающие брони клиента во всех зонах. \ CreateOrderCommandHandler.cs:91-107 (корректно); ClientBannedConsumer.cs:37-69 (утечка) | Разработка | Закрывает PM | Гайдбук 1.3.2 подтверждает зональность: «Список партнёрских зон, в которых клиент не может совершать заказы» — значит это баг, а не спорный вопрос. Закрываю сам: в обработчике события бана фильтровать брони/заявки ещё и по PartnerZoneId из события (поле уже есть, просто не используется) → тикет в Bitrix. Правка локальная, проверка при создании заказа уже верна. | Исправить обработчик события блокировки клиента: при отмене или архивировании существующих броней и заявок фильтровать их не только по ClientId, но и по PartnerZoneId из события блокировки. Блокировка должна влиять только на заказы, брони и заявки в той партнерской зоне, где она установлена. Проверку при создании нового заказа оставить без изменений, так как она уже использует связку клиент + зона. |
| #OQ1.3.2-8 | Код BR1.3.2-10 | Требование говорит: менять условия оплаты клиента можно только в зонах с подключённым терминалом; без платёжной информации — «По умолчанию» без редактирования. В коде проверки терминала при смене режима нет нигде: команда обновления записи клиента меняет PayKind без обращения к настройкам оплаты. Флага «терминал подключён» наружу сервис партнёров не отдаёт, состояние выводимо лишь косвенно. Запрет редактирования и откат к «По умолчанию» не реализованы. \ UpdateOrCreateClientPartCommand.cs:19,52; PaymentSettingsAggregate.cs:40; терминал-проверка — не найдено за обход | Разработка (Влад) | Open | Гайдбук 1.3.2 прямо подтверждает правило: «Функция активируется только в партнёрских зонах, в которых заполнена платёжная информация… В остальных случаях устанавливается элемент справочника „По умолчанию“, и его редактирование партнёром невозможно» — значит это недоделка, а не спорный вопрос. Но реализация межсервисная: вопрос к Владу — как сервис клиентов узнаёт «терминал подключён» (флаг наружу от сервиса партнёров или запрос) и где живёт откат к «По умолчанию». Разблокируется вместе с OQ1.3.2-2. | Добавить проверку наличия платежной информации/подключенного терминала партнерской зоны перед изменением условий оплаты клиента. Сервис клиентов должен получать этот признак из сервиса партнеров через согласованный контракт: отдельный флаг или запрос по PartnerZoneId. Если платежная информация отсутствует, сохранять для клиента режим оплаты «По умолчанию» и запрещать изменение PayKind партнером. Если платежная информация есть, разрешать изменение PayKind с учетом правил выбранного режима оплаты. |
| #OQ1.3.2-9 | Код BR1.3.2-9 | Проблема: команда обновления записи клиента принимает PayKind, но не валидирует согласованность: для «Только по предоплате» / «Персональные условия» не требует и не сохраняет ID условий оплаты; любой режим можно выставить без проверок. \ UpdateOrCreateClientPartCommand.cs:19,43-52 | Разработка | Open | Гайдбук задаёт, что два режима из четырёх опираются на ID условий оплаты → валидация состава нужна. Реализуется после появления самого хранения ID (OQ1.3.2-2, OQ1.3.2-10): требовать ID условий оплаты там, где режим их предполагает. Отдельного продуктового вопроса нет, но и чинить пока нечего — ждёт архитектурного решения. Связать с OQ1.3.2-2, OQ1.3.2-10. | После добавления хранения ID условий оплаты на записи клиента в партнерской зоне реализовать валидацию режима PayKind при создании и обновлении записи клиента. Для режимов «Только по предоплате» и «Персональные условия» требовать наличие ID условий оплаты и проверять, что выбранные условия доступны в этой партнерской зоне. Для режимов «По умолчанию» и «Доверять» ID условий оплаты не сохранять и не требовать. Некорректную комбинацию PayKind и условий оплаты не принимать. |
| #OQ1.3.2-10 | Код BR1.3.2-6 | Проблема: на записи клиента в зоне условие оплаты представлено только перечислением PayKind; ссылки на запись справочника условий оплаты у клиента нет. Сам справочник живёт в другом сервисе. Связь «клиент → конкретное условие оплаты» в данных отсутствует. \ ClientPartAggregate.cs:15; PaymentFiltersAggregate.cs:25-26 | Разработка (Влад) | Open | Гайдбук требует, чтобы «Только по предоплате» и «Персональные условия» ссылались на ID условий оплаты — значит связь нужна. Вопрос к Владу: где она живёт (поле на записи клиента в зоне / отдельная связь) с учётом того, что справочник в другом сервисе. Общий корень с OQ1.3.2-2, OQ1.3.2-14, OQ1.3.2-9 — решать одним архитектурным заходом. | Для клиента в партнерской зоне нужно хранить не только общий режим оплаты, но и выбранные партнером условия оплаты, когда режим этого требует. Для режимов «Только по предоплате» и «Персональные условия» система должна сохранять конкретные условия оплаты из справочника этой партнерской зоны и применять их при создании заказа и расчете оплаты. Для режимов «По умолчанию» и «Доверять» отдельные условия оплаты не выбираются. Если выбранные условия оплаты не относятся к партнерской зоне или недоступны, такое значение не сохраняется. |
| #OQ1.3.2-11 | Код BR1.3.2-7 | Требование говорит: заметка несёт ID заказа, по которому создана. В коде поля OrderId у заметки нет: ни в агрегате, ни в команде создания, ни в модели хранения. Привязать заметку к заказу нельзя. \ ClientMessageAggregate.cs; CreateClientMessageCommand.cs:10-15; ClientMessageMapping.cs:28-39 | Разработка | Закрывает PM | Гайдбук 1.3.2 прямо перечисляет «ID заказа, вследствие которого была создана запись» в составе заметки — правило есть, продуктового выбора нет. Закрываю сам: добавить OrderId в агрегат / команду / модель заметки → тикет в Bitrix. Правка небольшая и изолированная. | Добавить к заметке партнера по клиенту поле «ID заказа, по которому создана заметка». При создании заметки из контекста заказа сохранять этот ID в данных заметки. В характеристиках клиента отображать заметку вместе с привязанным заказом, чтобы было понятно, по какому заказу и по какой причине запись появилась. |
| #OQ1.3.2-12 | Код BR1.3.2-4 | Проблема: основной расчёт суммы непогашенных штрафов корректен (не оплачен полностью, не в архиве, точное сравнение зоны). Но соседний метод выборки непогашенных штрафов по зоне фильтрует зону через PartnerZoneId <= ... (сравнение «меньше-либо-равно» по Guid вместо равенства) — тянет штрафы из «меньших» по значению зон. \ ForfeitsRepository.cs:54-74 (корректный); ForfeitsRepository.cs:46 (подозрительный <=) | Разработка | Закрывает PM | Гайдбук 1.3.2 требует список штрафов по партнёрской зоне — сравнение <= этому прямо противоречит, это дефект зональной фильтрации. Закрываю сам: заменить PartnerZoneId <= ... на == в выборке непогашенных штрафов по зоне → тикет в Bitrix. При реализации уточнить у владельца кода, где метод используется (оценить радиус). | Исправить выборку непогашенных штрафов клиента по партнерской зоне: в список должны попадать только штрафы из той партнерской зоны, по которой выполняется запрос. Штрафы из других зон не должны отображаться или учитываться в характеристиках клиента для выбранной зоны. Расчет суммы непогашенных штрафов оставить по текущей логике, так как он уже использует точное сравнение партнерской зоны. |
| #OQ1.3.2-13 | Код вне BRD (обратный проход) | Что нашли: запись клиента в зоне ключуется суррогатным Id; запрета дублей в базе на пару (клиент, зона) нет, а обработчик создаёт запись по схеме «прочитать-потом-создать». При гонке возможны две записи на одну пару (клиент, зона) — с расходящимися бан / штраф / условием оплаты. \ ApplicationDbContext.cs:155 (только HasKey(Id)); UpdateOrCreateClientPartCommand.cs:40-51 | Разработка (Влад) | Open | Гайдбук 1.3.2 говорит о характеристиках как о «сгруппированной по партнёрским зонам информации» — то есть запись клиента в зоне по смыслу единственная, но явного требования уникальности в BRD нет. Вопрос к Владу: (А, рекоменд.) добавить запрет дублей в базе (уникальный индекс) на пару (ClientId, PartnerZoneId) и опереть обновление на него — БД-ограничение дёшево и закрывает гонку; (Б) оставить на уровне приложения, если гонок на этом пути не ожидается. Заодно завести требование уникальности в BRD. | Для одного клиента в одной партнерской зоне должна существовать только одна запись характеристик. При повторном изменении данных система должна обновлять существующую запись, а не создавать новую. Дубли записей по одной паре «клиент + партнерская зона» не допускаются, чтобы блокировка, штрафы, условия оплаты и заметки клиента не расходились между несколькими записями. |
| #OQ1.3.2-14 | Код BR1.3.2-9 | Требование говорит: «Только по предоплате» — условия оплаты: ID условий оплаты с установленным значением поля «% от итога». В коде режим PrepaymentOnly работает (исключает оплату на месте), но привязки к конкретному ID условия оплаты с «% от итога» на стороне клиента нет — на записи клиента в зоне есть только сам признак PayKind. Сам «% от итога» живёт как PrepaymentPercent на настройках оплаты зоны, а не выбирается через ID условий оплаты для клиента. \ ClientPartAggregate.cs:15; PaymentEvaluationHelper.cs:43-44; PaymentSettingsAggregate.cs:36 | Разработка (Влад) | Open | Гайдбук 1.3.2 отвечает на вопрос Влада «ID или процент из зоны»: у элемента «Только по предоплате» указано «ID условий оплаты с установленным значением поля „% от итога“» — то есть ссылка на условие нужна, процента из настроек зоны недостаточно. Продуктового выбора нет, архитектурный есть: вопрос к Владу — где хранить ссылку (общий корень с OQ1.3.2-2, OQ1.3.2-10). Отличие от OQ1.3.2-2: там режим не работает вовсе, здесь режим работает, но без привязки к условию. | Для режима «Только по предоплате» у клиента в партнерской зоне должно сохраняться конкретное условие оплаты из справочника этой зоны. Выбранное условие должно содержать заполненное значение «% от итога» и применяться при создании заказа и расчете оплаты. Общий процент из настроек партнерской зоны не заменяет выбранное условие оплаты клиента, а используется только там, где не требуется персональная привязка к условию. |
Вопросы по сверке с кодом
BRD 1.3.2 v0.1 · booking-monorepo · commit 9e19870 / dev/cms-conflict-warning · 2026-06-30 · якорь src/services/booking.clients (+ booking.discounts, booking.orders, booking.partner-management) Сводка: вопросов 14 (бизнесовых 7 / программных 7) · подтверждено без вопросов 3 (BR1.3.2-1, BR1.3.2-2, BR1.3.2-4) · отрезано находок обратного прохода 2
Бизнесовые
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| Б1 | BR1.3.2-5 | Блокировка должна хранить состав: партнерская зона, причина блокировки, дата блокировки | Блокировка хранится как один булев флаг IsBanned на записи клиента в зоне. Причины блокировки нет вообще; дата блокировки отдельным полем не хранится — момент бана попадает только в событие ClientBannedEvent (аудит), но не в саму запись блокировки. Зона есть. — нужно решение: нужны ли поля «причина» и «дата» как часть данных блокировки | ClientPartAggregate.cs:16; ClientPartMapping.cs:23-29; ChangeClientBanStatusCommand.cs:11 |
| Б2 | BR1.3.2-9 | «Персональные условия» — действуют особые условия предоплаты; партнер вручную указывает ID условий оплаты, добавленных в зоне | Вариант оплаты PersonalConditions есть в перечислении, но ни одной ветки логики по нему нет ни в заказах, ни в расчёте оплаты — клиент с PersonalConditions ведёт себя как Default. Список выбранных партнером ID условий оплаты нигде не хранится. — нужно решение: нужен ли режим «Персональные условия» и где хранить выбранные ID | ClientPayKind.cs:32; ClientPartAggregate.cs:15 (нет поля под список ID); веток по PersonalConditions нет в booking.orders и booking.partner-management |
| Б3 | BR1.3.2-9 | «Только по предоплате» — условия оплаты: ID условий оплаты с установленным значением поля «% от итога» | Режим PrepaymentOnly работает (исключает оплату на месте), но привязки к конкретному ID условий оплаты с «% от итога» на стороне клиента нет — на записи клиента в зоне есть только сам признак PayKind. Сам «% от итога» живёт как PrepaymentPercent на настройках оплаты зоны, а не выбирается через ID условий оплаты для клиента. — нужно решение: должен ли «Только по предоплате» ссылаться на конкретное условие оплаты, или достаточно процента из настроек зоны | ClientPartAggregate.cs:15; PaymentEvaluationHelper.cs:43-44; PaymentSettingsAggregate.cs:36 |
| Б4 | BR1.3.2-3 | Персональный промокод — «ручная скидка, связанная с Карточкой клиента» (один промокод на карточку) | В коде нет сущности «персональный промокод на одну карточку». Ручная скидка (ManualDiscountAggregate) адресуется на список клиентов и/или групп (связь многие-ко-многим), партнерская зона — это коллекция scope-ов, а не атрибут промокода клиента. «Промокоды клиента» — это выборка скидок, в чьём списке клиентов он есть. — нужно решение: модель «персональный промокод» = ручная скидка на список клиентов или нужна отдельная привязка 1:1 к карточке | ManualDiscountAggregate.cs:64-65 (_clients), :43-50 (scope/зоны) |
| Б5 | BR1.3.2-3 | Среди состава промокода — статус промокода | Статуса как отдельного хранимого поля нет: в скидках нет ни перечисления статусов, ни колонки. Есть только булев IsArchived; «активен / просрочен / исчерпан» вычисляется на лету в каждом запросе. На клиентских выборках промокодов статус не возвращается вовсе. — нужно решение: считать ли статус вычисляемым (архив + срок + лимит) или ввести явный статус | ArchiveInfo.cs:9-11; GetMyPromocodesQuery.cs:194-214; ManualPromocodeReadOnlyRepository.cs:39-45 |
| Б6 | BR1.3.2-6 | Условия оплаты клиента — состав: наименование, описание, способы оплаты | Справочник условий оплаты (PaymentFiltersAggregate) хранит наименование и описание, но способы оплаты в нём не хранятся — они привязаны к платёжному каналу зоны (AllowPaymentMethodOnPayChannel), уровнем выше. Запись условия оплаты сама по себе перечня способов оплаты не несёт. — нужно решение: считать способы оплаты частью условия оплаты или признать, что они принадлежат каналу оплаты зоны | PaymentFiltersAggregate.cs:26; PaymentMethod.cs:3-11; AllowPaymentMethodOnPayChannel.cs:12-20 |
| Б7 | BR1.3.2-7 | Заметка должна нести флаг переноса в каждое бронирование | Такого флага нет. Вместо него у заметки есть поле Admission — это видимость заметки (кому показывать: сотрудникам/только мне/только сервису/другим партнёрам), а не перенос в бронирования. — нужно решение: нужен ли отдельный булев «переносить в каждое бронирование» (это иной смысл, чем видимость) | ClientMessageAggregate.cs:17; ClientMessageAdmissionEnum.cs:3-9 |
Рекомендация / варианты решения
- Состав блокировки (Б1). Вариант А (рекоменд.): согласовать с продуктом и, если причина/дата нужны бизнесу, добавить их в запись блокировки (сейчас даже дата живёт только в событии, не в данных). Вариант Б: привести BRD к коду — блокировка как чистый флаг, причину/дату убрать из состава. Рекомендуется А: «причина» и «дата блокировки» обычно нужны для разбора спорных ситуаций.
- Условия оплаты клиента —
PersonalConditionsиPrepaymentOnly(Б2, Б3 — общая первопричина: на записи клиента в зоне хранится толькоPayKind, без привязки к конкретным условиям оплаты). Вариант А (рекоменд.): решить, что режимы со ссылкой на условия оплаты нужны, и тогда добавить хранение выбранных ID условий оплаты у клиента + реализовать веткуPersonalConditions. Вариант Б: упростить BRD до трёх рабочих режимов (Default/Trust/PrepaymentOnly через процент зоны), отказавшись от «Персональных условий». Рекомендуется А, если сценарий «индивидуальные условия по клиенту» реально востребован; иначе Б честнее отражает текущий код. - Модель и статус промокода (Б4, Б5). Вариант А: привести BRD к коду — описать промокод как ручную скидку на список клиентов с вычисляемым статусом (без зоны-атрибута и без хранимого статуса). Вариант Б (рекоменд.): согласовать с продуктом целевую модель «персональный промокод клиента» (1:1 к карточке? зона как атрибут? явный статус?) — это влияет и на состав, и на клиентские выборки, которые сейчас статус/лимиты не отдают. Рекомендуется Б: разрыв концептуальный, его нужно закрыть продуктовым решением до правок кода.
- Способы оплаты в условии оплаты (Б6). Вариант А (рекоменд.): привести BRD к коду — указать, что способы оплаты принадлежат каналу оплаты зоны, а условие оплаты несёт только наименование/описание/правила. Вариант Б: переносить/дублировать способы оплаты на запись условия оплаты. Рекомендуется А: текущее разнесение (методы на канале) — осознанная модель, дублирование породит рассинхрон.
- Смысл поля заметки (Б7). Вариант А (рекоменд.): согласовать с продуктом, нужен ли отдельный флаг «переносить в каждое бронирование» — и если да, добавить его (поле
Admissionпод это не подходит, у него другой смысл). Вариант Б: привести BRD к коду — заменить «флаг переноса» на «видимость заметки». Рекомендуется А, если перенос в бронирования — реальный сценарий; это не то же самое, что видимость.
Программные
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| П1 | BR1.3.2-8 | Блокировка ограничивает заказы только в той зоне, где установлена | При создании заказа проверка корректно по зоне: запись клиента берётся по (клиент, зона), и бан режет заказ только в этой зоне. Но обработчик события бана отменяет/архивирует уже существующие брони и заявки клиента, фильтруя их только по clientId, без зоны — хотя событие несёт PartnerZoneId. В итоге бан в одной зоне снимает ожидающие брони клиента во всех зонах. | Проверка при создании: CreateOrderCommandHandler.cs:91-107; утечка: ClientBannedConsumer.cs:37-69 (читается только context.Message.ClientId) |
| П2 | BR1.3.2-10 | Менять условия оплаты клиента можно только в зонах с подключённым терминалом; без платёжной информации — Default без редактирования | Проверки терминала при смене PayKind нет нигде: команда обновления записи клиента меняет PayKind без обращения к настройкам оплаты/терминалу. Флага «терминал подключён» наружу partner-management не отдаёт; состояние выводимо лишь косвенно из PaymentSettingsAggregate.TerminalId. Запрет редактирования и откат к Default не реализованы. | UpdateOrCreateClientPartCommand.cs:19,52; PaymentSettingsAggregate.cs:40; терминал-проверка — не найдено за обход (booking.clients, booking.partner-management) |
| П3 | BR1.3.2-9 | На запись клиента в зоне применяется одно из условий оплаты; набор значений — 4 режима | Команда обновления записи клиента принимает PayKind, но не валидирует согласованность: для PrepaymentOnly/PersonalConditions не требует и не сохраняет ID условий оплаты; любой режим можно выставить без проверок. — нужно решение: какие проверки обязательны при установке PayKind | UpdateOrCreateClientPartCommand.cs:19,43-52 |
| П4 | BR1.3.2-6 | Условия оплаты клиента по зонам (состав справочника) | На записи клиента в зоне (ClientPartAggregate) условие оплаты представлено только перечислением PayKind; ссылки на запись справочника условий оплаты (PaymentFiltersAggregate) у клиента нет. Сам справочник живёт в другом сервисе. Связь «клиент → конкретное условие оплаты» в данных отсутствует. — нужно решение: нужна ли у клиента прямая ссылка на ID условия оплаты | ClientPartAggregate.cs:15; PaymentFiltersAggregate.cs:25-26 |
| П5 | BR1.3.2-7 | Заметка должна нести ID заказа, по которому создана | Поля OrderId у заметки нет: ни в агрегате, ни в команде создания, ни в модели хранения. Привязать заметку к заказу нельзя. | ClientMessageAggregate.cs (нет OrderId); CreateClientMessageCommand.cs:10-15; ClientMessageMapping.cs:28-39 |
| П6 | BR1.3.2-4 | Список непогашенных штрафов по зоне | Основной расчёт суммы непогашенных штрафов корректен (PaidAmount < Amount, не в архиве, точное сравнение зоны). Но соседний метод выборки непогашенных штрафов по зоне фильтрует зону через PartnerZoneId <= ... (сравнение «меньше-либо-равно» по Guid вместо равенства) — это похоже на ошибку и тянет штрафы из «меньших» по значению зон. | Корректный: ForfeitsRepository.cs:54-74; подозрительный <=: ForfeitsRepository.cs:46 |
| П7 | вне BRD (обратный проход) | (нет явного требования об уникальности записи клиента в зоне) | Запись клиента в зоне (ClientPart) ключуется суррогатным Id; запрета дублей в базе на пару (клиент, зона) нет, а обработчик создаёт запись по схеме «прочитать-потом-создать». При гонке возможны две записи на одну пару (клиент, зона) — с расходящимися бан/штраф/PayKind. — нужно решение: добавить запрет дублей в базе на (ClientId, PartnerZoneId) | ApplicationDbContext.cs:155 (только HasKey(Id)); UpdateOrCreateClientPartCommand.cs:40-51 |
Рекомендация / варианты решения
- Утечка бана за пределы зоны (П1). Вариант А (рекоменд.): привести код к BRD — в обработчике события бана фильтровать брони/заявки ещё и по
PartnerZoneIdиз события (поле уже есть, просто не используется). Вариант Б: привести BRD к коду — признать, что бан в любой зоне снимает все ожидающие брони клиента (вряд ли желаемо). Рекомендуется А: локальная правка фильтра, устраняющая прямое противоречие требованию; проверка при создании заказа уже корректна. - Терминал-гейт смены условий оплаты (П2) + проверки при установке
PayKind(П3, П4). Вариант А (рекоменд.): после продуктового решения по BR1.3.2-9/-10 (см. бизнес-блок) реализовать на стороне booking.clients проверку «терминал подключён» (через флаг/запрос к partner-management) и валидацию составаPayKind(требовать ID условий оплаты там, где он нужен), плюс откат кDefaultбез редактирования при отсутствии платёжной информации. Вариант Б: оставить как есть и зафиксировать в BRD, что ограничение не применяется. Рекомендуется А, но он разблокируется только после бизнес-решения — сейчас это «не найдено за обход», а не просто недоделка. - ID заказа у заметки (П5). Вариант А (рекоменд.): добавить
OrderIdв агрегат/команду/модель заметки. Вариант Б: убрать пункт из состава в BRD. Рекомендуется А, если заметки по заказу — рабочий сценарий; правка небольшая и изолированная. - Ошибка
<=в выборке непогашенных штрафов (П6). Вариант А (рекоменд.): заменитьPartnerZoneId <= ...на==вGetCLientUnpaidForfeitsByPartnerZoneAsync. Вариант Б: оставить, если метод где-то намеренно полагается на это поведение (маловероятно). Рекомендуется А: почти наверняка дефект зональной фильтрации; уточнить у владельца кода, где этот метод используется. - Дубли записи клиента в зоне (П7). Вариант А (рекоменд.): добавить запрет дублей в базе (unique-индекс) на пару (ClientId, PartnerZoneId) и опереть обновление на него. Вариант Б: оставить на уровне приложения, если гонок на этом пути не ожидается. Рекомендуется А: запись клиента в зоне по смыслу единственная, БД-ограничение дёшево и закрывает гонку.