2056d5bbc7
Self-service /suff/pay/ page lets users record their own payments (cash/PayPal/bank/other) against their tab. Open balance is shown on /suff/me/ alongside total and paid amount, with a Bezahlen button when something is owed. Staff-only /suff/dashboard/ replaces the drink_stats / total_balance / user_stats CLI commands with a mobile-friendly festival view: overall refinancing progress bar (Spenden vs. Ausgaben with Bilanz), drinks refinancing bar (sales revenue vs. purchase cost with profit), per-drink sold/total/balance, open balances per user, and fun facts (top spender, top drink, busiest day, and top user per festival day). Linked from /suff/me/ when the logged-in user is staff. seed_drinks_2026 also creates the non-drink Payments we already know about (toilets, drinks/equipment down payment, band fees per stage day), idempotently keyed on (purpose, date). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
65 lines
2.4 KiB
Python
65 lines
2.4 KiB
Python
from datetime import date
|
|
|
|
from django.core.management import BaseCommand
|
|
from django.db import transaction
|
|
|
|
from gaehsnitz.models import Drink, Payment
|
|
|
|
PAYMENTS = [
|
|
# purpose, date, amount
|
|
("Toiletten", date(2026, 5, 6), 210.01),
|
|
("Anzahlung Getränke+Kühlschrank+Bänke", date(2026, 5, 18), 400.00),
|
|
("Band: Six Good Years", date(2026, 6, 12), 150.00),
|
|
("Band: Melo-Komplott", date(2026, 6, 12), 100.00),
|
|
("Band: Mörtel", date(2026, 6, 12), 150.00),
|
|
("Band: Kotpiloten", date(2026, 6, 13), 150.00),
|
|
("Band: Knast", date(2026, 6, 13), 150.00),
|
|
("Band: Quast", date(2026, 6, 13), 300.00),
|
|
]
|
|
|
|
DRINKS = [
|
|
# name, crates, btl/crate, size, price/crate, deposit/crate, sale/btl
|
|
("Sterni", 12, 20, 0.5, 10.99, 3.10, 2.00),
|
|
("Krosti", 5, 20, 0.5, 16.49, 3.10, 2.50),
|
|
("Buddi", 5, 20, 0.5, 20.99, 3.10, 2.50),
|
|
("Helles", 5, 20, 0.5, 15.99, 4.50, 2.50),
|
|
("Radler", 2, 20, 0.5, 14.99, 3.10, 2.50),
|
|
("Lübzer 0,0", 1, 20, 0.5, 17.99, 3.10, 2.50),
|
|
("Freiberger 0,0", 4, 20, 0.5, 15.49, 3.10, 2.50),
|
|
("Mate", 2, 20, 0.5, 17.49, 4.50, 2.50),
|
|
("Vita Cola", 2, 12, 1.0, 10.99, 3.30, 2.50),
|
|
("Spezi", 2, 20, 0.5, 17.99, 3.10, 2.50),
|
|
("Wasser", 10, 12, 1.0, 5.99, 3.30, 1.50),
|
|
]
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Seed Drink rows for the 2026 festival from the supplier invoice."
|
|
|
|
@transaction.atomic
|
|
def handle(self, *args, **options):
|
|
for purpose, day, amount in PAYMENTS:
|
|
obj, created = Payment.objects.update_or_create(
|
|
purpose=purpose,
|
|
date=day,
|
|
defaults={"amount": amount},
|
|
)
|
|
self.stdout.write(f"{'created' if created else 'updated'}: {obj.purpose} ({obj.date})")
|
|
|
|
for name, crates, btl, size, price, deposit, sale in DRINKS:
|
|
obj, created = Drink.objects.update_or_create(
|
|
name=name,
|
|
year=2026,
|
|
defaults={
|
|
"crates_ordered": crates,
|
|
"crates_purchased": crates,
|
|
"crates_returned": 0,
|
|
"purchase_price_per_crate": price,
|
|
"deposit_per_crate": deposit,
|
|
"bottles_per_crate": btl,
|
|
"bottle_size": size,
|
|
"sale_price_per_bottle": sale,
|
|
},
|
|
)
|
|
self.stdout.write(f"{'created' if created else 'updated'}: {obj.name}")
|