Направление работ: Back Код раздела: 1.3 Аккаунты и пользователи > Клиенты Статус: Готов Ответственный: Панкова Л.А. Версия: 0.2 Обновлено: 24.07.26


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

Раздел описывает Карточку клиента как сущность для хранения персональной информации об аккаунте и истории его взаимодействий с партнерами.

Карточка клиента создается и привязывается к аккаунту при регистрации, бронировании или добавлении сотрудника.

В текущем документе фиксируются состояния Карточки клиента и состав ее персональных, технических и партнерских данных.

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

Клиент — базовая пользовательская характеристика аккаунта МАРТ, которая присваивается при регистрации путем создания и привязки Карточки клиента.

Карточка клиента — сущность для хранения персональной информации об аккаунте и истории его взаимоотношений с партнерами в качестве получателя услуг.

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

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

Параметры и данные Карточки клиента — персональные данные, технические данные и данные по партнерам.

3. Scope

Входит

S1: Карточка клиента: создание, хранение персональных данных и связь с аккаунтом

S2: Состояния карточки клиента: карточка зарегистрированного пользователя и карточка незарегистрированного пользователя, включая правило, кто может изменять карточку в каждом состоянии.

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

Не входит

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

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

IDТребованиеКомментарийТип требованияВесСтатус
S1: BR1.3-1Клиент присваивается аккаунту МАРТ через создание и привязку Карточки клиентаБизнес1Частично \ #OQ1.3-1
S1: BR1.3-2Карточка клиента должна хранить персональную информацию об аккаунте и историю его взаимоотношений с партнерамиФамилияИмя (обязательный) • ОтчествоТелефон (обязательный) • Почтовый адресПубличное имя пользователя (латиница)Дата рожденияФотографияПолО себеСтраница в ВКПрофиль в InstagramКанал Telegram — ссылка на группу в Телеграмм • ГородГруппы клиента ( https://wiki.yandex.ru/homepage/d750da42741c/putevoditel-po-jekosisteme/guidebook/akkaunty-i-dostup/klienty/ad6842153a20/ ) • Группы деятельности ( https://wiki.yandex.ru/homepage/d750da42741c/putevoditel-po-jekosisteme/guidebook/administrirovanie-servisa/6d764dd604ff/17168a199436/ ) Нормы имени: минимум 2 буквы, максимум 50 букв, цифры запрещены.Бизнес1Частично \ #OQ1.3-2 \ #OQ1.3-3 \ #OQ1.3-4
S1: BR1.3-3Все заказы в системе должны привязываться к Карточке клиента за исключением заказов без статуса (Статус: PreReserv)Бизнес1Частично \ #OQ1.3-5
S2: BR1.3-4Карточка клиента может быть создана при регистрации аккаунта, бронировании в виджет без регистрации или добавлении сотрудника или клиентаСпособ создания определяет состояние Карточки клиентаСистемное1Частично \ #OQ1.3-10 \ #OQ1.3-11
S2: BR1.3-5Карточка клиента может находиться только в допустимых состоянияхДопустимые состояния: карточка зарегистрированного пользователя и карточка незарегистрированного пользователяБизнес1Не реализовано \ #OQ1.3-6
S2: BR1.3-6Право изменения Карточки клиента зависит от ее состоянияКарточку зарегистрированного пользователя может изменять только пользователь, которому она принадлежит. Карточку незарегистрированного пользователя может изменять только партнер или сотрудник партнераБизнес1Не реализовано \ #OQ1.3-7
S2: BR1.3-7Карточка зарегистрированного пользователя является источником прав доступа для связанного аккаунтаК таким карточкам всегда привязан хотя бы один клиентский тариф, он же Подписка (см. 1.3.1 Клиентская зона доступа).Бизнес1Не реализовано \ #OQ1.3-8
S3: BR1.3-8Правила заполнения и редактирования персональных данных зависят от состояния Карточки клиентаПерсональные данные заполняются клиентом или партнером. Телефон и почтовый адрес после завершения регистрации редактируются только через аккаунт. Публичное имя пользователя до регистрации недоступно, после ее завершения переносится значение логинаСистемное1Не реализовано \ #OQ1.3-12 \ #OQ1.3-13
S3: BR1.3-9Технические данные Карточки клиента должны заполняться автоматически вследствие системного процессингаСостав: кем создано, аккаунт, уведомления, подписки, автопродление тарифа, сферы, заказы, платежные документы, избранное, сохраненные карты, данные об устройствеСистемное1Частично \ #OQ1.3-14
S3: BR1.3-10Данные по партнерам должны заполняться каждым партнером в рамках одной партнерской зоны и группироваться в общий список характеристик клиентаСостав: скидки клиента, штрафы, признак блокировки партнерами, условия оплаты, группы деятельности, сообщения по клиенту. Партнерские группы добавляются вручную партнерамиБизнес1Частично \ #OQ1.3-9
S3: BR1.3-11Сообщения по клиенту должны хранить ограничение доступа на просмотрДопустимые значения: только я, сотрудники, все.Системное2Частично \ #OQ1.3-15 \ #OQ1.3-16 \ #OQ1.3-17
S3: BR1.3-12Система должна поддерживать фильтрацию Карточек клиентов по источнику создания (после релиза реализация)Системное3

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

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
#OQ1.3-1Код BR1.3-1Требование говорит: клиент привязывается к аккаунту связью один-к-одному. В коде отдельной связи нет — карточка и аккаунт считаются связанными по совпадению идентификаторов (ID карточки при создании берётся равным ID аккаунта), сама карточка создаётся асинхронно по событию регистрации. Отдельной колонки-ссылки и запрета «один аккаунт — одна карточка» в базе нет — инвариант держится только на соглашении «ID равны» и на проверке при регистрации. \ ClientAggregate.cs:131,148; AccountCreatedClientConsumer.cs:46,76Бизнес/РазработкаOpenВ гайдбуке 1.3 связь описана как «создание и привязка карточки к аккаунту», но способ гарантии (равенство ID / внешний ключ / уникальное ограничение) не оговорён — подтвердить нечем. (А) зафиксировать в BRD, что связь реализована равенством идентификаторов, и добавить защиту инварианта «один аккаунт — одна карточка» на уровне базы/процесса; (Б) оставить как есть, отметив отсутствие строгой связи в базе как осознанный компромисс. Гарантия на уровне базы — решает бизнес/архитектура.Нужно подтвердить, считаем ли связь аккаунта и карточки клиента через одинаковый ID достаточной. Если да, фиксируем это в BRD как текущую модель связи. Если нужна строгая гарантия «один аккаунт — одна карточка», разработке нужно добавить явную защиту на уровне базы или процесса создания карточки.
#OQ1.3-2Код BR1.3-2Требование говорит: Имя и Телефон — обязательные поля. В коде оба поля необязательные: доменной проверки на обязательность нет, при сборке имя подставляется пустой строкой — карточку можно сохранить без имени и телефона. \ Client.cs:30-31,60-61; ClientMapping.cs:24РазработкаЗакрывает PMГайдбук 1.3 прямо помечает Имя и Телефон как обязательные — значит это баг, а не спорный вопрос. Закрываю сам: включить проверку обязательности при создании/обновлении → тикет в Bitrix. Оговорка для реализации: сверить путь создания из виджета (партнёр бронирует без данных клиента); гайдбук исключения не даёт, поэтому по умолчанию обязательность применяется ко всем путям.Имя и телефон должны быть обязательными полями карточки клиента. Разработке нужно добавить проверку при создании и обновлении карточки, чтобы карточка не сохранялась без этих данных. Отдельно проверить сценарий создания из виджета без регистрации: если там допускается создание без имени или телефона, это исключение нужно явно описать в BRD.
#OQ1.3-3Код BR1.3-2Требование говорит: карточка хранит Почтовый адрес, Публичное имя (латиница), О себе, Профиль в Instagram, Город. В коде этих полей нет вообще — ни столбцов базы, ни в доменной модели. Из контактных ссылок есть только ВК и Telegram. \ Client.cs:30-145Бизнес/РазработкаOpenГайдбук 1.3 эти поля перечисляет — по спецификации они нужны. Но фактически их нет, и выбор «строить пять полей сейчас или урезать состав карточки» меняет продукт. (А) привести код к BRD — завести недостающие поля; (Б) привести BRD к коду — оставить фактический состав, недостающие вынести в явный список «планируется / не реализовано». Продуктовое решение о составе карточки. Связать с OQ1.3-13 (публичное имя нужно и для правил заполнения).Нужно подтвердить состав персональных данных карточки клиента. Сейчас в коде нет полей «почтовый адрес», «публичное имя», «о себе», «Instagram» и «город». Если эти поля нужны пользователю и должны быть частью карточки, разработке нужно добавить их в модель, базу и API. Если они не нужны или относятся к другому разделу, нужно скорректировать BRD и убрать их из состава карточки клиента.
#OQ1.3-4Код BR1.3-2Требование говорит: карточка хранит Фотографию. В коде столбца «фото» в карточке нет — фото подтягивается на чтение из внешнего сервиса Resources по gRPC. Функционально фото у карточки есть, но хранится не в ней. \ ImageService.cs:17,27-30; GetClientInfoByAccountIdHandler.cs:28-41РазработкаЗакрывает PMГайдбук 1.3 перечисляет фото как поле, но не требует хранить его именно в карточке — функциональная потребность закрыта (фото доступно из Resources). Продуктового выбора нет, вопрос в формулировке. Закрываю сам: уточнить в BRD, что фото отображается из сервиса Resources, а не хранится в карточке → правка формулировки BR1.3-2.Фотография клиента не хранится в самой карточке, а отображается из сервиса Resources при запросе данных карточки. Функционально фото доступно пользователю, поэтому доработка кода не требуется. Нужно уточнить BRD: фотография относится к отображаемым данным карточки клиента, но хранится во внешнем сервисе Resources.
#OQ1.3-5Код BR1.3-3Требование говорит: все заказы привязываются к карточке клиента, кроме заказов без статуса (PreReserv). В коде привязка гарантирована не всегда: у одиночного заказа поле клиента необязательное, строго обязательно оно только у групп заказов. То есть отдельный заказ может существовать без клиента. \ OrderAggregate.cs:122; OrderModelConfiguration.cs:41; ProcessingOrderGroup.cs:23,35Бизнес (Герман)OpenГайдбук 1.3 подтверждает правило («все заказы привязываются к карточкам»), но не описывает, бывают ли легитимные заказы без клиента (служебные/гостевые), кроме уже названного PreReserv. (А) сделать поле клиента обязательным и для одиночных заказов после выхода из PreReserv; (Б) уточнить в BRD, какие заказы без клиента допустимы. Решает бизнес; затрагивает разделы заказов (2.4 / 3.2).
#OQ1.3-6Код BR1.3-5Требование говорит: карточка может быть только в одном из допустимых состояний — зарегистрированного или незарегистрированного пользователя. В коде явного состояния (поля или перечисления) нет. Косвенные признаки ненадёжны: ID аккаунта проставляется всегда (в т.ч. пустой GUID для карточек из виджета), а флаг «профиль заполнен» отражает заполненность, а не регистрацию. \ ClientAggregate.cs:107,114,131,148,166; Client.cs:24-25,143-144Бизнес/РазработкаOpenГайдбук 1.3 явно вводит два состояния и разные права изменения для них — требование реально, спорить не о чем. Но состояние в домене не выражено, поэтому и правило доступа проверить нечем. (А, рекоменд.) ввести явное состояние карточки и на нём строить право изменения; (Б) если разграничение обеспечивается выше (шлюз / личные кабинеты) — описать это как внешний механизм и снять требование с сервиса карточки (сейчас такого разграничения не найдено нигде). Ключевое продукт+архитектура решение, разблокирует OQ1.3-7, OQ1.3-11, OQ1.3-12, OQ1.3-13.Нужно подтвердить, должно ли состояние карточки клиента явно храниться в системе: «зарегистрированный пользователь» или «незарегистрированный пользователь». Сейчас такого состояния в коде нет, поэтому система не может надежно отличать эти два случая. Если правило остается в BRD, разработке нужно добавить явное состояние карточки и использовать его в связанных проверках: кто может редактировать карточку и какие поля доступны после регистрации. Если состояние определяется вне этого сервиса, нужно описать этот внешний механизм в BRD.
#OQ1.3-7Код BR1.3-6Требование говорит: карточку зарегистрированного меняет только её владелец, карточку незарегистрированного — только партнёр или сотрудник партнёра. В коде разграничения нет вообще: ID вызывающего используется лишь чтобы записать «кем обновлено», сравнения с владельцем нет, на шлюзе проверка бинарная «должен быть авторизован». Фактически любой авторизованный может изменить любую карточку по её ID. \ UpdateClientCommandHandler.cs:39-44,83; ClientAggregate.cs:210-214; RpcAnonByRouteAuthorization.cs:48-57Разработка (Влад)OpenГайдбук 1.3 право доступа по состоянию подтверждает — правило реально, и это дыра в безопасности (любой правит любую карточку). Реализация зависит от наличия состояния (OQ1.3-6). Вопрос к Владу: где живёт проверка (домен карточки / шлюз / кабинеты) и как её ввести после появления состояния. Связать с OQ1.3-6.Нужно определить место, где будет применяться правило изменения карточки клиента. Сейчас в слое авторизации доступ в целом проверяется по permissions, но отдельного правила для изменения карточки с учетом ее состояния и владельца не найдено. Как вариант, можно поставить требованию вес 3 и дождаться полной реализации ABAC, после чего добавить отдельный permission/правило, которое будет отвечать за изменение карточки клиента: владелец редактирует зарегистрированную карточку, партнер или сотрудник партнера — незарегистрированную.
#OQ1.3-8Код BR1.3-7Требование говорит: к зарегистрированной карточке всегда привязан хотя бы один клиентский тариф (Подписка), при создании/привязке карточки привязывается подписка «По умолчанию». В коде автопривязки нет: признак «по умолчанию» у тарифа задаётся вручную и относится к каталогу тарифов (у тарифа нет привязки к клиенту), обработчик создания клиента и consumer регистрации тариф не привязывают — в сервисе авторизации TODO-заглушка. Гарантии «хотя бы один тариф» нет. \ ClientTariffAggregate.cs:13,64; CreateClientCommandHandler.cs:19-52; AccountCreatedClientConsumer.cs:93-131; RegistrationMobileEndCommandHandler.cs:122Бизнес/РазработкаOpenГайдбук 1.3 и 1.3.1 прямо говорят: базовая подписка активируется при создании карточки, подписка «По умолчанию» привязывается — правило реально, это недоделка. По скоупу инвариант авто-привязки живёт в 1.3.1 / 8.3.4, в 1.3 требование BR1.3-7 остаётся ссылкой; чинить и заводить недоработку — в 1.3.1. (А, рекоменд.) реализовать автопривязку подписки «По умолчанию» при создании/привязке (снять TODO); (Б) оставить в 1.3 только ссылку, требование держать в 1.3.1. Координация с 1.3.1 и 8.3.4.Нужно подтвердить, где должна выполняться автоматическая привязка подписки «По умолчанию» при создании или привязке карточки клиента: в сервисе карточек клиента или в разделе подписок/клиентской зоны доступа. Сейчас в коде такой автопривязки нет, поэтому у зарегистрированной карточки не гарантируется наличие хотя бы одного тарифа. Если правило остается обязательным, разработке нужно реализовать автопривязку подписки в согласованном сервисе, а в BRD 1.3 оставить ссылку на раздел 1.3.1, где описывается логика подписок.
#OQ1.3-9Код BR1.3-10Требование говорит: данные по партнёрам заполняются в рамках одной партнёрской зоны; в состав входят в т.ч. скидки клиента и группы деятельности. В коде зонирование неоднородно: блокировка, штраф, условия оплаты и сообщения привязаны к партнёрской зоне (верно), а скидки клиента и группы деятельности — нет (поля зоны в их моделях нет, хранятся на уровне клиента целиком). \ ClientPartAggregate.cs:13,15-17; ClientMessageAggregate.cs:11; ClientDiscount.cs; ClientServiceGroupEntity.csБизнес/РазработкаOpenГайдбук разводит два пункта по-разному, ответ раздельный. (1) Группы деятельности — гайдбук 1.3 относит их к персональным данным (см. 8.2.5), а не к данным по партнёрам → код верен, ошибка в BRD → убрать группы деятельности из состава BR1.3-10 (закрывается правкой BRD). (2) Скидки клиента — гайдбук 1.3.2 даёт скидке поле «Партнёрская зона» → скидки должны быть в рамках зоны → код привести к спецификации. По скоупу состав скидки — в 1.3.2, сущность скидки — в 2.7.1; зонирование согласовать там, в 1.3 не дублировать.Нужно разделить данные по партнерам. Блокировки, штрафы, условия оплаты и сообщения уже привязаны к партнерской зоне. Группы деятельности не нужно относить к партнерским данным, их следует перенести в персональные данные карточки клиента. По скидкам нужно подтвердить бизнес-правило: скидка должна быть общей для клиента или отдельной в рамках партнерской зоны. Если скидки должны зависеть от партнерской зоны, разработке нужно добавить такую привязку в модель скидок; если скидки общие, нужно уточнить это в BRD.
#OQ1.3-10Код BR1.3-4Требование говорит: карточка может быть создана при бронировании в виджете без регистрации. В коде сценарий реализован, но как отдельный вызываемый метод API (источник фиксируется как «виджет», ID аккаунта — пустой GUID). При этом ни один обработчик событий заказа карточку не создаёт (consumer закрытия/отмены заказа выходит, если клиента нет). То есть «при бронировании» зависит от того, вызовет ли метод сторона виджета — внутри сервиса карточки такого триггера нет. \ CreateClientFromWidgetCommandHandler.cs:36-50; OrderClosedClientConsumer.cs:41-45Разработка (Влад)OpenГайдбук 1.3 подтверждает, что карточка создаётся при бронировании, но не оговаривает механизм (событие заказа vs явный вызов). Вопрос к Владу: где в архитектуре живёт виджет-флоу — (А) зафиксировать, что карточку создаёт сторона виджета явным вызовом (описать в BRD/контракте, кто и когда вызывает); (Б) создавать карточку в обработчике события бронирования.Автоматическое создание карточки клиента при бронировании из виджета сейчас не подтверждено кодом. В коде есть отдельные методы CreateClientFromWidget / CreateClientFromWidgetMobile, но они помечены как unused, не найдены среди вызываемых методов и не открыты для анонимного доступа виджета на gateway. Нужно выбрать целевую механику: либо виджет сначала отдельным доступным вызовом создает карточку клиента и затем передает clientId в заказ, либо карточка создается автоматически внутри сценария CreateOrderWidget. Для автоматического создания потребуется расширить контракт CreateOrderWidget, потому что сейчас в заказ из виджета не передаются ФИО/телефон/email клиента.
#OQ1.3-11Код BR1.3-4Требование говорит: способ создания карточки определяет её состояние. В коде способ создания фиксируется (Web / виджет / партнёрский Web), но никакого состояния из него не выводится — поля состояния нет (см. OQ1.3-6). Связь «способ → состояние» не реализована. \ EventSourceMapper.cs:14-25; ClientAggregate.csРазработкаOpenГайдбук подтверждает «исходя из способа определяется состояние». Проблема — производная от отсутствия поля состояния. Рекомендация: отдельного решения не требует — как только появится состояние (OQ1.3-6), способ создания в него отображается. Пока — описать в BRD, что источник фиксирует канал, но состояние из него не выводится. Связать с OQ1.3-6.Способ создания карточки сейчас фиксируется как источник создания, но не определяет состояние карточки. Реализация зависит от OQ1.3-6: после добавления явного состояния карточки разработке нужно настроить правило, какое состояние присваивается при каждом способе создания. До этого в BRD нужно указать, что текущий источник создания показывает только канал, через который создана карточка, но не является состоянием карточки.
#OQ1.3-12Код BR1.3-8Требование говорит: телефон и почтовый адрес после завершения регистрации редактируются только через аккаунт (правила заполнения зависят от состояния карточки). В коде ограничения нет: обновление карточки безусловно перезаписывает телефон и email независимо от состояния, почтового адреса в модели нет вовсе, понятия «после регистрации» не существует. \ ClientAggregate.cs:204-205; UpdateClientCommandHandler.cs:83Бизнес/РазработкаOpenГайдбук 1.3 правило подтверждает дословно. Но реализация упирается в отсутствие состояния (OQ1.3-6) и отсутствующий почтовый адрес (OQ1.3-3), плюс вопрос, где живёт правило — в 1.3 или в аккаунте 1.1. (А) реализовать гейтинг после появления состояния; (Б) если редактирование контактов через аккаунт обеспечивается разделом 1.1 — перенести правило туда. Связать с OQ1.3-6, OQ1.3-3.Нужно подтвердить, где должно выполняться правило редактирования контактов после регистрации: в карточке клиента или в аккаунте. Сейчас карточка может напрямую обновить телефон и email, а почтового адреса в модели нет. Если после регистрации контакты должны меняться только через аккаунт, разработке нужно запретить прямое изменение этих полей в карточке клиента и связать проверку с состоянием карточки из OQ1.3-6. Если это правило относится к разделу аккаунтов, его нужно перенести в BRD 1.1, а в 1.3 оставить ссылку.
#OQ1.3-13Код BR1.3-8Требование говорит: публичное имя пользователя до регистрации недоступно, после завершения регистрации в него переносится значение логина. В коде поля «публичное имя» нет, переноса логина после регистрации тоже нет — правило не реализовано. \ ClientAggregate.cs; Client.csБизнес/РазработкаOpenГайдбук 1.3 правило описывает явно. Но оно завязано на отсутствующее поле (публичное имя — см. OQ1.3-3) и на состояние (OQ1.3-6). (А) завести поле и реализовать перенос логина после регистрации; (Б) если поле исключается из состава карточки (решение по OQ1.3-3) — снять и это правило. Связать с OQ1.3-3, OQ1.3-6.Нужно подтвердить, остается ли «публичное имя пользователя» частью карточки клиента. Сейчас такого поля в коде нет, поэтому логин после регистрации некуда переносить. Если публичное имя нужно, разработке нужно добавить поле и реализовать заполнение значением логина после завершения регистрации. Если поле не требуется, нужно убрать из BRD правило про публичное имя и перенос логина. Решение связано с OQ1.3-3 и OQ1.3-6.
#OQ1.3-14Код BR1.3-9Требование говорит: технические данные заполняются автоматически; состав — кем создано, аккаунт, уведомления, подписки, автопродление, сферы, заказы, платёжные документы, избранное, сохранённые карты, устройство. В коде часть состава отсутствует (уведомления, сохранённые карты, устройство не хранятся), заказы и платёжные документы живут в других сервисах и попадают как производные, подписка/тариф на основном методе карточки отдаётся как null (TODO). Подтверждено рабочими: кем создано, аккаунт, автопродление, сферы, избранное. \ Client.cs:30-145; GetClientInfoByAccountIdResponse.cs:31; GetClientInfoByAccountIdHandler.cs:45Бизнес/РазработкаOpenГайдбук 1.3 сам ссылается на внешние разделы: уведомления (7.2.1), заказы (9.4.1), платёжные документы (5.4), сохранённые карты (5.5), устройство (9.1), подписки/автопродление (1.3.1) — эти данные по замыслу живут не в карточке. (Б, рекоменд.) уточнить BRD: такие данные описать как «отображается из сервиса X», а не «хранится в карточке»; перечислить, что заполняется фактически и что отдаётся как null; (А) добавить реально отсутствующее (уведомления/устройство/карты или ссылки). Подписка null — связать с OQ1.3-8 (живёт в 1.3.1). Состав согласовать с продуктом.Нужно уточнить состав технических данных карточки клиента. Сейчас карточка хранит не все перечисленные данные: часть данных находится в других сервисах и должна только отображаться в карточке. В BRD нужно разделить данные на две группы: что реально хранится в карточке клиента, и что отображается из других разделов/сервисов. Если какие-то данные должны именно храниться в карточке, разработке нужно добавить их в модель и API. Отдельно нужно связать вопрос подписки с OQ1.3-8 и разделом 1.3.1.
#OQ1.3-15Код BR1.3-11Требование говорит: сообщения по клиенту хранят ограничение доступа на просмотр со значениями «только я / сотрудники / все» (три значения). В коде перечисление содержит четыре значения: EmployeesAndMe, OnlyMe, OnlyService, OtherPartners. Сопоставление приблизительное: «сотрудники» ≈ EmployeesAndMe (включает автора, ограничено зоной), «все» ≈ OtherPartners; чистого «все» нет, а OnlyService в гайдбуке не описано. \ ClientMessageAdmissionEnum.cs:5-8Бизнес/РазработкаOpenГайдбук 1.3 называет ровно три значения. (А, рекоменд.) свести к трём (только я / сотрудники / все) — OnlyService/OtherPartners убрать или отобразить на три; (Б) задокументировать все четыре и их семантику, уточнив, что «сотрудники» ограничены партнёрской зоной. В любом варианте закрыть дыру с непокрытым OnlyService (OQ1.3-16). Связать с OQ1.3-16.Нужно подтвердить модель доступа к сообщениям по клиенту. В BRD указаны три варианта: «только я», «сотрудники», «все», а в коде сейчас четыре: EmployeesAndMe, OnlyMe, OnlyService, OtherPartners. Если правильна модель из BRD, разработке нужно привести код к трем значениям или корректно сопоставить текущие значения с ними. Если нужны все четыре варианта, нужно описать их в BRD понятными названиями и правилами видимости. Решение связано с OQ1.3-16, потому что одно из текущих значений сейчас не обрабатывается при чтении.
#OQ1.3-16Код BR1.3-11Проблема: фильтр видимости на чтении обрабатывает только 3 из 4 значений; значение OnlyService не покрыто ни одной веткой — такие сообщения не вернутся никогда (тихо невидимы, данные теряются на чтении). Плюс «только я» трактуется как «только автор-сотрудник», а не клиент. \ ClientMessagesReadRepository.cs:25-30; GetClientMessagesHandler.cs:29-33Разработка (Влад)OpenТихая потеря данных на чтении — дыру закрыть обязательно, независимо от модели значений. Но какую ветку добавлять (или убрать OnlyService) зависит от решения по составу значений (OQ1.3-15). Вопрос к Владу: после выбора модели — либо добавить ветку для OnlyService, либо убрать значение и мигрировать данные. Связать с OQ1.3-15.Нужно устранить ситуацию, при которой сообщения с доступом OnlyService не возвращаются при чтении. После решения по OQ1.3-15 разработке нужно либо добавить корректную обработку OnlyService в фильтр видимости, либо убрать это значение и мигрировать существующие данные на поддерживаемые варианты доступа. Также нужно явно зафиксировать, что «только я» означает автора сообщения, а не клиента.
#OQ1.3-17Код BR1.3-11Проблема: ограничение доступа применяется только на чтение. На запись (редактирование / удаление / архивацию сообщения) проверки прав нет — любой авторизованный может изменить или удалить любое сообщение по его ID независимо от значения ограничения. \ UpdateClientMessageHandler.cs:22-27; ArchiveClientMessageHandler.cs; DeleteClientMessageHandler.csБизнес (Герман)OpenГайдбук 1.3 говорит только про ограничение на просмотр — про запись ничего нет, додумывать не за что. Но открытая на запись механика приватных заметок партнёров о клиенте — смежная дыра в безопасности. Решение бизнеса: нужно ли симметрично ограничивать запись (автор или сотрудник зоны). Рекомендация — да, ограничить; если сознательно нет — отметить в BRD, что ограничение только на просмотр.
#OQ1.3-18Код вне BRDЧто нашли: на карточке клиента лежит целая подсистема — баллы лояльности, кешбэк, лимиты, штрафы. В требованиях 1.3 как состав это не описано. \ Client.cs:108-109; ClientAggregate.cs:109,289РазработкаЗакрывает PMДом у всех частей есть по скоупу: кешбэк и лимиты активных заказов — гайдбук 1.3.1 (подписки), штрафы — 1.3.2 / BR1.3-10, баллы — 8.4.2 (Настройки балльной системы) / 2.7.3 (Скидки баллами). Закрываю сам: «Не входит → 1.3.1, 1.3.2, 8.4.2 / 2.7.3». Если баллы физически хранятся на карточке — максимум одна строка в технических данных со ссылкой на 8.4.2, правила не дублировать.Доработка кода не требуется. Баллы, кешбэк, лимиты и штрафы не нужно подробно описывать в разделе 1.3 как отдельную логику карточки клиента. Эти правила относятся к смежным разделам: подписки и лимиты — к 1.3.1, штрафы — к 1.3.2, баллы и кешбэк — к разделам про балльную систему и скидки. В 1.3 можно оставить только краткое упоминание, что такие данные могут отображаться на карточке клиента из связанных разделов.
#OQ1.3-19Код вне BRDЧто нашли: на карточке есть скрытая стейт-машина заполненности профиля — флаг «профиль заполнен» и процент заполненности (0–100), защёлкивается при 100%. В требованиях 1.3 не описано. \ Client.cs:117-118,143-144; ClientAggregate.cs:298-307Бизнес (Герман)OpenВ гайдбуке 1.3 (и смежных 1.3.1 / 1.3.2) про признак заполненности профиля и процент ничего нет — дома находке не нашлось, подтвердить нечем. Решение бизнеса: это осознанное бизнес-правило (напр. для онбординга / фронта) — тогда завести требование (в 1.3 или в разделе профиля фронта); или это внутренняя техническая деталь — тогда сознательно не описывать. Не додумываю за гайдбук.
#OQ1.3-20Код вне BRDЧто нашли: при создании/обновлении карточки проверяется, что дата рождения не в будущем. В требованиях 1.3 инвариант не описан. \ AdditionalClientInfo.cs:13-16РазработкаЗакрывает PMБезобидный инвариант на поле карточки (дата рождения — поле 1.3 по гайдбуку), продуктового выбора нет. Закрываю сам: зафиксировать как техническую валидацию — комментарием к BR1.3-2 или отдельным техническим требованием. Тикета на код не требуется (уже работает), только документирование.Доработка кода не требуется. Проверка даты рождения уже реализована: дата рождения клиента не может быть в будущем. Нужно зафиксировать это в BRD как техническую валидацию поля «Дата рождения» в составе персональных данных карточки клиента.
#OQ1.3-21Код вне BRDЧто нашли: в коде есть операции над карточкой, не описанные в составе 1.3: архивация/восстановление (мягкое удаление), привязка аккаунта к существующей карточке, бан/разбан в рамках зоны, избранные комнаты, ресинк скидок. \ ArchiveClientCommand.cs:7; AttachAccountToClientCommand.cs:8; ChangeClientBanStatusCommand.cs:11; AddRoomToFavouriteCommand.cs:13; ResyncClientDiscountsCommand.cs:9Бизнес (Герман)OpenЧасть операций уже имеет дом: бан/разбан — BR1.3-10 и гайдбук 1.3.2 (Блокировки), избранное — технические данные BR1.3-9. Не описаны нигде: архивация/восстановление, привязка аккаунта, ресинк скидок. Решение бизнеса: какие из неописанных операций внести в scope 1.3 требованиями (создание/архивация/восстановление ожидаются методологией Back в разделе операций), какие сознательно оставить вне. Гайдбук 1.3 про эти операции молчит.
#OQ1.3-22Код вне BRDЧто нашли: сервисные признаки клиента сверх «сфер» — должности, уровни навыка, музыкальные жанры, инструменты, танцевальные стили. Помечено Владом как «сверх BRD». \ ClientServicesTypesEnum.cs:11-31; GetClientInfoByAccountIdResponse.cs:24-28РазработкаЗакрывает PMНа деле это не «сверх BRD»: гайдбук 1.3 перечисляет их как состав Групп деятельности (уровень навыков, музыкальный инструмент, стиль танца, музыкальный жанр — см. 8.2.5). Закрываю сам: отнести к данным «Группы деятельности» (BR1.3-2), сослаться на 8.2.5; отдельным вопросом не является. «Должности» — сверить с 8.2.5, там же «виды деятельности».Доработка кода не требуется. Должности, уровни навыка, музыкальные жанры, инструменты и танцевальные стили нужно относить к «Группам деятельности» в составе персональных данных карточки клиента. Это уже связано с BR1.3-2 и смежным разделом про группы деятельности, поэтому отдельное требование в 1.3 заводить не нужно.
#OQ1.3-23Код вне BRDЧто нашли: признак подтверждения email подтягивается в карточку из внешнего сервиса Identity на чтении. В составе 1.3 не описан. \ GetClientInfoByAccountIdResponse.cs:9; GetClientInfoByAccountIdHandler.cs:31,43РазработкаЗакрывает PMПодтверждение email — зона аккаунта/авторизации (1.1), карточка лишь отображает признак из Identity на чтении (как фото из Resources в OQ1.3-4). Закрываю сам: «Не входит → 1.1» либо одна строка «отображается из Identity» в технических данных; правила подтверждения email не дублировать.Доработка кода не требуется. Признак подтверждения email не хранится и не изменяется в карточке клиента, а отображается из сервиса Identity при чтении карточки. Правила подтверждения email относятся к разделу аккаунтов/авторизации, поэтому в 1.3 достаточно указать, что этот признак отображается из внешнего сервиса.

Подстраницы


Вопросы по сверке с кодом

BRD 1.3 v0.1 · booking-monorepo · commit 9e19870 / dev/cms-conflict-warning · 2026-06-30 · якорь src/services/booking.clients (+ кросс-проверка src/services/booking.authentication) Сводка: 18 вопросов (бизнесовых 9, программных 9); подтверждено без вопросов 0; отрезано находок обратного прохода 4

Бизнесовые

Связано сЧто в BRDЧто в кодеГде в коде
Б1BR1.3-1Клиент присваивается аккаунту через создание и привязку Карточки клиента (связь аккаунт↔карточка)Связь реализована как совпадение идентификаторов: при создании ID карточки берётся равным ID аккаунта (clientId = accountId ?? Guid.NewGuid(), далее AccountId = new AccountId(clientId)), то есть ClientId == AccountId. Карточка создаётся асинхронно по событию AccountCreatedEvent через consumer. Отдельной колонки account_id со строгой связью в базе нет — связь держится только на соглашении «ID равны». — нужно решение: считать ли «равенство ID» достаточной моделью связи 1:1ClientAggregate.cs:131,148; AccountCreatedClientConsumer.cs:46,76; Account.cs:85
Б2BR1.3-2Имя и Телефон помечены как обязательныеВ коде оба поля необязательные: first_name и phone_number допускают пустое значение, доменной проверки на обязательность нет (при сборке имя подставляется как пустая строка)Client.cs:30-31,60-61; ClientMapping.cs:24
Б3BR1.3-2Карточка хранит поля: Почтовый адрес, Публичное имя пользователя (латиница), О себе, Профиль в Instagram, ГородЭтих полей в карточке нет ни как столбцов базы, ни в доменной модели (поиск по address/postal, public_name/username/login, about/bio, instagram, city — пусто). Из контактных ссылок есть только ВК и Telegram. — нужно решение: нужны ли эти поля карточке или часть из них исключается из составаClient.cs:30-145 (поля отсутствуют)
Б4BR1.3-2Карточка хранит ФотографиюСтолбца «фото» в карточке нет. Фотография и файлы подтягиваются на чтение из внешнего сервиса Resources по gRPC в момент запроса, в самой карточке не хранятся. — нужно решение: считать ли это «хранением в карточке» по смыслу BRDImageService.cs:17,27-30; GetClientInfoByAccountIdHandler.cs:28-41
Б5BR1.3-3Все заказы должны привязываться к Карточке клиента за исключением заказов без статуса (Статус: PreReserv)Привязка гарантирована не для всех заказов: у одиночного заказа client_id допускает пустое значение (необязательный столбец), а строго обязателен он только у групп заказов (processing group). То есть отдельный заказ может существовать без клиентаOrderAggregate.cs:122; OrderModelConfiguration.cs:41; ProcessingOrderGroup.cs:23,35
Б6BR1.3-5Карточка может находиться только в допустимых состояниях: зарегистрированного / незарегистрированного пользователяЯвного состояния (поля или перечисления «зарегистрирован / не зарегистрирован») в коде нет. Косвенные признаки ненадёжны: ID аккаунта проставляется всегда (в т.ч. для карточек из виджета — там подставляется пустой GUID), а флаг AlreadyFilled отражает заполненность профиля, а не регистрацию. — нужно решение: моделировать ли состояние явноClientAggregate.cs:107,114,131,148,166; Client.cs:24-25,143-144
Б7BR1.3-6Карточку зарегистрированного может менять только её владелец; карточку незарегистрированного — только партнёр или сотрудник партнёраНикакого разграничения по состоянию карточки и роли вызывающего нет. ID аккаунта в команде обновления используется только чтобы записать «кем обновлено», сравнения с владельцем карточки нет. На шлюзе проверка бинарная — «должен быть авторизован», без ролей / владельца / партнёра. Фактически любой авторизованный может изменить любую карточку по её IDUpdateClientCommandHandler.cs:39-44,83; ClientAggregate.cs:210-214; RpcAnonByRouteAuthorization.cs:48-57
Б8BR1.3-7При создании или привязке карточки к аккаунту к ней привязывается Подписка с признаком «По умолчанию»; у зарегистрированной карточки всегда есть хотя бы один клиентский тарифПризнак «по умолчанию» (IsDefault) у тарифа есть, но он задаётся вручную при создании тарифа партнёром и относится к каталогу тарифов (у ClientTariff нет привязки к клиенту). Автоматической привязки тарифа или подписки при создании карточки нет — обработчик создания клиента и consumer регистрации тариф не привязывают (в сервисе авторизации на этот счёт стоит TODO-заглушка). Гарантии «хотя бы один тариф всегда привязан» в коде нетClientTariffAggregate.cs:13,64; CreateClientCommandHandler.cs:19-52; AccountCreatedClientConsumer.cs:93-131; RegistrationMobileEndCommandHandler.cs:122
Б9BR1.3-10Данные по партнёрам заполняются каждым партнёром в рамках одной партнёрской зоны (скидки, штрафы, блокировка, условия оплаты, группы деятельности, сообщения)Часть данных действительно привязана к партнёрской зоне (блокировка, штраф, условия оплаты — на ClientPart с PartnerZoneId; сообщения — с PartnerZoneId). Но скидки клиента и группы деятельности НЕ разрезаны по партнёрской зоне: в их моделях нет поля партнёрской зоны, они хранятся на уровне клиента целиком. — нужно решение: должны ли скидки и группы деятельности быть в рамках зоныClientPartAggregate.cs:13,15-17; ClientMessageAggregate.cs:11; ClientDiscount.cs (нет PartnerZoneId); ClientServiceGroupEntity.cs (нет PartnerZoneId)

Рекомендация / варианты решения

  • Связь аккаунт↔карточка 1:1 (Б1). Вариант А (рекоменд.): зафиксировать в BRD, что связь реализована равенством идентификаторов (ClientId == AccountId), и добавить требование на защиту инварианта «один аккаунт — одна карточка» в базе или в процессе; сейчас он держится только на соглашении и на pre-check при регистрации. Вариант Б: оставить как есть, явно отметив отсутствие строгой связи в базе как осознанный компромисс. Нужна ли гарантия на уровне базы — решает бизнес / архитектура.
  • Обязательность Имени и Телефона (Б2). Вариант А (рекоменд.): привести код к BRD — добавить проверку обязательности при создании и обновлении, иначе требование декларативно. Вариант Б: привести BRD к коду — признать оба поля необязательными (например, при создании из виджета телефон может быть единственным контактом, а имя — отсутствовать). Согласовать с продуктом, какой минимальный набор обязателен в каждом сценарии создания.
  • Состав персональных полей: адрес, публичное имя, о себе, Instagram, город, фото (Б3, Б4). Вариант А: привести код к BRD — завести недостающие поля и решить, хранить ли фото в карточке или продолжать брать из Resources. Вариант Б (рекоменд.): привести BRD к коду — оставить фактический состав (ФИО, дата рождения, пол, email, телефон, ВК, Telegram + фото из Resources), а недостающие поля вынести в явный список «планируется / не реализовано». По существу это продуктовое решение о составе карточки.
  • Привязка всех заказов к клиенту (Б5). Вариант А (рекоменд.): привести код к BRD — сделать client_id обязательным и для одиночных заказов (как уже сделано для групп). Вариант Б: уточнить в BRD, что заказ без карточки клиента допустим (напр. служебные / гостевые заказы), и описать эти случаи. Зависит от того, бывают ли легитимные заказы без клиента.
  • Состояние карточки «зарегистрированный / незарегистрированный» (Б6) и право изменения по состоянию (Б7). Общая первопричина — состояние карточки в домене не выражено, поэтому и правило доступа по состоянию проверить нечем. Вариант А (рекоменд.): привести код к BRD — ввести явное состояние карточки и на его основе разграничить право изменения (владелец vs партнёр / сотрудник). Вариант Б: привести BRD к коду — если разграничение доступа по правилам BRD на самом деле обеспечивается выше (на шлюзе / в личных кабинетах), описать это как внешний механизм и снять требование с этого сервиса; но сейчас такого разграничения не найдено нигде. Это ключевое продуктовое и архитектурное решение, разблокирует Б6/Б7.
  • Подписка «по умолчанию» при создании карточки (Б8). Вариант А (рекоменд.): привести код к BRD — реализовать автопривязку тарифа-подписки с признаком «по умолчанию» при создании или привязке карточки (сейчас стоит TODO-заглушка). Вариант Б: привести BRD к коду — если «подписка по умолчанию» относится к разделу 1.3.1 и здесь лишь упоминается, перенести требование туда и убрать из карточки. Решает продукт совместно с разделом 1.3.1.
  • Партнёрское разрезание скидок и групп деятельности (Б9). Вариант А: привести код к BRD — добавить партнёрскую зону в модель скидок и групп деятельности. Вариант Б (рекоменд.): уточнить BRD — если скидки и группы деятельности по замыслу общеклиентские (а к зоне привязаны только блокировка, штрафы, оплата, сообщения), скорректировать формулировку «в рамках одной партнёрской зоны» для этих двух пунктов. Согласовать с продуктом фактическую модель видимости.

Программные

Связано сЧто в BRDЧто в кодеГде в коде
П1BR1.3-4Карточка может быть создана при бронировании в виджет без регистрацииСценарий «создание из виджета без регистрации» реализован, но как отдельный вызываемый метод API (CreateClientFromWidgetCommand, источник фиксируется как Widget, ID аккаунта — пустой GUID). При этом ни один обработчик событий заказа карточку не создаёт (consumer закрытия / отмены заказа выходит, если клиента нет). То есть «при бронировании» зависит от того, вызовет ли этот метод сторона виджета — внутри booking.clients такой триггер не найденCreateClientFromWidgetCommandHandler.cs:36-50; OrderClosedClientConsumer.cs:41-45
П2BR1.3-4Способ создания определяет состояние Карточки клиентаСпособ создания фиксируется как SourceId (Web / Widget / партнёрский Web), но никакого «состояния карточки» из него не выводится — поля состояния нет (см. Б6). Связь «способ → состояние» в коде не реализованаEventSourceMapper.cs:14-25; ClientAggregate.cs (нет поля состояния)
П3BR1.3-8Телефон и почтовый адрес после завершения регистрации редактируются только через аккаунтОграничения нет: обновление карточки безусловно перезаписывает телефон и email независимо от состояния регистрации; почтового адреса в модели нет вовсе. Признака «после регистрации» в логике редактирования не существуетClientAggregate.cs:204-205; UpdateClientCommandHandler.cs:83
П4BR1.3-8Публичное имя пользователя до регистрации недоступно, после регистрации в него переносится значение логинаПоля «публичное имя пользователя» в карточке нет, переноса логина после регистрации тоже нет (поиск по publicname / login / username — пусто). Правило не реализовано(поле и логика отсутствуют; ClientAggregate.cs, Client.cs)
П5BR1.3-9Технические данные карточки заполняются автоматически. Состав: кем создано, аккаунт, уведомления, подписки, автопродление тарифа, сферы, заказы, платёжные документы, избранное, сохранённые карты, данные об устройствеЧасть состава отсутствует: «уведомления», «сохранённые карты», «данные об устройстве» в сервисе карточки не хранятся (поиск по device, saved_card, notification_settings — пусто). «Заказы» и «платёжные документы» живут в других сервисах и попадают в карточку лишь как производные (счётчик заказов, события). Подписка / тариф на основном методе карточки сейчас отдаётся как null (TODO)Client.cs:30-145; GetClientInfoByAccountIdResponse.cs:31; GetClientInfoByAccountIdHandler.cs:45
П6BR1.3-9Технические данные: кем создано, аккаунт, автопродление тарифа, сферы, избранноеЭта часть подтверждается: «кем создано» (created_by), «аккаунт» (account_id), автопродление (AutoRenewal / IsAutoRenewal), сферы (через client_services, тип SphereActivity), избранное (favorite_rooms) — присутствуют. Расхождение только в неполноте состава (см. П5), сами эти поля заполняютсяClient.cs:24,90; ClientTariffAggregate.cs:23; ClientServicesTypesEnum.cs:36; ClientAggregate.cs:68-69
П7BR1.3-11Сообщения хранят ограничение доступа на просмотр. Допустимые значения: только я, сотрудники, всеВ коде перечисление содержит ЧЕТЫРЕ значения, а не три: EmployeesAndMe=0, OnlyMe=1, OnlyService=2, OtherPartners=3. Сопоставление с BRD приблизительное: «сотрудники» ≈ EmployeesAndMe (включает автора и ограничено партнёрской зоной), «все» ≈ OtherPartners; чистого значения «все» нет, а значение OnlyService в BRD не описаноClientMessageAdmissionEnum.cs:5-8
П8BR1.3-11Ограничение доступа должно соблюдаться (значение «только я / сотрудники / все» определяет видимость)Фильтрация на чтении реально применяется на стороне сервера, НО обрабатывает только 3 из 4 значений: OtherPartners (видно всем), EmployeesAndMe + совпадение партнёрской зоны, OnlyMe + совпадение автора. Значение OnlyService=2 не покрыто ни одной веткой — такие сообщения не вернутся никогда (тихо невидимы). «Только я» = только автор-сотрудник (CreatedBy), а не клиентClientMessagesReadRepository.cs:25-30; GetClientMessagesHandler.cs:29-33
П9BR1.3-11Ограничение доступа на ПРОСМОТР сообщенийОграничение применяется только на чтение. На запись (редактирование / удаление / архивацию сообщения) проверки прав нет — любой авторизованный может изменить или удалить любое сообщение по его ID независимо от значения ограничения. BRD прямо про запись не говорит, но это смежный пробел в той же механикеUpdateClientMessageHandler.cs:22-27; ArchiveClientMessageHandler.cs; DeleteClientMessageHandler.cs

Рекомендация / варианты решения

  • Триггер создания карточки из виджета «при бронировании» (П1). Вариант А (рекоменд.): уточнить BRD / контракт — явно зафиксировать, что карточка из виджета создаётся отдельным вызовом стороны виджета, а не как следствие события заказа; описать, кто и когда его вызывает. Вариант Б: привести код к BRD — создавать карточку в обработчике события бронирования. Зависит от того, где в архитектуре живёт виджет-флоу.
  • Связь «способ создания → состояние карточки» (П2). Завязано на отсутствие поля состояния (см. рекомендацию по Б6/Б7). Отдельного программного решения не требует: появится состояние — SourceId можно в него отображать. Пока — описать в BRD, что SourceId фиксирует канал, но состояние из него не выводится.
  • Гейтинг телефона / адреса и перенос логина в публичное имя (П3, П4). Общая первопричина — нет понятия «после регистрации» и нет полей публичного имени и адреса. Вариант А (рекоменд.): привести код к BRD — реализовать поля и правила (после регистрации телефон / адрес правятся только через аккаунт; публичное имя = логин). Вариант Б: привести BRD к коду — если редактирование контактов через аккаунт обеспечивается разделом 1.1, перенести правило туда; пункт про публичное имя снять или перенести, раз поля нет. Решение продуктовое, затем реализация.
  • Полнота состава технических данных (П5, П6). Вариант А: привести код к BRD — добавить недостающее (уведомления / устройство / сохранённые карты или ссылки на них) и снять TODO с подписки на методе карточки. Вариант Б (рекоменд.): уточнить BRD — для данных из других сервисов (заказы, платёжные документы, сохранённые карты, устройство) описать их как «отображается из сервиса X», а не «хранится в карточке»; перечислить, что заполняется фактически (П6) и что отдаётся как null. Согласовать ожидаемый состав с продуктом.
  • Модель значений ограничения доступа к сообщениям (П7, П8). Общая первопричина — перечисление из 4 значений не совпадает с тремя из BRD, и одно значение «мертво» на чтении. Вариант А (рекоменд.): привести код к BRD — свести к трём значениям (только я / сотрудники / все), убрать или задействовать OnlyService, добавив для него ветку в фильтр чтения, чтобы не было тихо невидимых сообщений. Вариант Б: привести BRD к коду — задокументировать все четыре значения и их семантику (включая «только этот сервис» и «другие партнёры»), уточнить, что «сотрудники» ограничены партнёрской зоной. В любом варианте обязательно закрыть дыру с непокрытым OnlyService (иначе данные теряются на чтении).
  • Отсутствие проверки прав на запись сообщений (П9). Вариант А (рекоменд.): привести код к BRD по духу — добавить проверку прав на редактирование / удаление / архивацию сообщения (автор или сотрудник зоны), симметрично чтению. Вариант Б: оставить как есть и явно отметить в BRD, что ограничение распространяется только на просмотр. Так как речь о приватных заметках партнёров о клиенте, рекомендована защита записи.

Приложение. Находки обратного прохода (код сверх BRD). Ниже — поведение и данные, найденные в коде, но не упомянутые в BRD 1.3. Показаны как кандидаты на требования (бизнес-значимые); мелкий плумбинг отрезан. Отрезано находок: 4 (служебные: предвычисленный столбец полнотекстового поиска search_column; неиспользуемый в доступе SourceId; инфраструктурные настройки кэша; внутренний расчёт FillFactor дублируется двумя формулами).

Связано сЧто в коде (нет в BRD)Где в кодеКуда отнести
Д1вне BRDБаллы лояльности клиента (Points) и целая подсистема баллов / кешбэка / лимитов / штрафов на карточке клиентаClient.cs:108-109; ClientAggregate.cs:109,289бизнес-правило
Д2вне BRDПризнак «профиль заполнен» (AlreadyFilled) и процент заполненности (FillFactor, 0–100) — латентная стейт-машина профиля, защёлкивается при 100%Client.cs:117-118,143-144; ClientAggregate.cs:298-307бизнес-правило
Д3вне BRDПроверка «дата рождения не в будущем» при создании / обновлении карточкиAdditionalClientInfo.cs:13-16программное (инвариант)
Д4вне BRDАрхивация / восстановление карточки (мягкое удаление), привязка аккаунта к существующей карточке (AttachAccount), бан / разбан в рамках зоны, избранные комнаты, ресинк скидок — операции есть, в составе раздела не описаныArchiveClientCommand.cs:7; AttachAccountToClientCommand.cs:8; ChangeClientBanStatusCommand.cs:11; AddRoomToFavouriteCommand.cs:13; ResyncClientDiscountsCommand.cs:9программное
Д5вне BRDСервисные признаки клиента сверх «сфер»: должности, уровни навыка, музыкальные жанры, инструменты, танцевальные стили (типы 1–5)ClientServicesTypesEnum.cs:11-31; GetClientInfoByAccountIdResponse.cs:24-28бизнес-правило
Д6вне BRDПризнак подтверждения email (EmailVerified) подтягивается в карточку из внешнего сервиса Identity на чтенииGetClientInfoByAccountIdResponse.cs:9; GetClientInfoByAccountIdHandler.cs:31,43программное