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:
493
abrechnung/migrations/0001_initial.py
Normal file
493
abrechnung/migrations/0001_initial.py
Normal 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",
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user