Compare commits

..

17 Commits

Author SHA1 Message Date
57d0796693 Update dependency ruff to v0.15.7 2026-03-19 17:00:50 +00:00
de8fd5e3cc Merge pull request 'Update dependency django to v6.0.3' (#19) from renovate/django-6.x into main
Reviewed-on: #19
2026-03-12 11:32:41 +00:00
3bfe7340ca Merge pull request 'Update dependency ruff to v0.15.5' (#18) from renovate/ruff-0.x into main
Reviewed-on: #18
2026-03-12 11:31:39 +00:00
d38c0d0a24 Update dependency ruff to v0.15.5 2026-03-06 07:00:57 +00:00
0d6f7bdd3a Update dependency django to v6.0.3 2026-03-03 17:00:56 +00:00
91571bf935 Merge pull request 'Update dependency ruff to v0.15.2' (#17) from renovate/ruff-0.x into main
Reviewed-on: #17
2026-02-23 22:26:16 +00:00
22c85b5032 change workdir and fix uv venv usage 2026-02-21 12:19:49 +01:00
62eb864c18 Update dependency ruff to v0.15.2 2026-02-20 07:00:57 +00:00
1793be90e1 reformat using prettier 2026-02-19 18:53:08 +01:00
acd8e8a52a add editorconfig 2026-02-19 18:48:38 +01:00
8a1a5ac000 update dependencies and add ruff 2026-02-19 18:42:42 +01:00
10d3254dda reformat everything using ruff 2026-02-19 18:32:01 +01:00
5b48167c66 replace pip with uv 2026-02-19 18:31:21 +01:00
683c73421f Merge pull request 'Update dependency django to v6.0.2' (#15) from renovate/django-6.x into main
Reviewed-on: #15
2026-02-19 16:44:07 +00:00
d4aeb50af9 Merge pull request 'Update dependency psycopg to v3.3.3' (#16) from renovate/psycopg-3.x into main
Reviewed-on: #16
2026-02-19 16:41:14 +00:00
b58800a935 Update dependency psycopg to v3.3.3 2026-02-18 17:01:07 +00:00
25e2cd72f5 Update dependency django to v6.0.2 2026-02-03 17:01:22 +00:00
16 changed files with 530 additions and 331 deletions

10
.editorconfig Normal file
View File

@@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 120
trim_trailing_whitespace = true

View File

@@ -1,8 +1,10 @@
FROM python:3.14-alpine FROM python:3.14-alpine
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
WORKDIR /code/ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
COPY requirements.txt . WORKDIR /app
RUN pip install --upgrade pip && \ COPY pyproject.toml .
pip install --no-cache-dir --requirement requirements.txt ENV VIRTUAL_ENV=/opt/venv
RUN uv venv $VIRTUAL_ENV && uv pip install -r pyproject.toml
ENV PATH="/opt/venv/bin:$PATH"
COPY . . COPY . .
ENTRYPOINT ["./entrypoint.sh"] ENTRYPOINT ["./entrypoint.sh"]

View File

@@ -1,30 +1,29 @@
services: services:
db:
image: postgres:18-alpine
init: true
environment:
POSTGRES_DB: "gaehsnitzdb"
POSTGRES_USER: "gaehsnitzuser"
POSTGRES_PASSWORD: "SCKL97ukwICPpjJ9rXyhdljZ86T29A"
expose:
- "5432"
volumes:
- ./volumes/db/data:/var/lib/postgresql/data:rw
db: web:
image: postgres:18-alpine build:
init: true context: .
environment: init: true
POSTGRES_DB: "gaehsnitzdb" environment:
POSTGRES_USER: "gaehsnitzuser" DB_HOST: "db"
POSTGRES_PASSWORD: "SCKL97ukwICPpjJ9rXyhdljZ86T29A" DB_PORT: "5432"
expose: DB_NAME: "gaehsnitzdb"
- "5432" DB_USER: "gaehsnitzuser"
volumes: DB_PASSWORD: "SCKL97ukwICPpjJ9rXyhdljZ86T29A"
- ./volumes/db/data:/var/lib/postgresql/data:rw depends_on:
- db
web: ports:
build: - "80:8000"
context: . volumes:
init: true - .:/app
environment:
DB_HOST: "db"
DB_PORT: "5432"
DB_NAME: "gaehsnitzdb"
DB_USER: "gaehsnitzuser"
DB_PASSWORD: "SCKL97ukwICPpjJ9rXyhdljZ86T29A"
depends_on:
- db
ports:
- "80:8000"
volumes:
- .:/code:rw

View File

@@ -20,17 +20,18 @@ class CustomUserAdmin(UserAdmin):
ordering = ("username",) ordering = ("username",)
list_filter = [] list_filter = []
fieldsets = ( fieldsets = (
(None, {"fields": ( (
"username", None,
"password", {
)}), "fields": (
("BALANCE", {"fields": ( "username",
"consumed_drinks_price", "password",
)}), )
) },
readonly_fields = ( ),
"consumed_drinks_price", ("BALANCE", {"fields": ("consumed_drinks_price",)}),
) )
readonly_fields = ("consumed_drinks_price",)
inlines = (ConsumptionInline,) inlines = (ConsumptionInline,)
def consumed_drinks_price(self, user: User): def consumed_drinks_price(self, user: User):
@@ -61,52 +62,88 @@ class PaymentAdmin(admin.ModelAdmin):
class DrinkAdmin(admin.ModelAdmin): class DrinkAdmin(admin.ModelAdmin):
list_display = ("name", "purchase_price_per_crate", "crates_purchased", "purchase_price_total") list_display = ("name", "purchase_price_per_crate", "crates_purchased", "purchase_price_total")
fieldsets = ( fieldsets = (
(None, {"fields": ( (None, {"fields": ("name",)}),
"name", (
)}), "crates",
("crates", {"fields": ( {
"crates_ordered", "fields": (
"crates_purchased", "crates_ordered",
"crates_returned", "crates_purchased",
)}), "crates_returned",
("bottles", {"fields": ( )
"bottles_per_crate", },
"bottles_total", ),
"bottles_returned", (
)}), "bottles",
("amount", {"fields": ( {
"bottle_size", "fields": (
"amount_per_crate", "bottles_per_crate",
"amount_total", "bottles_total",
)}), "bottles_returned",
("purchase", {"fields": ( )
"purchase_price_per_crate", },
"purchase_price_per_bottle", ),
"purchase_price_total", (
)}), "amount",
("deposit", {"fields": ( {
"deposit_per_crate", "fields": (
"deposit_total", "bottle_size",
"deposit_refund", "amount_per_crate",
"deposit_kept", "amount_total",
)}), )
("sales", {"fields": ( },
"sale_price_per_bottle", ),
"bottles_sold", (
"sales_purchase_value", "purchase",
"sale_price_total", {
"bottles_given_away", "fields": (
"giveaway_purchase_value", "purchase_price_per_crate",
"balance", "purchase_price_per_bottle",
)}), "purchase_price_total",
)
},
),
(
"deposit",
{
"fields": (
"deposit_per_crate",
"deposit_total",
"deposit_refund",
"deposit_kept",
)
},
),
(
"sales",
{
"fields": (
"sale_price_per_bottle",
"bottles_sold",
"sales_purchase_value",
"sale_price_total",
"bottles_given_away",
"giveaway_purchase_value",
"balance",
)
},
),
) )
readonly_fields = ( readonly_fields = (
"bottles_total", "bottles_returned", "bottles_total",
"amount_per_crate", "amount_total", "bottles_returned",
"purchase_price_per_bottle", "purchase_price_total", "amount_per_crate",
"deposit_total", "deposit_refund", "deposit_kept", "amount_total",
"bottles_sold", "sales_purchase_value", "sale_price_total", "purchase_price_per_bottle",
"bottles_given_away", "giveaway_purchase_value", "purchase_price_total",
"deposit_total",
"deposit_refund",
"deposit_kept",
"bottles_sold",
"sales_purchase_value",
"sale_price_total",
"bottles_given_away",
"giveaway_purchase_value",
"balance", "balance",
) )

View File

@@ -46,16 +46,14 @@ def payback(user, day, month, amount, note):
def consumption(day: int, for_free: bool, drink_dict: Dict[User, List[Tuple[int, Drink]]]): def consumption(day: int, for_free: bool, drink_dict: Dict[User, List[Tuple[int, Drink]]]):
for user, drink_list in drink_dict.items(): for user, drink_list in drink_dict.items():
for amount, drink in drink_list: for amount, drink in drink_list:
Consumption.objects.create( Consumption.objects.create(user=user, drink=drink, amount=amount, day=day, for_free=for_free)
user=user, drink=drink, amount=amount, day=day, for_free=for_free)
class Command(BaseCommand): class Command(BaseCommand):
@transaction.atomic() @transaction.atomic()
def handle(self, *args, **options): def handle(self, *args, **options):
if any(model.objects.exists() for model in [Donation, Payment, Drink, Consumption]): if any(model.objects.exists() for model in [Donation, Payment, Drink, Consumption]):
raise CommandError( raise CommandError("clear all donation, payment, drink and consumption objects before running this command")
"clear all donation, payment, drink and consumption objects before running this command")
# --------------- PEOPLE --------------- # --------------- PEOPLE ---------------
@@ -224,82 +222,106 @@ class Command(BaseCommand):
# --------------- THURSDAY --------------- # --------------- THURSDAY ---------------
consumption(day=1, for_free=False, drink_dict={ consumption(
robert: [(2, mate), (2, radler), (1, wasser)], day=1,
thure: [(4, sterni), (1, krosti), (2, radler)], for_free=False,
tobi: [(7, sterni), (2, krosti)], drink_dict={
herald: [(1, radler), (2, wasser), (4, wein)], robert: [(2, mate), (2, radler), (1, wasser)],
annemarie: [(1, sterni), (1, wasser), (2, wein)], thure: [(4, sterni), (1, krosti), (2, radler)],
cathi_clemens: [(1, sterni), (2, radler)], tobi: [(7, sterni), (2, krosti)],
flo: [(6, sterni), (1, wasser)], herald: [(1, radler), (2, wasser), (4, wein)],
josi: [(4, sterni)], annemarie: [(1, sterni), (1, wasser), (2, wein)],
aimee: [(5, sterni)], cathi_clemens: [(1, sterni), (2, radler)],
lutz: [(1, sterni), (1, wasser)], flo: [(6, sterni), (1, wasser)],
lasse: [(2, sterni), (1, wasser)], josi: [(4, sterni)],
domi: [(1, sterni), (1, radler)], aimee: [(5, sterni)],
}) lutz: [(1, sterni), (1, wasser)],
consumption(day=1, for_free=True, drink_dict={ lasse: [(2, sterni), (1, wasser)],
seth_family: [(1, krosti), (1, radler)], domi: [(1, sterni), (1, radler)],
}) },
)
consumption(
day=1,
for_free=True,
drink_dict={
seth_family: [(1, krosti), (1, radler)],
},
)
# --------------- FRIDAY --------------- # --------------- FRIDAY ---------------
consumption(day=2, for_free=False, drink_dict={ consumption(
robert: [(2, mate), (1, radler)], day=2,
thure: [(8, sterni), (2, krosti), (1, wasser)], for_free=False,
tobi: [(2, mate), (5, sterni), (5, krosti), (3, radler), (1, cola)], drink_dict={
herald: [(1, mate), (1, sterni), (2, radler), (1, cola), (4, wasser), (7, wein)], robert: [(2, mate), (1, radler)],
annemarie: [(2, mate), (9, sterni), (1, krosti), (2, radler), (3, wasser)], thure: [(8, sterni), (2, krosti), (1, wasser)],
cathi_clemens: [(3, sterni), (3, radler)], tobi: [(2, mate), (5, sterni), (5, krosti), (3, radler), (1, cola)],
flo: [(1, mate), (6, sterni), (3, wasser)], herald: [(1, mate), (1, sterni), (2, radler), (1, cola), (4, wasser), (7, wein)],
josi: [(8, sterni), (1, krosti), (2, radler)], annemarie: [(2, mate), (9, sterni), (1, krosti), (2, radler), (3, wasser)],
andrew: [(4, sterni)], cathi_clemens: [(3, sterni), (3, radler)],
peter: [(7, sterni), (1, krosti), (2, wasser)], flo: [(1, mate), (6, sterni), (3, wasser)],
fiddi_melli: [(6, sterni), (3, radler), (1, wasser)], josi: [(8, sterni), (1, krosti), (2, radler)],
suse: [(1, krosti), (1, radler)], andrew: [(4, sterni)],
lilly_simon: [(1, mate), (7, sterni), (3, wasser)] peter: [(7, sterni), (1, krosti), (2, wasser)],
}) fiddi_melli: [(6, sterni), (3, radler), (1, wasser)],
suse: [(1, krosti), (1, radler)],
lilly_simon: [(1, mate), (7, sterni), (3, wasser)],
},
)
consumption(day=2, for_free=True, drink_dict={ consumption(
seth_family: [(1, mate), (6, sterni), (3, wasser)], day=2,
aimee: [(1, mate), (9, sterni), (3, wasser)], for_free=True,
lutz: [(1, mate), (9, sterni), (3, wasser)], drink_dict={
lasse: [(7, sterni), (1, wasser)], seth_family: [(1, mate), (6, sterni), (3, wasser)],
domi: [(1, mate), (4, sterni), (1, wasser)], aimee: [(1, mate), (9, sterni), (3, wasser)],
hans_welle: [(3, mate), (7, sterni), (7, krosti), (1, cola), (2, wasser)], lutz: [(1, mate), (9, sterni), (3, wasser)],
}) lasse: [(7, sterni), (1, wasser)],
domi: [(1, mate), (4, sterni), (1, wasser)],
hans_welle: [(3, mate), (7, sterni), (7, krosti), (1, cola), (2, wasser)],
},
)
# --------------- SATURDAY --------------- # --------------- SATURDAY ---------------
consumption(day=3, for_free=False, drink_dict={ consumption(
robert: [(4, mate), (1, cola), (4, wasser)], day=3,
tobi: [(9, sterni), (1, krosti), (2, radler), (1, cola)], for_free=False,
herald: [(2, mate), (2, radler), (3, wasser), (5, wein)], drink_dict={
annemarie: [(2, mate), (6, sterni), (3, radler), (1, cola), (1, wasser)], robert: [(4, mate), (1, cola), (4, wasser)],
cathi_clemens: [(4, sterni), (8, radler), (1, wasser)], tobi: [(9, sterni), (1, krosti), (2, radler), (1, cola)],
flo: [(2, mate), (8, sterni), (1, radler), (3, wasser)], herald: [(2, mate), (2, radler), (3, wasser), (5, wein)],
josi: [(9, sterni), (2, radler), (2, wasser)], annemarie: [(2, mate), (6, sterni), (3, radler), (1, cola), (1, wasser)],
aimee: [(10, sterni), (2, radler)], cathi_clemens: [(4, sterni), (8, radler), (1, wasser)],
lutz: [(1, krosti), (1, wasser)], flo: [(2, mate), (8, sterni), (1, radler), (3, wasser)],
lasse: [(1, mate), (8, sterni)], josi: [(9, sterni), (2, radler), (2, wasser)],
domi: [(6, sterni), (2, radler), (1, wasser)], aimee: [(10, sterni), (2, radler)],
peter: [(3, sterni), (3, krosti), (2, radler)], lutz: [(1, krosti), (1, wasser)],
fiddi_melli: [(1, mate), (1, wasser)], lasse: [(1, mate), (8, sterni)],
suse: [(1, mate), (1, sterni), (2, krosti), (4, radler)], domi: [(6, sterni), (2, radler), (1, wasser)],
lilly_simon: [(1, mate), (2, radler), (7, wasser)], peter: [(3, sterni), (3, krosti), (2, radler)],
kevin: [(1, mate), (1, radler)], fiddi_melli: [(1, mate), (1, wasser)],
rebecca: [(1, radler)], suse: [(1, mate), (1, sterni), (2, krosti), (4, radler)],
resi_simon: [(8, sterni), (1, radler), (1, wasser)], lilly_simon: [(1, mate), (2, radler), (7, wasser)],
}) kevin: [(1, mate), (1, radler)],
rebecca: [(1, radler)],
resi_simon: [(8, sterni), (1, radler), (1, wasser)],
},
)
consumption(day=3, for_free=True, drink_dict={ consumption(
thure: [(1, mate), (4, sterni), (5, radler), (3, wasser)], day=3,
seth_family: [(1, mate), (2, sterni), (12, krosti), (1, wasser)], for_free=True,
andrew: [(3, sterni), (2, krosti), (3, radler), (1, wasser)], drink_dict={
robin: [(1, mate), (4, krosti), (2, radler)], thure: [(1, mate), (4, sterni), (5, radler), (3, wasser)],
melokomplott: [(5, mate), (13, sterni), (1, krosti), (1, radler)], seth_family: [(1, mate), (2, sterni), (12, krosti), (1, wasser)],
residudes: [(2, mate), (1, sterni), (15, krosti), (7, radler), (1, wasser)], andrew: [(3, sterni), (2, krosti), (3, radler), (1, wasser)],
}) robin: [(1, mate), (4, krosti), (2, radler)],
melokomplott: [(5, mate), (13, sterni), (1, krosti), (1, radler)],
residudes: [(2, mate), (1, sterni), (15, krosti), (7, radler), (1, wasser)],
},
)
# --------------- ADMIN STUFF --------------- # --------------- ADMIN STUFF ---------------

View File

@@ -35,16 +35,14 @@ def payment(purpose, day, month, amount):
def consumption(day: int, for_free: bool, drink_dict: Dict[User, List[Tuple[int, Drink]]]): def consumption(day: int, for_free: bool, drink_dict: Dict[User, List[Tuple[int, Drink]]]):
for user, drink_list in drink_dict.items(): for user, drink_list in drink_dict.items():
for amount, drink in drink_list: for amount, drink in drink_list:
Consumption.objects.create( Consumption.objects.create(user=user, drink=drink, amount=amount, day=day, for_free=for_free)
user=user, drink=drink, amount=amount, day=day, for_free=for_free)
class Command(BaseCommand): class Command(BaseCommand):
@transaction.atomic() @transaction.atomic()
def handle(self, *args, **options): def handle(self, *args, **options):
if any(model.objects.exists() for model in [Donation, Payment, Drink, Consumption]): if any(model.objects.exists() for model in [Donation, Payment, Drink, Consumption]):
raise CommandError( raise CommandError("clear all donation, payment, drink and consumption objects before running this command")
"clear all donation, payment, drink and consumption objects before running this command")
# --------------- PEOPLE --------------- # --------------- PEOPLE ---------------
@@ -232,86 +230,106 @@ class Command(BaseCommand):
# --------------- THURSDAY --------------- # --------------- THURSDAY ---------------
consumption(day=1, for_free=False, drink_dict={ consumption(
enni: [(2, wasser), (9, sterni)], day=1,
robert: [(2, wasser), (2, freiberger), (1, spezi), (2, mate)], for_free=False,
josi: [(5, sterni), (1, radler)], drink_dict={
tobi: [(5, sterni), (1, freiberger), (2, mate)], enni: [(2, wasser), (9, sterni)],
annemarie: [(1, wasser), (4, sterni), (1, radler)], robert: [(2, wasser), (2, freiberger), (1, spezi), (2, mate)],
sandra: [(1, wasser), (3, sterni), (1, spezi), (1, mate)], josi: [(5, sterni), (1, radler)],
flo: [(2, wasser), (5, sterni), (1, mate), (1, helles)], tobi: [(5, sterni), (1, freiberger), (2, mate)],
}) annemarie: [(1, wasser), (4, sterni), (1, radler)],
sandra: [(1, wasser), (3, sterni), (1, spezi), (1, mate)],
flo: [(2, wasser), (5, sterni), (1, mate), (1, helles)],
},
)
# --------------- FRIDAY --------------- # --------------- FRIDAY ---------------
consumption(day=2, for_free=False, drink_dict={ consumption(
tobi: [(2, wasser), (9, sterni), (2, freiberger)], day=2,
annemarie: [(1, wasser), (5, sterni), (2, mate), (2, radler), (2, buddi), (3, sekt)], for_free=False,
sandra: [(1, freiberger), (1, buddi), (1, spezi), (2, mate), (1, radler), (2, sekt)], drink_dict={
robert: [(2, wasser), (1, freiberger), (1, buddi), (1, spezi)], tobi: [(2, wasser), (9, sterni), (2, freiberger)],
josi: [(2, wasser), (6, sterni), (1, freiberger), (2, radler)], annemarie: [(1, wasser), (5, sterni), (2, mate), (2, radler), (2, buddi), (3, sekt)],
enni: [(2, wasser), (10, sterni), (1, spezi)], sandra: [(1, freiberger), (1, buddi), (1, spezi), (2, mate), (1, radler), (2, sekt)],
steven: [(1, sterni), (1, krosti), (1, radler), (5, helles)], robert: [(2, wasser), (1, freiberger), (1, buddi), (1, spezi)],
marten: [(9, krosti)], josi: [(2, wasser), (6, sterni), (1, freiberger), (2, radler)],
thure: [(4, sterni)], enni: [(2, wasser), (10, sterni), (1, spezi)],
jona: [(2, freiberger), (10, krosti)], steven: [(1, sterni), (1, krosti), (1, radler), (5, helles)],
benni: [(5, krosti), (1, helles)], marten: [(9, krosti)],
katha: [(1, krosti), (1, mate)], thure: [(4, sterni)],
diana: [(5, sterni), (1, radler)], jona: [(2, freiberger), (10, krosti)],
katz: [(3, krosti), (1, buddi)], benni: [(5, krosti), (1, helles)],
alexius: [(8, sterni)], katha: [(1, krosti), (1, mate)],
pauline: [(5, sterni), (1, radler)], diana: [(5, sterni), (1, radler)],
herald: [(1, wasser), (2, sterni), (1, freiberger)], katz: [(3, krosti), (1, buddi)],
arthur: [(6, sterni), (1, radler)], alexius: [(8, sterni)],
matze: [(1, sterni), (1, freiberger)], pauline: [(5, sterni), (1, radler)],
dennis: [(1, sterni), (2, krosti)], herald: [(1, wasser), (2, sterni), (1, freiberger)],
marvin: [(3, sterni), (1, krosti), (1, mate)], arthur: [(6, sterni), (1, radler)],
simon: [(1, sterni)], matze: [(1, sterni), (1, freiberger)],
}) dennis: [(1, sterni), (2, krosti)],
marvin: [(3, sterni), (1, krosti), (1, mate)],
simon: [(1, sterni)],
},
)
consumption(day=2, for_free=True, drink_dict={ consumption(
flo: [(2, wasser), (3, sterni), (1, freiberger), (3, helles)], day=2,
casi: [(6, sterni)], for_free=True,
sepp: [(6, sterni)], drink_dict={
ohli: [(4, sterni)], flo: [(2, wasser), (3, sterni), (1, freiberger), (3, helles)],
marius: [(1, radler)], casi: [(6, sterni)],
anonym: [(10, sterni), (10, krosti), (2, radler), (4, helles), (1, sekt)] sepp: [(6, sterni)],
}) ohli: [(4, sterni)],
marius: [(1, radler)],
anonym: [(10, sterni), (10, krosti), (2, radler), (4, helles), (1, sekt)],
},
)
# --------------- SATURDAY --------------- # --------------- SATURDAY ---------------
consumption(day=3, for_free=False, drink_dict={ consumption(
herald: [(1, sterni), (2, freiberger), (2, radler), (1, buddi), (5, spezi), (2, wasser)], day=3,
thure: [(6, sterni), (1, krosti), (1, radler), (2, spezi)], for_free=False,
sandra: [(6, sterni), (1, freiberger), (2, radler), (1, mate), (1, cola), (1, sekt)], drink_dict={
marvin: [(1, sterni), (1, mate)], herald: [(1, sterni), (2, freiberger), (2, radler), (1, buddi), (5, spezi), (2, wasser)],
simon: [(10, sterni), (1, radler), (1, mate)], thure: [(6, sterni), (1, krosti), (1, radler), (2, spezi)],
jona: [(6, sterni), (4, krosti)], sandra: [(6, sterni), (1, freiberger), (2, radler), (1, mate), (1, cola), (1, sekt)],
robert: [(1, sterni), (1, freiberger), (1, buddi), (2, mate), (2, spezi), (3, wasser)], marvin: [(1, sterni), (1, mate)],
steven: [(5, helles), (1, sterni), (2, radler), (1, mate)], simon: [(10, sterni), (1, radler), (1, mate)],
flo: [(4, helles), (1, freiberger), (1, buddi), (2, mate)], jona: [(6, sterni), (4, krosti)],
matze: [(4, sterni), (2, radler), (3, buddi), (7, spezi)], robert: [(1, sterni), (1, freiberger), (1, buddi), (2, mate), (2, spezi), (3, wasser)],
alexius: [(2, helles), (3, sterni), (1, radler), (1, cola)], steven: [(5, helles), (1, sterni), (2, radler), (1, mate)],
josi: [(5, sterni), (1, radler), (2, mate), (2, wasser), (1, sekt)], flo: [(4, helles), (1, freiberger), (1, buddi), (2, mate)],
benni: [(1, helles), (2, krosti), (2, radler), (1, wasser)], matze: [(4, sterni), (2, radler), (3, buddi), (7, spezi)],
ohli: [(1, helles), (3, sterni), (2, krosti), (1, radler), (1, buddi), (1, mate)], alexius: [(2, helles), (3, sterni), (1, radler), (1, cola)],
arthur: [(3, sterni), (1, krosti), (1, radler), (1, mate), (3, spezi), (2, sekt)], josi: [(5, sterni), (1, radler), (2, mate), (2, wasser), (1, sekt)],
pauline: [(2, sterni), (2, radler), (1, mate), (1, cola)], benni: [(1, helles), (2, krosti), (2, radler), (1, wasser)],
enni: [(9, sterni), (1, spezi), (2, wasser), (1, sekt)], ohli: [(1, helles), (3, sterni), (2, krosti), (1, radler), (1, buddi), (1, mate)],
annemarie: [(5, sterni), (4, radler), (2, buddi), (1, mate), (1, wasser), (2, sekt)], arthur: [(3, sterni), (1, krosti), (1, radler), (1, mate), (3, spezi), (2, sekt)],
tobi: [(3, sterni), (1, radler), (1, mate), (1, wasser)], pauline: [(2, sterni), (2, radler), (1, mate), (1, cola)],
marten: [(4, helles), (1, krosti)], enni: [(9, sterni), (1, spezi), (2, wasser), (1, sekt)],
lukas: [(4, sterni), (1, spezi)], annemarie: [(5, sterni), (4, radler), (2, buddi), (1, mate), (1, wasser), (2, sekt)],
lilly: [(5, sterni), (1, mate)], tobi: [(3, sterni), (1, radler), (1, mate), (1, wasser)],
andrea: [(1, buddi)], marten: [(4, helles), (1, krosti)],
lena: [(1, sterni)], lukas: [(4, sterni), (1, spezi)],
anonym: [(2, helles), (3, sterni), (5, krosti), (2, radler), (1, cola), (2, sekt)] lilly: [(5, sterni), (1, mate)],
}) andrea: [(1, buddi)],
lena: [(1, sterni)],
anonym: [(2, helles), (3, sterni), (5, krosti), (2, radler), (1, cola), (2, sekt)],
},
)
consumption(day=3, for_free=True, drink_dict={ consumption(
rockbert: [(8, krosti), (1, radler), (1, mate)], day=3,
anonym: [(8, sterni), (2, krosti), (2, mate), (1, cola)] for_free=True,
}) drink_dict={
rockbert: [(8, krosti), (1, radler), (1, mate)],
anonym: [(8, sterni), (2, krosti), (2, mate), (1, cola)],
},
)
# --------------- ADMIN STUFF --------------- # --------------- ADMIN STUFF ---------------

View File

@@ -20,4 +20,4 @@ class Command(BaseCommand):
name = id_to_name[drink_dict["drink_id"]] name = id_to_name[drink_dict["drink_id"]]
amount = drink_dict["amount"] amount = drink_dict["amount"]
drink_list.append(f"{amount}x {name}") drink_list.append(f"{amount}x {name}")
print(f"{user.username.capitalize()}: {euro(to_pay)} ({", ".join(drink_list)})") print(f"{user.username.capitalize()}: {euro(to_pay)} ({', '.join(drink_list)})")

View File

@@ -10,94 +10,189 @@ import gaehsnitz.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('auth', '0012_alter_user_first_name_max_length'), ("auth", "0012_alter_user_first_name_max_length"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='User', name="User",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('password', models.CharField(max_length=128, verbose_name='password')), ("password", models.CharField(max_length=128, verbose_name="password")),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ("last_login", models.DateTimeField(blank=True, null=True, verbose_name="last login")),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), (
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), "is_superuser",
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), models.BooleanField(
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), default=False,
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), help_text="Designates that this user has all permissions without explicitly assigning them.",
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), verbose_name="superuser status",
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), (
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), "username",
models.CharField(
error_messages={"unique": "A user with that username already exists."},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150,
unique=True,
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
verbose_name="username",
),
),
("first_name", models.CharField(blank=True, max_length=150, verbose_name="first name")),
("last_name", models.CharField(blank=True, max_length=150, verbose_name="last name")),
("email", models.EmailField(blank=True, max_length=254, verbose_name="email address")),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
("date_joined", models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined")),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
], ],
options={ options={
'verbose_name': 'user', "verbose_name": "user",
'verbose_name_plural': 'users', "verbose_name_plural": "users",
'abstract': False, "abstract": False,
}, },
managers=[ managers=[
('objects', django.contrib.auth.models.UserManager()), ("objects", django.contrib.auth.models.UserManager()),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Drink', name="Drink",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('name', models.CharField(max_length=32, unique=True)), ("name", models.CharField(max_length=32, unique=True)),
('crates_ordered', models.PositiveSmallIntegerField(help_text='just informational to see how good we planned, not the actual consumed/paid drinks')), (
('crates_purchased', models.PositiveSmallIntegerField()), "crates_ordered",
('crates_returned', models.PositiveSmallIntegerField()), models.PositiveSmallIntegerField(
('purchase_price_per_crate', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), help_text="just informational to see how good we planned, not the actual consumed/paid drinks"
('deposit_per_crate', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), ),
('bottles_per_crate', models.PositiveSmallIntegerField()), ),
('bottle_size', models.FloatField()), ("crates_purchased", models.PositiveSmallIntegerField()),
('sale_price_per_bottle', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), ("crates_returned", models.PositiveSmallIntegerField()),
("purchase_price_per_crate", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
("deposit_per_crate", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
("bottles_per_crate", models.PositiveSmallIntegerField()),
("bottle_size", models.FloatField()),
("sale_price_per_bottle", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Payment', name="Payment",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('purpose', models.CharField(max_length=64)), ("purpose", models.CharField(max_length=64)),
('date', models.DateField()), ("date", models.DateField()),
('amount', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), ("amount", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
('from_user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='payments', related_query_name='payment', to=settings.AUTH_USER_MODEL)), (
"from_user",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="payments",
related_query_name="payment",
to=settings.AUTH_USER_MODEL,
),
),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Payback', name="Payback",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('date', models.DateField()), ("date", models.DateField()),
('amount', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), ("amount", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
('note', models.CharField(blank=True, default='', max_length=64)), ("note", models.CharField(blank=True, default="", max_length=64)),
('to_user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='paybacks', related_query_name='payback', to=settings.AUTH_USER_MODEL)), (
"to_user",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="paybacks",
related_query_name="payback",
to=settings.AUTH_USER_MODEL,
),
),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Donation', name="Donation",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('date', models.DateField()), ("date", models.DateField()),
('amount', gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)), ("amount", gaehsnitz.models.PriceField(decimal_places=2, max_digits=6)),
('note', models.CharField(blank=True, default='', max_length=64)), ("note", models.CharField(blank=True, default="", max_length=64)),
('from_user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='donations', related_query_name='donation', to=settings.AUTH_USER_MODEL)), (
"from_user",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="donations",
related_query_name="donation",
to=settings.AUTH_USER_MODEL,
),
),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Consumption', name="Consumption",
fields=[ fields=[
('id', models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("id", models.SmallAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
('amount', models.PositiveSmallIntegerField()), ("amount", models.PositiveSmallIntegerField()),
('day', models.PositiveSmallIntegerField(choices=[(1, 'Do'), (2, 'Fr'), (3, 'Sa')])), ("day", models.PositiveSmallIntegerField(choices=[(1, "Do"), (2, "Fr"), (3, "Sa")])),
('for_free', models.BooleanField(default=False)), ("for_free", models.BooleanField(default=False)),
('drink', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consumption_list', related_query_name='consumption', to='gaehsnitz.drink')), (
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consumption_list', related_query_name='consumption', to=settings.AUTH_USER_MODEL)), "drink",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="consumption_list",
related_query_name="consumption",
to="gaehsnitz.drink",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="consumption_list",
related_query_name="consumption",
to=settings.AUTH_USER_MODEL,
),
),
], ],
), ),
] ]

View File

@@ -4,26 +4,25 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('gaehsnitz', '0001_initial'), ("gaehsnitz", "0001_initial"),
] ]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(
model_name='donation', model_name="donation",
name='from_user', name="from_user",
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='payment', model_name="payment",
name='from_user', name="from_user",
), ),
migrations.AlterField( migrations.AlterField(
model_name='consumption', model_name="consumption",
name='day', name="day",
field=models.PositiveSmallIntegerField(choices=[(1, 'Do'), (2, 'Fr'), (3, 'Sa'), (4, 'So')]), field=models.PositiveSmallIntegerField(choices=[(1, "Do"), (2, "Fr"), (3, "Sa"), (4, "So")]),
), ),
migrations.DeleteModel( migrations.DeleteModel(
name='Payback', name="Payback",
), ),
] ]

View File

@@ -4,14 +4,12 @@ from django.db.models import Sum, F
class PriceField(models.DecimalField): class PriceField(models.DecimalField):
def __init__(self, verbose_name=None, name=None, **kwargs): def __init__(self, verbose_name=None, name=None, **kwargs):
kwargs.update({"max_digits": 6, "decimal_places": 2}) kwargs.update({"max_digits": 6, "decimal_places": 2})
super().__init__(verbose_name, name, **kwargs) super().__init__(verbose_name, name, **kwargs)
class User(AbstractUser): class User(AbstractUser):
@property @property
def paid_drinks(self): def paid_drinks(self):
return self.consumption_list.filter(for_free=False) return self.consumption_list.filter(for_free=False)
@@ -22,9 +20,9 @@ class User(AbstractUser):
@property @property
def consumed_drinks_price(self): def consumed_drinks_price(self):
query = self.paid_drinks \ query = self.paid_drinks.annotate(cost=F("amount") * F("drink__sale_price_per_bottle")).aggregate(
.annotate(cost=F("amount") * F("drink__sale_price_per_bottle")) \ sum=Sum("cost")
.aggregate(sum=Sum("cost")) )
return query["sum"] or 0 return query["sum"] or 0
@@ -45,7 +43,6 @@ class Payment(models.Model):
amount = PriceField() amount = PriceField()
class Drink(models.Model): class Drink(models.Model):
name = models.CharField(max_length=32, unique=True) name = models.CharField(max_length=32, unique=True)
crates_ordered = models.PositiveSmallIntegerField( crates_ordered = models.PositiveSmallIntegerField(
@@ -127,11 +124,11 @@ class Drink(models.Model):
class Consumption(models.Model): class Consumption(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
to=User, on_delete=models.CASCADE, to=User, on_delete=models.CASCADE, related_name="consumption_list", related_query_name="consumption"
related_name="consumption_list", related_query_name="consumption") )
drink = models.ForeignKey( drink = models.ForeignKey(
to=Drink, on_delete=models.CASCADE, to=Drink, on_delete=models.CASCADE, related_name="consumption_list", related_query_name="consumption"
related_name="consumption_list", related_query_name="consumption") )
amount = models.PositiveSmallIntegerField() amount = models.PositiveSmallIntegerField()
day = models.PositiveSmallIntegerField(choices=[(1, "Do"), (2, "Fr"), (3, "Sa"), (4, "So")]) day = models.PositiveSmallIntegerField(choices=[(1, "Do"), (2, "Fr"), (3, "Sa"), (4, "So")])
for_free = models.BooleanField(default=False) for_free = models.BooleanField(default=False)

View File

@@ -14,9 +14,11 @@ class GaehsnitzTemplateView(TemplateView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
delta_til_start = festival_start_date - date.today() delta_til_start = festival_start_date - date.today()
days_til_start = max(delta_til_start.days, 0) days_til_start = max(delta_til_start.days, 0)
context.update({ context.update(
"days_til_festival_start": days_til_start, {
}) "days_til_festival_start": days_til_start,
}
)
return context return context
@@ -55,12 +57,14 @@ class FinanceView(GaehsnitzTemplateView):
displayed_payments.append(Payment(purpose="Bands", amount=band_sum)) displayed_payments.append(Payment(purpose="Bands", amount=band_sum))
context.update({ context.update(
"total_donations": total_donations, {
"total_payments": total_payments, "total_donations": total_donations,
"total_balance": total_balance, "total_payments": total_payments,
"payments": displayed_payments, "total_balance": total_balance,
}) "payments": displayed_payments,
}
)
return context return context

View File

@@ -2,6 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gaehsnitzproject.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gaehsnitzproject.settings")
application = get_wsgi_application() application = get_wsgi_application()

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
"""Django's command-line utility for administrative tasks.""" """Django's command-line utility for administrative tasks."""
import os import os
import sys import sys
def main(): def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gaehsnitzproject.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gaehsnitzproject.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
@@ -17,5 +18,5 @@ def main():
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

21
pyproject.toml Normal file
View File

@@ -0,0 +1,21 @@
[project]
name = "gaehsnitz"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
"django==6.0.3",
"gunicorn==25.1.0",
"psycopg[binary]==3.3.3",
]
[dependency-groups]
dev = [
"ruff==0.15.7",
]
[tool.ruff]
target-version = "py314"
line-length = 120
[tool.ruff.lint]
select = ["E", "F", "W", "I"]

View File

@@ -1,7 +1,4 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", $schema: "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ extends: ["config:recommended", ":configMigration"],
"config:recommended",
":configMigration"
]
} }

View File

@@ -1,3 +0,0 @@
django==6.0.1
gunicorn==25.0.0
psycopg[binary]==3.3.2