P1: Тариф «Квартал» активируется примерно на 1 месяц вместо 3 #52

Closed
opened 2026-07-21 00:21:32 +03:00 by cursor-ai · 5 comments
Owner

Проблема

При выборе тарифа «Квартал» на stage в UI даты подписки показывают период ~1 месяц (например 20.07–19.08), а не ~3 месяца.

Замечено в browser QA (smoke-user, страница Подписка). Годовой тариф при этом дал разумный горизонт (~до июля следующего года).

Влияние

Пользователь платит/активирует квартал, но получает месячный срок — риск доверия и биллинга (даже в демо-оплате).

Ожидаемый результат

План quarter/квартал даёт ends_at ≈ start + 3 месяца (согласованно с plan_to_months / логикой подписки).

Критерии приёмки

  • Активация quarterly → период ≈ 3 месяца
  • Месяц / полгода / год также соответствуют ожидаемым длительностям
  • Покрыто тестом на plan_to_months / создание подписки

Файлы (подсказка)

  • src/logic/logic_subscription.erl (plan_price / plan_to_months)

Приоритет

P1

## Проблема При выборе тарифа «Квартал» на stage в UI даты подписки показывают период ~1 месяц (например 20.07–19.08), а не ~3 месяца. Замечено в browser QA (smoke-user, страница Подписка). Годовой тариф при этом дал разумный горизонт (~до июля следующего года). ## Влияние Пользователь платит/активирует квартал, но получает месячный срок — риск доверия и биллинга (даже в демо-оплате). ## Ожидаемый результат План quarter/квартал даёт `ends_at` ≈ start + 3 месяца (согласованно с `plan_to_months` / логикой подписки). ## Критерии приёмки - [ ] Активация quarterly → период ≈ 3 месяца - [ ] Месяц / полгода / год также соответствуют ожидаемым длительностям - [ ] Покрыто тестом на `plan_to_months` / создание подписки ## Файлы (подсказка) - `src/logic/logic_subscription.erl` (`plan_price` / `plan_to_months`) ## Приоритет P1
cursor-ai added the Bug label 2026-07-21 00:21:32 +03:00
cursor-ai self-assigned this 2026-07-21 19:41:43 +03:00
Author
Owner

Беру задачу в работу из Cursor.

Сначала предложу варианты решения с trade-offs; код не меняю до вашего выбора варианта.

Беру задачу в работу из Cursor. Сначала предложу варианты решения с trade-offs; код не меняю до вашего выбора варианта.
Author
Owner

Анализ

plan_to_months(quarterly) -> 3 в core_subscription корректен. Баг в ветвлении длительности в core_subscription:create/3: параметр TrialUsed одновременно пишет флаг в запись и выбирает срок:

  • TrialUsed = true → plan_to_months(Plan);
  • TrialUsed = false → всегда +30 дней (?TRIAL_DAYS), игнорируя Plan.

В logic_subscription:activate_subscription/3: TrialUsed = length(AllSubs) > 0. Первая платная активация без предыдущих подписок получает alse → ~1 месяц даже для quarterly/annual. Если раньше был trial — срок считается по плану (поэтому годовая могла «работать»).

Варианты решения

Вариант 1 (рекомендуемый)

Разделить флаг rial_used и расчёт срока: срок всегда от Plan (plan_to_months / trial days), rial_used только метаданные. Поправить create/3 и вызовы (ctivate_subscription, start_trial). Тесты на monthly/quarterly/biannual/annual/trial.

Плюсы: устраняет корневую путаницу семантики; все планы корректны.
Минусы: чуть шире дифф (create + callers).

Вариант 2

Минимальный хак: в ctivate_subscription для платных планов всегда передавать TrialUsed = true в create/3 (флаг rial_used в записи выставлять отдельно по истории).

Плюсы: маленький дифф.
Минусы: сохраняет опасную семантику TrialUsed в create/3; легко сломать снова.

Вариант 3

В create/3 ветвиться по Plan =:= trial, а не по boolean.

Плюсы: простой и понятный критерий.
Минусы: нужно аккуратно не смешать с полем rial_used в записи.

Жду выбор варианта перед правками кода.

## Анализ plan_to_months(quarterly) -> 3 в core_subscription корректен. Баг в ветвлении длительности в core_subscription:create/3: параметр TrialUsed одновременно пишет флаг в запись и выбирает срок: - TrialUsed = true → plan_to_months(Plan); - TrialUsed = false → всегда +30 дней (?TRIAL_DAYS), **игнорируя Plan**. В logic_subscription:activate_subscription/3: TrialUsed = length(AllSubs) > 0. Первая платная активация без предыдущих подписок получает alse → ~1 месяц даже для quarterly/annual. Если раньше был trial — срок считается по плану (поэтому годовая могла «работать»). ## Варианты решения ### Вариант 1 (рекомендуемый) Разделить флаг rial_used и расчёт срока: срок всегда от Plan (plan_to_months / trial days), rial_used только метаданные. Поправить create/3 и вызовы (ctivate_subscription, start_trial). Тесты на monthly/quarterly/biannual/annual/trial. **Плюсы:** устраняет корневую путаницу семантики; все планы корректны. **Минусы:** чуть шире дифф (create + callers). ### Вариант 2 Минимальный хак: в ctivate_subscription для платных планов всегда передавать TrialUsed = true в create/3 (флаг rial_used в записи выставлять отдельно по истории). **Плюсы:** маленький дифф. **Минусы:** сохраняет опасную семантику TrialUsed в create/3; легко сломать снова. ### Вариант 3 В create/3 ветвиться по Plan =:= trial, а не по boolean. **Плюсы:** простой и понятный критерий. **Минусы:** нужно аккуратно не смешать с полем rial_used в записи. Жду выбор варианта перед правками кода.
Author
Owner

Реализую утверждённый вариант 1: отделяю флаг rial_used от расчёта длительности — длительность всегда из Plan через plan_to_months; правлю create и вызывающих; добавляю тесты для month/quarter/half_year/year.

Реализую утверждённый вариант 1: отделяю флаг rial_used от расчёта длительности — длительность всегда из Plan через plan_to_months; правлю create и вызывающих; добавляю тесты для month/quarter/half_year/year.
Author
Owner

Сделано (вариант 1)

В core_subscription:create/3 длительность больше не зависит от rial_used: всегда plan_to_months(Plan) → dd_months. Флаг rial_used только метаданные. В ctivate_subscription уточнены комментарии (логика флага прежняя).

Файлы

  • src/core/core_subscription.erl — create: duration from plan
  • src/logic/logic_subscription.erl — комментарии к callers
  • est/unit/core_subscription_tests.erl — monthly/quarterly/biannual/annual
  • est/unit/logic_subscription_tests.erl — activate duration для тех же планов

Тесты

WSL:

  • make eunit-module MODULE=core_subscription_tests — All 10 passed
  • make eunit-module MODULE=logic_subscription_tests — All 17 passed

Issue оставляю открытой до подтверждения коммита/закрытия.

## Сделано (вариант 1) В core_subscription:create/3 длительность больше не зависит от rial_used: всегда plan_to_months(Plan) → dd_months. Флаг rial_used только метаданные. В ctivate_subscription уточнены комментарии (логика флага прежняя). ### Файлы - src/core/core_subscription.erl — create: duration from plan - src/logic/logic_subscription.erl — комментарии к callers - est/unit/core_subscription_tests.erl — monthly/quarterly/biannual/annual - est/unit/logic_subscription_tests.erl — activate duration для тех же планов ### Тесты WSL: - make eunit-module MODULE=core_subscription_tests — **All 10 passed** - make eunit-module MODULE=logic_subscription_tests — **All 17 passed** Issue оставляю открытой до подтверждения коммита/закрытия.
Author
Owner

Зафиксировано в 5ac2321: длительность подписки всегда из plan_to_months; trial_used только флаг. Тесты monthly/quarterly/biannual/annual.

Зафиксировано в `5ac2321`: длительность подписки всегда из `plan_to_months`; `trial_used` только флаг. Тесты monthly/quarterly/biannual/annual.
Sign in to join this conversation.