dev #25

Merged
andika merged 5 commits from dev into loaf-prod 2026-01-26 11:20:15 +00:00
2 changed files with 144 additions and 0 deletions
Showing only changes of commit b29bb641f5 - Show all commits

Binary file not shown.

144
server.py
View File

@@ -1742,6 +1742,75 @@ async def get_my_event_activity(
"total_rsvps": len(rsvps) "total_rsvps": len(rsvps)
} }
# ============================================================================
# Member Transaction History Endpoint
# ============================================================================
@api_router.get("/members/transactions")
async def get_member_transactions(
current_user: User = Depends(get_active_member),
db: Session = Depends(get_db)
):
"""
Get current member's transaction history including subscriptions and donations.
Returns both types of transactions sorted by date (newest first).
"""
# Get user's subscriptions with plan details
subscriptions = db.query(Subscription).filter(
Subscription.user_id == current_user.id
).order_by(Subscription.created_at.desc()).all()
subscription_list = []
for sub in subscriptions:
plan = db.query(SubscriptionPlan).filter(SubscriptionPlan.id == sub.plan_id).first()
subscription_list.append({
"id": str(sub.id),
"type": "subscription",
"description": plan.name if plan else "Subscription",
"amount_cents": sub.amount_paid_cents or (sub.base_subscription_cents + sub.donation_cents),
"base_amount_cents": sub.base_subscription_cents,
"donation_cents": sub.donation_cents,
"status": sub.status.value if sub.status else "unknown",
"payment_method": sub.payment_method,
"card_brand": sub.card_brand,
"card_last4": sub.card_last4,
"stripe_receipt_url": sub.stripe_receipt_url,
"created_at": sub.created_at.isoformat() if sub.created_at else None,
"payment_completed_at": sub.payment_completed_at.isoformat() if sub.payment_completed_at else None,
"start_date": sub.start_date.isoformat() if sub.start_date else None,
"end_date": sub.end_date.isoformat() if sub.end_date else None,
"billing_cycle": plan.billing_cycle if plan else None,
"manual_payment": sub.manual_payment
})
# Get user's donations
donations = db.query(Donation).filter(
Donation.user_id == current_user.id
).order_by(Donation.created_at.desc()).all()
donation_list = []
for don in donations:
donation_list.append({
"id": str(don.id),
"type": "donation",
"description": "Donation",
"amount_cents": don.amount_cents,
"status": don.status.value if don.status else "unknown",
"payment_method": don.payment_method,
"card_brand": don.card_brand,
"card_last4": don.card_last4,
"stripe_receipt_url": don.stripe_receipt_url,
"created_at": don.created_at.isoformat() if don.created_at else None,
"payment_completed_at": don.payment_completed_at.isoformat() if don.payment_completed_at else None,
"notes": don.notes
})
return {
"subscriptions": subscription_list,
"donations": donation_list,
"total_subscription_amount_cents": sum(s["amount_cents"] or 0 for s in subscription_list),
"total_donation_amount_cents": sum(d["amount_cents"] or 0 for d in donation_list)
}
# ============================================================================ # ============================================================================
# Calendar Export Endpoints (Universal iCalendar .ics format) # Calendar Export Endpoints (Universal iCalendar .ics format)
# ============================================================================ # ============================================================================
@@ -2305,6 +2374,81 @@ async def get_user_by_id(
"updated_at": user.updated_at.isoformat() if user.updated_at else None "updated_at": user.updated_at.isoformat() if user.updated_at else None
} }
@api_router.get("/admin/users/{user_id}/transactions")
async def get_user_transactions(
user_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(require_permission("users.view"))
):
"""
Get a specific user's transaction history (admin only).
Returns subscriptions and donations for the specified user.
"""
# Verify user exists
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Get user's subscriptions with plan details
subscriptions = db.query(Subscription).filter(
Subscription.user_id == user_id
).order_by(Subscription.created_at.desc()).all()
subscription_list = []
for sub in subscriptions:
plan = db.query(SubscriptionPlan).filter(SubscriptionPlan.id == sub.plan_id).first()
subscription_list.append({
"id": str(sub.id),
"type": "subscription",
"description": plan.name if plan else "Subscription",
"amount_cents": sub.amount_paid_cents or (sub.base_subscription_cents + sub.donation_cents),
"base_amount_cents": sub.base_subscription_cents,
"donation_cents": sub.donation_cents,
"status": sub.status.value if sub.status else "unknown",
"payment_method": sub.payment_method,
"card_brand": sub.card_brand,
"card_last4": sub.card_last4,
"stripe_receipt_url": sub.stripe_receipt_url,
"created_at": sub.created_at.isoformat() if sub.created_at else None,
"payment_completed_at": sub.payment_completed_at.isoformat() if sub.payment_completed_at else None,
"start_date": sub.start_date.isoformat() if sub.start_date else None,
"end_date": sub.end_date.isoformat() if sub.end_date else None,
"billing_cycle": plan.billing_cycle if plan else None,
"manual_payment": sub.manual_payment,
"manual_payment_notes": sub.manual_payment_notes
})
# Get user's donations
donations = db.query(Donation).filter(
Donation.user_id == user_id
).order_by(Donation.created_at.desc()).all()
donation_list = []
for don in donations:
donation_list.append({
"id": str(don.id),
"type": "donation",
"description": "Donation",
"amount_cents": don.amount_cents,
"status": don.status.value if don.status else "unknown",
"payment_method": don.payment_method,
"card_brand": don.card_brand,
"card_last4": don.card_last4,
"stripe_receipt_url": don.stripe_receipt_url,
"created_at": don.created_at.isoformat() if don.created_at else None,
"payment_completed_at": don.payment_completed_at.isoformat() if don.payment_completed_at else None,
"notes": don.notes
})
return {
"user_id": str(user.id),
"user_name": f"{user.first_name} {user.last_name}",
"subscriptions": subscription_list,
"donations": donation_list,
"total_subscription_amount_cents": sum(s["amount_cents"] or 0 for s in subscription_list),
"total_donation_amount_cents": sum(d["amount_cents"] or 0 for d in donation_list)
}
@api_router.put("/admin/users/{user_id}") @api_router.put("/admin/users/{user_id}")
async def update_user_profile( async def update_user_profile(
user_id: str, user_id: str,