M1: Grundgerüst, Datenmodell und Admin

Django-5.2-Projekt mit uv, django-environ und Docker-Compose-Setup
(web + Postgres, WeasyPrint-Systembibliotheken im Image). Vollständiges
Datenmodell (Firma, Therapeut, Auftraggeber, Teilnehmer, Leistungsart,
Leistung, InvoiceLog, InvoiceCounter) mit Admin, Login-Flow und
Platzhalter-Dashboard. Fortschritt in FORTSCHRITT.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yy3uodsai6Ytt3GJ7tBdz
This commit is contained in:
Claude
2026-07-10 23:53:56 +00:00
parent 7b60be2933
commit cfa2266dd9
31 changed files with 2044 additions and 0 deletions

View File

@@ -0,0 +1,493 @@
# Generated by Django 5.2.16 on 2026-07-10 22:50
import django.core.validators
import django.db.models.deletion
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Auftraggeber",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=200, verbose_name="Name")),
(
"typ",
models.CharField(
choices=[
("AMT", "Amt / Behörde"),
("FIRMA", "Firma"),
("PRIVAT", "Privatzahler"),
],
default="AMT",
max_length=10,
verbose_name="Typ",
),
),
(
"strasse",
models.CharField(
max_length=200, verbose_name="Straße & Hausnummer"
),
),
("plz", models.CharField(max_length=10, verbose_name="PLZ")),
("ort", models.CharField(max_length=100, verbose_name="Ort")),
(
"land",
models.CharField(
default="DE", max_length=2, verbose_name="Land (ISO)"
),
),
(
"ust_idnr",
models.CharField(
blank=True, max_length=20, verbose_name="USt-IdNr."
),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="E-Mail"
),
),
(
"leitweg_id",
models.CharField(
blank=True,
help_text="Nur für Behörden mit XRechnung-Pflicht.",
max_length=50,
verbose_name="Leitweg-ID",
),
),
],
options={
"verbose_name": "Auftraggeber",
"verbose_name_plural": "Auftraggeber",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="Firma",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=200, verbose_name="Firmenname")),
(
"strasse",
models.CharField(
max_length=200, verbose_name="Straße & Hausnummer"
),
),
("plz", models.CharField(max_length=10, verbose_name="PLZ")),
("ort", models.CharField(max_length=100, verbose_name="Ort")),
(
"land",
models.CharField(
default="DE", max_length=2, verbose_name="Land (ISO)"
),
),
(
"ust_idnr",
models.CharField(
blank=True, max_length=20, verbose_name="USt-IdNr."
),
),
(
"steuernummer",
models.CharField(
blank=True, max_length=30, verbose_name="Steuernummer"
),
),
(
"iban",
models.CharField(blank=True, max_length=34, verbose_name="IBAN"),
),
(
"bic",
models.CharField(blank=True, max_length=11, verbose_name="BIC"),
),
(
"bank",
models.CharField(blank=True, max_length=100, verbose_name="Bank"),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="E-Mail"
),
),
(
"telefon",
models.CharField(blank=True, max_length=40, verbose_name="Telefon"),
),
(
"logo",
models.ImageField(
blank=True, null=True, upload_to="firma/", verbose_name="Logo"
),
),
(
"ust_befreiungsgrund",
models.CharField(
default="Steuerfrei gemäß § 4 UStG",
help_text="Erscheint als Befreiungsgrund auf Rechnung und im ZUGFeRD-XML.",
max_length=255,
verbose_name="USt-Befreiungsgrund",
),
),
],
options={
"verbose_name": "Firma",
"verbose_name_plural": "Firma",
},
),
migrations.CreateModel(
name="InvoiceCounter",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"praefix",
models.CharField(max_length=20, unique=True, verbose_name="Präfix"),
),
(
"letzter_wert",
models.PositiveIntegerField(
default=0, verbose_name="letzter vergebener Wert"
),
),
],
options={
"verbose_name": "Nummernkreis-Zähler",
"verbose_name_plural": "Nummernkreis-Zähler",
},
),
migrations.CreateModel(
name="Leistungsart",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"bezeichnung",
models.CharField(
max_length=100, unique=True, verbose_name="Bezeichnung"
),
),
(
"standard_stundensatz",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=8,
null=True,
validators=[
django.core.validators.MinValueValidator(Decimal("0"))
],
verbose_name="Standard-Stundensatz (€)",
),
),
("aktiv", models.BooleanField(default=True, verbose_name="aktiv")),
],
options={
"verbose_name": "Leistungsart",
"verbose_name_plural": "Leistungsarten",
"ordering": ["bezeichnung"],
},
),
migrations.CreateModel(
name="Therapeut",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=150, verbose_name="Name")),
(
"kuerzel",
models.CharField(max_length=10, unique=True, verbose_name="Kürzel"),
),
("aktiv", models.BooleanField(default=True, verbose_name="aktiv")),
],
options={
"verbose_name": "Therapeut",
"verbose_name_plural": "Therapeuten",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="InvoiceLog",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"rechnungsnummer",
models.CharField(
max_length=30, unique=True, verbose_name="Rechnungsnummer"
),
),
("datum", models.DateField(verbose_name="Rechnungsdatum")),
(
"gesamtbetrag",
models.DecimalField(
decimal_places=2, max_digits=12, verbose_name="Gesamtbetrag (€)"
),
),
(
"status",
models.CharField(
choices=[("GUELTIG", "Gültig"), ("STORNIERT", "Storniert")],
default="GUELTIG",
max_length=10,
verbose_name="Status",
),
),
(
"typ",
models.CharField(
choices=[("RECHNUNG", "Rechnung"), ("STORNO", "Storno")],
default="RECHNUNG",
max_length=10,
verbose_name="Typ",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="erstellt am"),
),
(
"auftraggeber",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="rechnungen",
to="abrechnung.auftraggeber",
verbose_name="Auftraggeber",
),
),
(
"created_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="erstellte_rechnungen",
to=settings.AUTH_USER_MODEL,
verbose_name="erstellt von",
),
),
(
"storno_von",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="storno_belege",
to="abrechnung.invoicelog",
verbose_name="Storno von",
),
),
],
options={
"verbose_name": "Rechnungsjournal-Eintrag",
"verbose_name_plural": "Rechnungsjournal",
"ordering": ["-datum", "-id"],
},
),
migrations.CreateModel(
name="Teilnehmer",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=200, verbose_name="Name")),
(
"aktenzeichen",
models.CharField(
blank=True, max_length=50, verbose_name="Aktenzeichen"
),
),
(
"geburtsdatum",
models.DateField(
blank=True, null=True, verbose_name="Geburtsdatum"
),
),
("aktiv", models.BooleanField(default=True, verbose_name="aktiv")),
(
"auftraggeber",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="teilnehmer",
to="abrechnung.auftraggeber",
verbose_name="Auftraggeber",
),
),
(
"therapeut",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="teilnehmer",
to="abrechnung.therapeut",
verbose_name="Zugeordneter Therapeut",
),
),
],
options={
"verbose_name": "Teilnehmer",
"verbose_name_plural": "Teilnehmer",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="Leistung",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("datum", models.DateField(verbose_name="Datum")),
(
"stundenanzahl",
models.DecimalField(
decimal_places=2,
max_digits=6,
validators=[
django.core.validators.MinValueValidator(Decimal("0"))
],
verbose_name="Stunden",
),
),
(
"stundensatz",
models.DecimalField(
decimal_places=2,
max_digits=8,
validators=[
django.core.validators.MinValueValidator(Decimal("0"))
],
verbose_name="Stundensatz (€)",
),
),
(
"beschreibung",
models.CharField(
blank=True, max_length=255, verbose_name="Beschreibung"
),
),
(
"abgerechnet_mit",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="leistungen",
to="abrechnung.invoicelog",
verbose_name="abgerechnet mit",
),
),
(
"leistungsart",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="leistungen",
to="abrechnung.leistungsart",
verbose_name="Leistungsart",
),
),
(
"teilnehmer",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="leistungen",
to="abrechnung.teilnehmer",
verbose_name="Teilnehmer",
),
),
(
"therapeut",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="leistungen",
to="abrechnung.therapeut",
verbose_name="Therapeut",
),
),
],
options={
"verbose_name": "Leistung",
"verbose_name_plural": "Leistungen",
"ordering": ["datum", "id"],
"indexes": [
models.Index(
fields=["therapeut", "datum"],
name="abrechnung__therape_3934a3_idx",
),
models.Index(
fields=["abgerechnet_mit"],
name="abrechnung__abgerec_00377c_idx",
),
],
},
),
]

View File