2.7.1 Акции и промокоды

Направление работ: Back Код раздела: 2.7.1 Объекты и предложения > Скидки > Акции и промокоды Статус: черновик Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 20.07.26


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

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

Акция применяется автоматически при выполнении условий активации. Промокод применяется вручную, когда его указывают при создании заказа.

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

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

Промокод — ручная скидка, для применения которой клиент или система передает уникальный код при создании заказа.

Условия активации — параметры заказа, по которым система определяет возможность применения скидки.

Персональный промокод — промокод, разрешенный только для заданных клиентов или групп клиентов.

3. Scope

Входит

S1 Акции и промокоды: сущности, принадлежность партнерской зоне, способы применения и правила отображения.

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

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

Не входит

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

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

IDТребованиеКомментарийТип требованияВесСтатус
S1: BR2.7.1-1Акция и промокод должны действовать только в партнерской зоне, в которой они созданы.Исключение составляют сервисные промокоды из Back 8.4.1.Бизнес1Реализовано
OQ2.7.1-15
S1: BR2.7.1-2Акция должна применяться автоматически ко всем заказам, которые соответствуют ее условиям активации.Бизнес1Реализовано
OQ2.7.1-13
S1: BR2.7.1-3Промокод должен применяться только при его указании во время создания заказа.Бизнес1Реализовано
OQ2.7.1-13
S1: BR2.7.1-4Акция должна отображаться в публичных интерфейсах всем пользователям при выполнении условий активации.Бизнес2
OQ2.7.1-10
S1: BR2.7.1-5Промокод должен отображаться в публичных интерфейсах только клиенту, с которым он связан.Связь промокода с клиентом раскрывается в BR2.7.1-15.Бизнес2Реализовано
OQ2.7.1-16
S2: BR2.7.1-6Акция должна хранить название и описание.Название — текстовое значение не более 50 символов. Описание — текстовое значение.Системное1Частично
OQ2.7.1-5
S2: BR2.7.1-7Промокод должен хранить название и описание.Название состоит из 10 символов и должно быть уникальным в системе. Описание — текстовое значение.Системное1Частично
OQ2.7.1-5
S2: BR2.7.1-8Акция и промокод должны хранить область действия по объектам, площадкам и услугам.Область действия:
- объекты из Back 2.1 (если не заданы — все объекты партнерской зоны)
- площадки или услуги из Back 2.2 (если не заданы — все площадки и услуги выбранного объекта)
Системное1Частично
OQ2.7.1-6
S2: BR2.7.1-9Акция и промокод должны хранить размер скидки.Допустимые варианты:
- размер скидки в процентах (не менее 5%)
- размер скидки в рублях (не менее 100 рублей)

Размер задается ровно одним способом: в процентах или рублях.
Системное1Частично
OQ2.7.1-4
S2: BR2.7.1-10Акция и промокод могут хранить лимиты использования скидки.Доступные лимиты:
- максимальное число завершенных заказов, для которых действует скидка
- максимальное число завершенных заказов со скидкой для одного клиента
Бизнес2Частично
OQ2.7.1-7
S2: BR2.7.1-11Акция и промокод должны хранить условия применения по параметрам заказа.Условия применения:
- минимальная длительность заказа (не менее 1 часа; значение по умолчанию — 1 час)
- опции из Back 8.2.1 (если не заданы — все опции)
- диапазон опции: число или диапазон «X-X» (если не задан — любой диапазон)
- источники из Back 8.2.2 (если не заданы — все источники)
Системное1Частично
OQ2.7.1-3
S2: BR2.7.1-12Акция и промокод должны хранить временные условия применения.Временные условия:
- дни недели, множественный выбор из понедельника-воскресенья (если не заданы — все дни)
- один или несколько интервалов времени (если не заданы — весь день)
- признак «День в день»: дата создания заказа совпадает с датой его проведения (если не задан — ограничение не применяется)
- календарный период действия (если не задан — ограничение не применяется)
Системное1Реализовано
OQ2.7.1-14
S2: BR2.7.1-13Акция должна хранить количество часов до начала заказа, после которого активируется скидка.Минимальное значение — 7 часов.Системное1Не реализовано
OQ2.7.1-1
S2: BR2.7.1-14Промокод должен хранить количество часов до начала заказа, после которого активируется скидка.Минимальное значение — 1 час.Системное1Частично
OQ2.7.1-2
S2: BR2.7.1-15Промокод может быть ограничен списком клиентов или групп клиентов.Если задан хотя бы один разрешенный клиент или группа, промокод считается персональным и доступен только связанным с ним клиентам. Карточки и группы клиентов раскрываются в Back 1.3.Бизнес2Реализовано
OQ2.7.1-11, OQ2.7.1-16
S2: BR2.7.1-16Для акции и промокода должны отдельно настраиваться правила применения скидки к дополнительным позициям заказа.Доступные настройки:
- распространяется ли скидка на дополнительные позиции
- действует ли скидка только на дополнительные позиции
- список конкретных позиций партнерской зоны из Back 2.5
Бизнес2Частично
OQ2.7.1-9
S3: BR2.7.1-17Система не должна хранить полностью идентичные акции или полностью идентичные промокоды.Дубль определяется по полному совпадению параметров сущности.Системное1Частично
OQ2.7.1-8

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

Сверка с кодом: BRD 2.7.1 v0.1 · репозиторий booking-monorepo · commit 83423b21 / main · 26.07.26 · якорь src/services/booking.discounts Сводка: 16 вопросов (Open 12 · Подтверждено 4) Маршрутизация без сверки с гайдбуком (гайдбук не передавался).

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
OQ2.7.1-1Код BR2.7.1-13Требование говорит: акция хранит количество часов до начала заказа, после которого активируется скидка (минимум 7 часов). В коде у акции такого поля нет вовсе — ни в сущности, ни в таблице, ни в параметрах создания. Условие «за сколько часов до начала» для акций не существует ни как хранимое значение, ни как проверка при подборе. У промокода поле есть (см. OQ2.7.1-2), у акции — нет. \ AutoDiscountAggregate.cs:30-67 (состав полей), CreateAutoDiscountCommand.csБизнесOpen(А) Подтвердить требование (рекоменд.) — тогда это доработка: добавить параметр акции и учитывать его при подборе. (Б) Снять BR2.7.1-13, если правило перенесено на промокоды. Требование помечено весом 1, а функциональности нет целиком — стоит решить до планирования работ по скидкам. Ответ логично дать вместе с OQ2.7.1-2: у двух видов скидок правило описано одинаково, а в коде реализовано по-разному.
OQ2.7.1-2Код BR2.7.1-14Требование говорит: промокод хранит количество часов до начала заказа, после которого активируется скидка (минимум 1 час). В коде поле есть и сохраняется, но при применении промокода не используется: контекст, по которому считается скидка, этого параметра не содержит вовсе, и ни одна проверка на него не ссылается. Минимальное значение тоже не проверяется — можно сохранить 0 или отрицательное число. Практически: промокод применится к заказу, который начинается через 10 минут, даже если задано «не позднее чем за 5 часов». \ ManualDiscountAggregate.cs:80 и :357 (хранение без проверки), ManualPromoContextDto.cs (параметра нет в контексте расчёта), GetDiscountsForOrderMultiQuery.cs:131-215 (проверки длительности, времени и лимитов есть, проверки часов до начала нет)РазработкаOpenЗавести тикет в Bitrix: включить «часов до начала» в контекст расчёта и проверять его при подборе промокода, а при сохранении отклонять значения меньше 1. Продуктового выбора нет: BR2.7.1-14 однозначен, параметр уже настраивается партнёром и сейчас молча не работает — это хуже, чем его отсутствие.
OQ2.7.1-3Код BR2.7.1-11Требование говорит: акция и промокод хранят условие применения по источникам заказа. В коде источники есть только у промокода, и они тоже не применяются: список хранится у сущности и в базе, но в контекст расчёта не попадает и при подборе не проверяется. У акции источников нет ни в каком виде. Остальные условия применения из BR2.7.1-11 работают: минимальная длительность проверяется, опции и их диапазоны — тоже (подбор отсекает скидку, если опция заказа не входит в список или не попадает в диапазон). \ ManualDiscountAggregate.cs:78-79 (хранение источников), ManualPromoContextDto.cs (источников нет в контексте); DiscountCalculationReadRepository.cs:226-236 и :424 (опции и диапазоны работают), GetDiscountsForOrderMultiQuery.cs:135 (длительность)РазработкаOpenЗавести тикет в Bitrix: либо довести источники до расчёта у промокода и добавить их акции, либо убрать неработающее поле, чтобы оно не вводило партнёра в заблуждение. Перед правкой стоит подтвердить у бизнеса, нужны ли источники у акции (в BRD они описаны для обоих видов) — это часть того же решения, что и OQ2.5-12 по позициям.
OQ2.7.1-4Код BR2.7.1-9Требование говорит: размер скидки — не менее 5% или не менее 100 рублей, и задаётся ровно одним способом. В коде ни один из трёх пунктов не обеспечен: величина скидки хранится парой «число + признак процента» без каких-либо проверок, пороги 5% и 100 ₽ нигде не встречаются, а «ровно один способ» невозможно проверить в принципе, потому что у скидки две независимые величины — на время и на позиции, и каждая имеет собственный признак процента. Например, допустимо задать 3% на время и 50 ₽ на позиции одновременно. \ AmountOrPercent.cs:11-18 (нет проверок), AutoDiscountAggregate.cs:33-34 и ManualDiscountAggregate.cs:109-110 (две величины)БизнесOpen(А) Подтвердить пороги и правило «ровно один способ» (рекоменд.) — тогда нужно уточнить, как оно ложится на две составляющие: относится ли «ровно один способ» к каждой величине по отдельности или к скидке целиком (например, «нельзя смешивать проценты и рубли в одной скидке»). После ответа — тикет на валидацию при сохранении. (Б) Если ограничений нет — снять их из BR2.7.1-9 и описать модель «на время + на позиции» явно.
OQ2.7.1-5Код BR2.7.1-6, BR2.7.1-7Требование говорит: название акции — не более 50 символов, название промокода — ровно 10 символов. В коде длина не проверяется нигде: ни при создании, ни при изменении, ни в доменной модели — название и описание сохраняются как есть. Уникальность при этом работает и ровно так, как описано в BRD: у промокода название проверяется по всей системе, у акции — в пределах партнёрской зоны. \ Info.cs:14-18 (без проверок), CreateAutoDiscountCommandHandler.cs:52-53 и AutoDiscountRepository.cs:119-122 (уникальность в зоне), ManualDiscountRepository.cs:129-134 (уникальность по всей системе)РазработкаOpenЗавести тикет в Bitrix: добавить проверку длины названия при сохранении — 50 символов для акции, 10 для промокода. Продуктового выбора нет. Уточнить у бизнеса стоит только одно: 10 символов промокода — это строго «ровно 10» или «не более 10»; формулировка BRD допускает оба чтения.
OQ2.7.1-6Код BR2.7.1-8Требование говорит: область действия скидки задаётся по объектам, площадкам и услугам. В коде услуг в области действия нет — она состоит только из объектов и площадок. Часть про объекты и площадки работает, включая правило «не задано — значит всё»: при подборе скидка проходит, если совпал объект или площадка. Ровно та же нехватка услуг зафиксирована по дополнительным позициям (OQ2.5-7) — похоже, это общее свойство модели области действия, а не частный пропуск в скидках. \ AutoDiscountAggregate.cs:44-56 и ManualDiscountAggregate.cs:51-63 (только объекты и площадки)БизнесOpen(А) Подтвердить требование (рекоменд., если скидки действительно должны привязываться к отдельным услугам) — это доработка общей модели области действия, и решать её нужно вместе с OQ2.5-7, а не отдельно по скидкам. (Б) Снять услуги из BR2.7.1-8, если достаточно объекта и площадки.
OQ2.7.1-7Код BR2.7.1-10Требование говорит: лимиты считаются по числу завершённых заказов — общему и на одного клиента. В коде лимиты есть и работают, но считают они не завершённые заказы, а применения скидки: счётчик увеличивается в момент, когда скидка закрепляется за заказом, и уменьшается, когда применение снимается (отмена или неудачное создание заказа). Завершение заказа на счётчик не влияет никак. Практическая разница: заказ создан со скидкой и висит неоплаченным — лимит уже израсходован; заказ отменён — лимит вернулся. По умолчанию оба лимита не ограничены. \ AutoDiscountAggregate.cs:184-223 (расход), :225-278 (возврат), Settings.cs:8-11 (без ограничения по умолчанию), OrderCreationFailedDiscountEventConsumer.csБизнесOpen(А) Признать текущую модель целевой (рекоменд.) и заменить в BR2.7.1-10 «завершённых заказов» на «заказов, к которым применена скидка» — считать по завершению означало бы держать лимит незанятым до оплаты и допускать перерасход. (Б) Если лимит должен расходоваться именно по завершению — это доработка со сменой момента списания; тогда нужно отдельно решить, что делать с уже созданными, но не завершёнными заказами.
OQ2.7.1-8Код BR2.7.1-17Требование говорит: система не должна хранить полностью идентичные акции или полностью идентичные промокоды; дубль определяется по полному совпадению параметров. В коде проверка есть только у акций и она другая по смыслу — не «полное совпадение», а «пересечение»: при создании акция отклоняется, если находится уже существующая, пересекающаяся по датам, интервалам времени, клиентам, позициям и области действия, — то есть отклоняются и просто похожие, не идентичные акции. У промокодов проверки дублей нет вовсе: контролируется только уникальность названия, поэтому два промокода с разными кодами и полностью совпадающими параметрами создаются свободно. \ CreateAutoDiscountCommandHandler.cs:63-77 (проверка пересечения), ManualDiscountRepository.cs:129-134 (только имя)БизнесOpen(А) Привести BRD к коду для акций (рекоменд.): описать реальное правило — запрет пересекающихся акций, а не идентичных; правило строже и осмысленнее, но сейчас документ обещает другое, и партнёр получает отказ там, где по BRD дубля нет. (Б) Отдельно решить, нужен ли запрет дублей промокодам: у них уникальный код по всей системе, и полный дубль с другим кодом может быть вполне легитимным сценарием (две рассылки с одинаковыми условиями).
OQ2.7.1-9Код BR2.7.1-16Требование говорит: для акции и промокода отдельно настраиваются правила применения скидки к дополнительным позициям — распространяется ли скидка на позиции, действует ли только на них и список конкретных позиций партнёрской зоны. В коде первые две настройки выражены косвенно, через наличие величин: задана величина «на позиции» — скидка на позиции действует, не задана величина «на время» — действует только на позиции. Отдельных признаков нет, но поведение достижимо. Списка конкретных позиций у скидки нет — привязка к дополнительным позициям из Back 2.5 отсутствует; список «опций», который есть у скидки, — это справочник опций из Back 8.2.1, другая сущность. \ AutoDiscountAggregate.cs:33-34, GetDiscountsForOrderMultiQuery.cs:165-178 (обе величины независимо), состав полей — без позицийБизнесOpen(А) Подтвердить требование про список позиций (рекоменд., если партнёр должен выбирать, на какие именно позиции даётся скидка) — это доработка. (Б) Снять третий пункт BR2.7.1-16, если достаточно «на все позиции / только на позиции». В любом случае в BRD стоит развести термины «опция» и «дополнительная позиция»: сейчас их легко перепутать, а в коде это разные сущности с разным поведением.
OQ2.7.1-10Код BR2.7.1-4Что нашли: требование «акция отображается в публичных интерфейсах всем пользователям при выполнении условий активации» в сервисе скидок подтвердить нечем. Публичные запросы списка есть только у промокодов — для виджета и мобильного приложения; у акций публичного запроса списка нет, все запросы акций предназначены кабинету партнёра. Косвенно акция всё же попадает в публичные интерфейсы — через расчёт стоимости, где она возвращается вместе с ценой, — но это не «отображение акции» в смысле требования. \ GetPromocodesWidgetQuery.cs и GetMyPromocodesQuery.cs (публичные, только промокоды), GetAutoDiscountsQuery.cs (кабинет), GetDiscountsForOrderMultiQuery.cs (акции в расчёте цены)РазработкаOpenУточнить у разработки, где реализовано публичное отображение акций: если оно живёт в кабинете партнёра, виджете или мобильном приложении и собирается из расчёта цены, то в BRD 2.7.1 нужно указать, что бэк отдаёт акции только через расчёт, а вопрос отображения относится к клиентским разделам. Если публичного отображения нет вовсе — вернуть вопрос бизнесу как нереализованное требование веса 2.
OQ2.7.1-11Код вне BRDЧто нашли: ограничение по клиентам и группам клиентов есть не только у промокода, но и у акции — у акции хранятся те же списки разрешённых клиентов и групп, и подбор их учитывает. В BRD персональной названа только ручная скидка (BR2.7.1-15), про акцию сказано, что она применяется автоматически ко всем подходящим заказам (BR2.7.1-2), — то есть в документе такой возможности нет. \ AutoDiscountAggregate.cs:57-67 (клиенты и группы у акции), :339-340 (изменение списков), DiscountCalculationReadRepository.cs (учёт при подборе)БизнесOpen(А) Признать поведение целевым (рекоменд.) и внести в BRD: акция тоже может быть ограничена клиентами и группами; тогда BR2.7.1-2 нужно смягчить — «ко всем подходящим заказам» верно только для акций без списка клиентов. (Б) Если персональные акции не задумывались — ставить задачу на удаление возможности, но сначала проверьте по базе, не пользуются ли ей партнёры.
OQ2.7.1-12Код вне BRDПроблема: три мелких дефекта вокруг параметров скидки, каждый сам по себе безобиден, вместе — мина при доработках. (1) У скидки есть параметр «минимальное количество заказов», который хранится, отдаётся во все списки и события, но нигде не проверяется — мёртвый параметр. (2) У настроек скидки два разных значения по умолчанию для минимальной длительности заказа: конструктор подставляет «бесконечность», а создание скидки — 1 час; если настройки где-то создадут без явного значения, скидка не применится никогда. (3) В сервисе лежит пустой файл-заглушка класса Prefix с комментарием автора «удалить, если больше не используется». \ Settings.cs:13 и :20-21 (мёртвый параметр и расхождение умолчаний) против AutoDiscountAggregate.cs:405-406; Prefix.cs:1РазработкаOpenЗавести тикет в Bitrix на техуборку: удалить мёртвый параметр «минимальное количество заказов» либо задействовать его; свести умолчания минимальной длительности к одному значению; удалить файл-заглушку. Продуктового выбора нет, поведение не меняется.
OQ2.7.1-13Код BR2.7.1-2, BR2.7.1-3Что подтверждено: способы применения разведены так, как описано в BRD. Акции подбираются автоматически: система сама находит все подходящие заказу акции и применяет их, кода от пользователя не требуется. Промокод участвует в расчёте только тогда, когда его код передали в запросе, и при неподходящих условиях возвращает понятную причину отказа — «промокод не действует в выбранное время», «превышен лимит использования», «минимальная длительность заказа N ч». \ GetDiscountsForOrderMultiQuery.cs:240-290 (автоподбор акций), :120-215 (промокод по коду с причинами отказа)ПодтвержденоРасхождения нет.
OQ2.7.1-14Код BR2.7.1-12Что подтверждено: все четыре временных условия хранятся и применяются — дни недели и интервалы времени (одним списком «интервал + дни недели», повторы в списке запрещены), признак «День в день» и календарный период действия. При подборе интервалы скидки накладываются на интервал заказа, и скидка действует только на пересечении. \ AutoDiscountAggregate.cs:35-36, Settings.cs:14-15, PromoDateRangeMapper.MapPromoTemplateToDateRanges — вызовы в GetDiscountsForOrderMultiQuery.cs:143-154ПодтвержденоРасхождения нет.
OQ2.7.1-15Код BR2.7.1-1Что подтверждено: привязка к партнёрской зоне обеспечена — область действия скидки всегда строится через иерархию партнёрской зоны, а выборки и проверки прав идут по ней же. Исключение для сервисных промокодов из Back 8.4.1 поддержано: у промокода зон может быть несколько, и есть признак «партнёрский / сервисный», тогда как у акции зона ровно одна. \ AutoDiscountAggregate.cs:43 (одна зона), ManualDiscountAggregate.cs:46-50 (несколько зон), :77 (признак партнёрского), PartnerZoneScopeService.csПодтвержденоРасхождения нет.
OQ2.7.1-16Код BR2.7.1-5, BR2.7.1-15Что подтверждено: персональные промокоды работают как описано. Списки разрешённых клиентов и групп хранятся у промокода, а видимость фильтруется по клиенту: в личном списке клиент видит только промокоды, где он указан, а в общих списках — промокоды без ограничения по клиентам или те, где он указан. Дополнительно из выдачи убираются промокоды, у которых клиент уже исчерпал свой лимит. \ ManualDiscountAggregate.cs:64-71 (списки), ManualPromocodeReadOnlyRepository.cs:42-44 (личный список), :163 и :274-285 (общие списки и лимит клиента)ПодтвержденоРасхождения нет.

0 items under this folder.