promotions/apps/promotions/migrations
Ali Asadi ea070ad293 FEATURE(promotions): wallet-transaction ledger + reversible payouts
The promotions service had no record of the wallet transfers it makes --
the Promotion row's state was the only trace. Add PromotionTransaction, a
ledger row per transfer (mirrors advertising's AdPayment / escrow's
EscrowWalletPayment): PAYOUT (promotions credit -> recipient wallet) and
ROLLBACK (advertising transit -> promotions credit).

- Promotion.promote() now drives its payout through a PromotionTransaction
  PAYOUT row (.execute() does the submit/verify dance) instead of an
  inline, unrecorded wallet call.
- rollback_promotion_payout(user, event_label): reverses a payout that
  landed in the advertising transit wallet, back to the promotions credit
  wallet, when the advertising side discards what it paid for (e.g. a
  captured billboard deleted while pending approval). The Promotion stays
  consumed -- only the money moves; payouts straight to the user's wallet
  are not reversible. Idempotent; returns reversed | deferred | nothing.
- The ROLLBACK row doubles as the async-race marker: when the request
  arrives before the Celery payout task has run, a ROLLBACK row is
  recorded and Recipient.promote() suppresses (or, if it raced, reverses)
  the payout. Replaces the separate PromotionRollback table from the first
  cut of this change.
- POST .../application/<user>/event/<event_label>/rollback/
- migration 0011 (hand-written; verified via makemigrations --dry-run + check)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 15:07:37 +03:30
..
0001_initial.py delme 2025-10-30 15:55:58 +03:30
0002_promotion_event_alter_plan_processor_and_more.py first test pass 2025-11-06 14:11:00 +03:30
0003_alter_recipient_promotion_type.py promotion get promoted 2025-11-08 20:16:33 +03:30
0004_alter_promotion_state.py new wallet system 2025-11-11 18:57:22 +03:30
0005_alter_event_user.py bugfix 2025-11-12 15:34:38 +03:30
0006_event_promotions_event_data_bc656abc_and_more.py index for jsonfield 2025-11-15 14:50:18 +03:30
0007_alter_event_options_plan_banner_and_more.py get plan details 2026-02-22 13:59:48 +03:30
0008_plan_balance_holder_alter_plan_description_details.py get promotion amount from another promotion 2026-06-25 11:52:04 +03:30
0009_recipient_access_type_alloweduser.py FEATURE(promotions): restrict recipients to allowed users 2026-08-03 16:11:33 +03:30
0010_recipient_wallet_destination.py FIX(promotions): route payouts by explicit Recipient.wallet_destination, not promotion type 2026-08-25 17:12:01 +03:30
0011_promotiontransaction.py FEATURE(promotions): wallet-transaction ledger + reversible payouts 2026-09-01 15:07:37 +03:30
__init__.py basic promotion handler 2025-08-03 13:28:40 +03:30