Auf der Rechnung standen die Daten bisher in ISO-Form (2026-08-22).
- Neuer Helfer datum_de() formatiert nach TT.MM.JJJJ. Er versteht ISO
und bereits deutsche Eingaben und laesst alles Unbekannte unveraendert
statt es zu verstuemmeln.
- Angewendet auf Rechnungs- und Leistungsdatum im PDF sowie auf die
Datumszeile im TXT-Export. Auch als Jinja-Filter registriert.
- Gespeichert wird weiterhin ISO: sortierbar und eindeutig. Nur die
Ausgabe ist deutsch.
- Die beiden Datumsfelder auf der Rechnungsseite sind jetzt
type="date". Der Browser zeigt sie im deutschen Format mit
Kalenderauswahl und liefert intern sauberes ISO zurueck, womit
mehrdeutige Freitexteingaben entfallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Laden eines Projekts blieb der alte Projektname stehen, wenn kurz
zuvor getippt wurde.
Ursache: projektLaden() brach den laufenden Autosave-Timer nicht ab.
Der feuerte 500 ms nach dem letzten Tastendruck, las die noch alten
Formularfelder aus und schrieb sie ueber den gerade geladenen Stand.
Anschliessend folgte ein location.reload(), der den ueberschriebenen
Stand anzeigte. Auf localhost war das Zeitfenster meist zu klein, ueber
LAN gegen den Container dagegen zuverlaessig zu treffen.
- projektLaden() und projektNeu() brechen saveTimeout und calcTimeout ab.
- /projekte/laden und /projekte/neu liefern den neuen Stand zurueck,
das Frontend setzt die Felder direkt (neue Funktion standUebernehmen).
- Kein location.reload() mehr. Das beseitigt zusaetzlich die
Formular-Wiederherstellung, mit der Firefox und Safari von Hand
geaenderte Felder nach einem Reload zurueckholen -- betroffen waere
genau das Projektnamen-Feld gewesen.
Reproduziert und gegengeprueft: mit der alten Reihenfolge schreibt der
Autosave "Projekt B" ueber das geladene "Projekt A"; mit dem Fix bleiben
Feld und Serverstand ueber mehrere Wechsel hinweg stabil.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher liess sich ein neuer Auftrag nur von Hand beginnen: Namen
ueberschreiben, jede Position neu ausfuellen und auf der Rechnungsseite
Kundendaten und Rechnungsnummer leeren. Wurde der letzte Schritt
vergessen, stand der alte Kunde auf der neuen Rechnung.
- POST /projekte/neu setzt nur PROJEKT_FELDER zurueck (Name, Positionen,
Kundendaten, Rechnungsnummer, Datum auf heute). Betriebswerte wie
Marge, Stundenlohn, Drucker, Steuer und Firmendaten bleiben.
- GET /projekte liefert zusaetzlich 'ungespeichert'. Der Knopf fragt nur
nach, wenn der offene Stand wirklich nicht als Projekt gesichert ist.
- Der Vergleich ignoriert 'datum' und 'geaendert', sonst waere ein
gespeichertes Projekt am naechsten Tag faelschlich "ungespeichert".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Zahlungsziel ist eine Geschaeftsbedingung. Auf einem Privatbeleg hat
es nichts zu suchen -- dort wird der Betrag ueblicherweise direkt
beglichen.
ZAHLUNGSZIEL und DANK sind jetzt getrennte Konstanten. Das Zahlungsziel
erscheint nur bei 'kleinunternehmer' und 'regel', der Dank in allen drei
Regimen. Gilt fuer PDF und Rechnungsvorschau.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher liess sich pro Rechnung nur ein Teil kalkulieren. Ein Objekt aus
mehreren Teilen musste in getrennten Rechnungen abgebildet werden.
Positionen:
- Die Kalkulation arbeitet mit einer Positionsliste statt einem
Einzelteil. Bezeichnung, Material, Preis/kg, Druckzeit, Arbeitszeit
und Stueckzahl gelten je Position -- verschiedene Teile nutzen oft
unterschiedliche Filamente und brauchen unterschiedlich viel Nacharbeit.
- Drucker, Strom, Stundenlohn, Marge und Steuer bleiben auftragsweit.
- Jede Position wird eine eigene Rechnungszeile mit Menge, Einzelpreis
und Zeilensumme. Bei vielen Positionen bricht das PDF auf weitere
Seiten um.
- Die Auftragssumme ist die Summe der bereits gerundeten Zeilen, damit
die Rechnung zeilenweise nachrechenbar bleibt.
- saubere_positionen() normalisiert jede Liste aus Config oder Client
(Typen, Wertebereiche, Obergrenze 50); es bleibt immer mindestens
eine Position bestehen.
- Alte Configs mit den Flachfeldern werden in eine einzelne Position
migriert, die Altfelder entfernt.
- sammlePositionen() liefert null auf Seiten ohne Positionsliste, damit
ein Tastendruck auf der Einstellungs- oder Rechnungsseite die
Positionen nicht loescht.
Projekte:
- Benannte Kalkulationen lassen sich speichern und wieder laden,
Ablage in druckkalkulation_projekte.yaml (ein Mapping fuer alle
Projekte, Schluessel ist der Projektname).
- Gesichert werden nur PROJEKT_FELDER: Name, Positionen, Kundendaten,
Rechnungsnummer, Rechnungs- und Leistungsdatum. Betriebswerte gehoeren
bewusst nicht dazu -- sonst wuerde das Laden eines alten Projekts den
aktuellen Stundenlohn oder das Steuerregime ueberschreiben.
- Neue Routen: GET /projekte sowie POST /projekte/{speichern,laden,loeschen}.
- Eigenes Lock, gleiche atomare Schreibtechnik wie die Config.
- Ueberschreiben, Laden bei ungespeicherten Aenderungen und Loeschen
fragen jeweils nach.
- Der offene Autosave-Debounce wird vor dem Speichern durchgedrueckt,
damit wirklich der aktuelle Stand ins Projekt geht.
Die beiden Teile stehen in einem Commit, weil die Aenderungen in app.py,
script.js und index.html ineinandergreifen und ein aufgetrennter
Zwischenstand nicht als solcher getestet wurde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sicherheit:
- debug=True aus app.run() entfernt. Der Werkzeug-Debugger erlaubt
Codeausfuehrung ueber das Netz und lief bisher im LXC-Container mit.
Neu ueber FLASK_DEBUG/HOST/PORT steuerbar; mit FLASK_DEBUG=1 bindet
die App automatisch nur an 127.0.0.1.
- Wirkungsloses --no-reload aus dem systemd-ExecStart entfernt
(app.run() wertet sys.argv nicht aus).
- /save_config uebernimmt nur noch Keys aus DEFAULT_VALUES.
PDF:
- pdf_text() transliteriert Zeichen, die die latin-1-Kernfonts nicht
koennen (Gedankenstrich, typografische Anfuehrungszeichen, Euro-Zeichen).
Vorher warf jeder solche Kundenname einen UnicodeEncodeError -> HTTP 500.
- Legacy-fpdf2-API abgeloest: ln=True -> new_x/new_y, output(dest='S')
-> output(), Font "Arial" -> "Helvetica". Alle drei waren als
"will soon be removed" markiert.
Kalkulation:
- Stueckzahl wirkt jetzt in der Hauptansicht. Vorher zeigte "GESAMT"
den Preis pro Stueck, obwohl die Stueckzahl direkt darueber eingegeben
wurde. Anzeige jetzt getrennt nach PRO STUECK und AUFTRAG.
- Rundung auf Cent-Basis des Stueckpreises, damit die Rechnungszeile
nachrechenbar bleibt (Einzelpreis x Menge == Zeilensumme).
- as_stueckzahl() faengt '', '1.5' und 0 ab (vorher ValueError -> 500).
- Gewinnmarge im Ergebnis kommt vom Server; das Label zeigte auf der
Startseite immer 20 %, weil es ein nicht vorhandenes Feld auslas.
Besteuerung (drei Regime statt eines Bool-Schalters):
- 'privat' - kein Unternehmer nach §2 UStG: weder MwSt. noch
Steuernummer noch §19-Hinweis auf dem Beleg
- 'kleinunternehmer' - §19 UStG mit Pflichthinweis
- 'regel' - MwSt. mit konfigurierbarem Satz (19 % / 7 %)
Steuernummer, USt-IdNr. und Leistungsdatum (§14 UStG) ergaenzt; sie
erscheinen nur in den beiden Unternehmer-Modi, unabhaengig davon, ob
die Felder in der Config gefuellt sind.
Config-Handling:
- save_config() schreibt atomar (tmp + os.replace + fsync), ein Lock
serialisiert die Read-modify-write-Zyklen.
- load_config() wirft ConfigError bei beschaedigter YAML, statt still
auf Defaults zu fallen -- sonst haette der naechste Autosave die
Datei endgueltig ueberschrieben.
- _migriere() leitet steuerregime aus dem alten mwst_ausweisen ab und
laeuft vor dem Auffuellen mit Defaults sowie nach jedem Speichern.
Frontend:
- Dark-Mode-Toggle schrieb "Light"/"Dark" als Textknoten in den
Icon-Button und liess das Icon in der falschen Richtung stehen.
- recalculate() steigt auf Seiten ohne Ergebnisblock aus; auf der
Einstellungsseite warf bisher jeder Tastendruck eine TypeError.
- Live-Berechnung entprellt (150 ms) statt ein Request pro Tastendruck.
Sonstiges:
- requirements.txt mit gepinnten Versionen; beide Proxmox-Skripte
nutzen sie statt loser Paketlisten und ohne blindes --upgrade.
- Falsche Port-Ausgabe in proxmox-setup.sh korrigiert (5000 -> 5001).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Neue /settings-Route mit eigener Seite (Einstellungen für Drucker, Arbeit, Marge)
- Settings-Cards aus index.html entfernt
- /save_config merged mit bestehenden Werten (kein Überschreiben)
- Navigation im Header: Kalkulation ↔ Einstellungen (alle Seiten)
- Port auf 5001 geändert (macOS AirPlay blockiert 5000)