Skip to content

Deactivation payment/notifications#69

Merged
matt2jog merged 8 commits into
mainfrom
deactivation-payment/notifications
May 8, 2026
Merged

Deactivation payment/notifications#69
matt2jog merged 8 commits into
mainfrom
deactivation-payment/notifications

Conversation

@talbarr14

@talbarr14 talbarr14 commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Subscriptions and payments are now canceled when accounts are deactivated.
  • Bug Fixes

    • Payment refresh now processes only active subscriptions.
  • Improvements

    • Updated deactivation notifications to reflect subscription cancellation status.
    • Enhanced availability time validation with improved constraint handling.
  • Tests

    • Added test coverage for subscription cancellation during account deactivation.
    • Added test coverage for payment refresh with canceled subscriptions.

@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@matt2jog has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 11 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72f5f2df-c03e-4c7d-876b-9ad9f73755db

📥 Commits

Reviewing files that changed from the base of the PR and between 55b61d7 and f8343ac.

📒 Files selected for processing (4)
  • src/api/app.py
  • src/api/roles/shared/account.py
  • src/database/account/models.py
  • tests/test_shared_account_notifications.py
📝 Walkthrough

Walkthrough

This PR adds subscription cancellation when client–coach relationships are deleted and updates the refresh_payments endpoint to skip canceled subscriptions. Deactivation notifications now report stopped future payments. Tests verify cancellation behavior and endpoint filtering.

Changes

Subscription Cancellation on Relationship Deletion and Refresh Filtering

Layer / File(s) Summary
Data Model Validation
src/database/account/models.py
Availability validator now returns early on missing time fields, enforces start < end ordering, and validates recurrence_end_dt constraints. Notification created_at field retains existing default factory.
Payment Cancellation Logic
src/api/roles/shared/account.py
New cancel_payments_for_request helper cancels all active subscriptions for a client–coach request, sets canceled timestamps, and deactivates billing cycles. Client and coach relationship deletion both invoke this before removing requests. Deactivation notifications updated to report "future payments" being "stopped". Imports cleaned up and date added for timestamps.
Refresh Endpoint Filtering
src/api/app.py
/refresh_payments now imports SubscriptionStatus and filters to only process subscriptions with ACTIVE status, skipping canceled subscriptions in billing-cycle refresh.
Tests & Fixtures
tests/test_shared_account_notifications.py
Test helper create_client_coach_relationship now accepts with_subscription flag to optionally create pricing plan, subscription, and billing cycle. New tests verify deactivation cancels subscriptions and billing cycles, and refresh_payments skips canceled subscriptions. Existing and new notification assertions expect "future payments"/"stopped" language.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • matt2jog

Poem

🐰 Hop, hop! The subscriptions now sing a final song,
When coaches and clients part ways and move along.
The payments are canceled, the cycles at rest,
And refresh skips over the canceled ones best!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Deactivation payment/notifications' accurately reflects the main changes: deactivation logic now handles payment cancellation and updates notification messages across multiple modules.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch deactivation-payment/notifications

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/api/roles/shared/account.py (1)

410-447: 💤 Low value

Minor: cancel and side-effects flow could be tightened.

Both branches (client-side and coach-side) duplicate the same shape: fetch requests → cancel_payments_for_request → delete relationships → delete request. Extracting the inner per-request loop into a small helper would remove the duplication without changing behavior. Optional only; current code is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/roles/shared/account.py` around lines 410 - 447, The
delete_client_coach_mappings function duplicates logic for handling each
ClientCoachRequest; create a small helper (e.g.,
process_client_coach_request(db, request)) that takes the Session and a
ClientCoachRequest, calls cancel_payments_for_request(db, request), deletes all
ClientCoachRelationship rows for request.id, and then deletes the request;
replace both per-request for-loops in delete_client_coach_mappings with calls to
this helper to remove duplication while preserving behavior.
tests/test_shared_account_notifications.py (1)

250-254: ⚡ Quick win

Use monkeypatch for CRON_SECRET to avoid leaking env state across tests.

os.environ["CRON_SECRET"] = "test-cron-secret" mutates the process environment for the rest of the pytest run, which can affect other tests that rely on CRON_SECRET being unset (e.g., the 500 path in /refresh_payments). Pytest's built-in monkeypatch fixture cleans up automatically.

♻️ Proposed fix using monkeypatch
 def test_refresh_payments_skips_canceled_subscription(
     test_client,
     db_session,
     client_auth_header,
+    monkeypatch,
 ):
     ...
-    os.environ["CRON_SECRET"] = "test-cron-secret"
+    monkeypatch.setenv("CRON_SECRET", "test-cron-secret")
     resp = test_client.post(
         "/refresh_payments",
         json={"cron_secret": "test-cron-secret"},
     )

With this change, the import os added on line 3 is no longer needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_shared_account_notifications.py` around lines 250 - 254, Replace
the direct environment mutation os.environ["CRON_SECRET"] = "test-cron-secret"
with pytest's monkeypatch: add the monkeypatch fixture to the test signature and
call monkeypatch.setenv("CRON_SECRET", "test-cron-secret") before invoking
test_client.post("/refresh_payments", ...); also remove the now-unnecessary
import os if it's unused elsewhere in the file so the test does not leak
CRON_SECRET to other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/api/roles/shared/account.py`:
- Around line 364-366: The notification `details` string currently set to
"Subscription canceled" must be updated to include both the substrings "future
payments" and "stopped" (case-insensitive) to satisfy the messaging contract and
tests; locate where `message` and `details` are set (the variables `message` and
`details` in src/api/roles/shared/account.py) and replace the fixed details text
with a phrase such as "Future payments tied to this relationship have been
stopped." so that the `test_account_deactivate_sends_notification` and
`test_account_deactivate_coach_notifies_client` assertions pass.

---

Nitpick comments:
In `@src/api/roles/shared/account.py`:
- Around line 410-447: The delete_client_coach_mappings function duplicates
logic for handling each ClientCoachRequest; create a small helper (e.g.,
process_client_coach_request(db, request)) that takes the Session and a
ClientCoachRequest, calls cancel_payments_for_request(db, request), deletes all
ClientCoachRelationship rows for request.id, and then deletes the request;
replace both per-request for-loops in delete_client_coach_mappings with calls to
this helper to remove duplication while preserving behavior.

In `@tests/test_shared_account_notifications.py`:
- Around line 250-254: Replace the direct environment mutation
os.environ["CRON_SECRET"] = "test-cron-secret" with pytest's monkeypatch: add
the monkeypatch fixture to the test signature and call
monkeypatch.setenv("CRON_SECRET", "test-cron-secret") before invoking
test_client.post("/refresh_payments", ...); also remove the now-unnecessary
import os if it's unused elsewhere in the file so the test does not leak
CRON_SECRET to other tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1165a96-0c52-4a9a-8110-7db81a4797d5

📥 Commits

Reviewing files that changed from the base of the PR and between 1f0b43d and 55b61d7.

📒 Files selected for processing (4)
  • src/api/app.py
  • src/api/roles/shared/account.py
  • src/database/account/models.py
  • tests/test_shared_account_notifications.py

Comment on lines 364 to 366
message = f"{deactivated_account.name} has deactivated their account."
details = f"{role.capitalize()} account {deactivated_account.id} was deactivated."
details = "Subscription canceled"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: notification details does not match the deactivation messaging contract — failing CI.

The fixed string "Subscription canceled" doesn't contain "future payments" or "stopped", but test_account_deactivate_sends_notification (line 157) and test_account_deactivate_coach_notifies_client (line 295) both assert that details includes both substrings (case-insensitive). This is the root cause of the pr-run-tests-prerequisite / test pipeline failures. The intent (per the AI summary and tests) is for affected users to be informed that future payments tied to the relationship have been stopped, which the current string fails to convey.

🐛 Proposed fix to align details with the messaging contract
-    details = "Subscription canceled"
+    details = (
+        "Any future payments tied to this coaching relationship have been stopped."
+    )

This satisfies both "future payments" and "stopped" token assertions and reads naturally to the affected client/coach.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message = f"{deactivated_account.name} has deactivated their account."
details = f"{role.capitalize()} account {deactivated_account.id} was deactivated."
details = "Subscription canceled"
message = f"{deactivated_account.name} has deactivated their account."
details = (
"Any future payments tied to this coaching relationship have been stopped."
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/roles/shared/account.py` around lines 364 - 366, The notification
`details` string currently set to "Subscription canceled" must be updated to
include both the substrings "future payments" and "stopped" (case-insensitive)
to satisfy the messaging contract and tests; locate where `message` and
`details` are set (the variables `message` and `details` in
src/api/roles/shared/account.py) and replace the fixed details text with a
phrase such as "Future payments tied to this relationship have been stopped." so
that the `test_account_deactivate_sends_notification` and
`test_account_deactivate_coach_notifies_client` assertions pass.

@matt2jog matt2jog merged commit b1cf6b2 into main May 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants