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
494 lines
17 KiB
Python
494 lines
17 KiB
Python
# 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",
|
|
),
|
|
],
|
|
},
|
|
),
|
|
]
|