Exclude already-paid players from first-payment blast audience #482

Open
opened 2026-04-14 03:17:16 +00:00 by forgejo_admin · 0 comments
Contributor

Type

Feature

Lineage

Discovered during Apr 13 re-blast. Querenne Nyamuhebe had already paid her April fee (Order #25, paid). She was accidentally re-emailed because the blast query didn't filter on existing paid orders.

Repo

forgejo_admin/basketball-api

User Story

As an admin running a first-payment blast
I want the audience to automatically exclude players who already paid for the current monthly period
So that parents who already paid don't get confusing "pay now" emails

Context

POST /admin/email/first-payment selects Player.contract_status == signed AND Player.contract_token IS NOT NULL. No filter for existing paid orders. When a paid player re-clicks, checkout correctly returns 409 (no double-charge), but the email shouldn't have been sent in the first place.

File Targets

  • src/basketball_api/routes/admin.pyadmin_send_first_payment() blast query (~line 990). Add a NOT EXISTS clause excluding players with any paid Order joined to a monthly-category Product.

Files to NOT touch:

  • src/basketball_api/services/email.py
  • src/basketball_api/routes/checkout.py

Acceptance Criteria

  • Blast query excludes players with any paid order in ProductCategory.monthly
  • Players with only pending/canceled/no orders are still included
  • test_email filter still works
  • Response sent_count reflects filtered audience

Test Expectations

  • Test: player with paid monthly order excluded
  • Test: player with canceled order included
  • Test: player with pending order included
  • Run: pytest tests/test_first_payment_blast.py -v

Constraints

  • Match existing SQLAlchemy patterns in admin.py
  • Prefer explicit EXISTS subquery for readability
  • Scope to current active monthly product; revisit for multi-month billing

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • Verified against prod: re-running Apr blast would skip Querenne
  • westside-basketball — project this affects
  • basketball-api#479 — related amount-match fix
  • Companion ticket: friendlier 409 for already-paid players
### Type Feature ### Lineage Discovered during Apr 13 re-blast. Querenne Nyamuhebe had already paid her April fee (Order #25, paid). She was accidentally re-emailed because the blast query didn't filter on existing paid orders. ### Repo `forgejo_admin/basketball-api` ### User Story As an admin running a first-payment blast I want the audience to automatically exclude players who already paid for the current monthly period So that parents who already paid don't get confusing "pay now" emails ### Context `POST /admin/email/first-payment` selects `Player.contract_status == signed AND Player.contract_token IS NOT NULL`. No filter for existing paid orders. When a paid player re-clicks, checkout correctly returns 409 (no double-charge), but the email shouldn't have been sent in the first place. ### File Targets - `src/basketball_api/routes/admin.py` — `admin_send_first_payment()` blast query (~line 990). Add a NOT EXISTS clause excluding players with any paid Order joined to a monthly-category Product. Files to NOT touch: - `src/basketball_api/services/email.py` - `src/basketball_api/routes/checkout.py` ### Acceptance Criteria - [ ] Blast query excludes players with any `paid` order in `ProductCategory.monthly` - [ ] Players with only pending/canceled/no orders are still included - [ ] `test_email` filter still works - [ ] Response `sent_count` reflects filtered audience ### Test Expectations - Test: player with paid monthly order excluded - Test: player with canceled order included - Test: player with pending order included - Run: `pytest tests/test_first_payment_blast.py -v` ### Constraints - Match existing SQLAlchemy patterns in admin.py - Prefer explicit EXISTS subquery for readability - Scope to current active monthly product; revisit for multi-month billing ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes - [ ] Verified against prod: re-running Apr blast would skip Querenne ### Related - `westside-basketball` — project this affects - `basketball-api#479` — related amount-match fix - Companion ticket: friendlier 409 for already-paid players
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/basketball-api#482
No description provided.