Erik Thiele ab6d02eccb Fix: Ausstehender Autosave ueberschrieb das geladene Projekt
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>
2026-08-22 12:10:50 +02:00

3D-Druck Kostenkalkulation

Web-App zur Kalkulation von 3D-Druck-Kosten mit PDF-Rechnung und TXT-Export.

Setup

source .venv/bin/activate
pip install -r requirements.txt

Start

python app.py

-> http://127.0.0.1:5001

Der Debug-Modus ist standardmaessig aus (der Werkzeug-Debugger erlaubt sonst Codeausfuehrung ueber das Netz). Zum Entwickeln:

FLASK_DEBUG=1 python app.py

Mit FLASK_DEBUG=1 lauscht die App nur auf 127.0.0.1. Host und Port lassen sich ueber HOST und PORT ueberschreiben.

Funktionen

  • Mehrere Positionen pro Auftrag — ein Objekt aus mehreren Teilen wird als Positionsliste kalkuliert und als eigene Rechnungszeilen ausgewiesen. Material, Preis/kg, Druckzeit, Arbeitszeit und Stückzahl gelten je Position; Drucker, Strom, Stundenlohn, Marge und Steuer gelten für den ganzen Auftrag.
  • Neues Projekt — leert Projektname, Positionen, Kundendaten und Rechnungsnummer; warnt vorher, wenn der offene Stand nicht gesichert ist
  • Projekte speichern und laden — benannte Kalkulationen bleiben erhalten und lassen sich jederzeit wieder aufrufen (druckkalkulation_projekte.yaml). Betriebswerte (Drucker, Strom, Stundenlohn, Marge, Steuer) gehören nicht zum Projekt und bleiben beim Laden unangetastet.
  • Live-Berechnung bei jeder Eingabe (Material, Strom, Abschreibung, Arbeit, Gewinn)
  • Dark Mode (persistiert in YAML-Konfiguration)
  • PDF-Rechnung mit Logo, Kundendaten und Positionstabelle (via fpdf2)
  • TXT-Export der Kalkulation
  • Auto-Save aller Werte via AJAX (debounced 500ms)
  • Einstellungsseite für Druckerabschreibung, Arbeitskosten, Stromkosten, Firmendaten, Logo
  • Besteuerung mit drei Modi:
    • Privatverkauf — kein Unternehmer nach §2 UStG: keine MwSt., keine Steuernummer, kein §19-Hinweis, nur „Privatverkauf. Kein Ausweis von Umsatzsteuer."
    • Kleinunternehmer — §19 UStG: keine MwSt., dafür der Pflichthinweis
    • Regelbesteuert — MwSt. mit 19 % oder 7 %
  • Rechnungspflichtangaben nach §14 UStG (Steuernummer/USt-IdNr., Leistungsdatum) — nur in den beiden Unternehmer-Modi
  • Logo-Upload (PNG/JPEG/WebP) für die Rechnung

Installation auf Proxmox (LXC-Container)

Das Skript proxmox-setup.sh erstellt einen Ubuntu-24.04-LXC-Container, klont das Repository, installiert alle Abhangigkeiten und richtet einen systemd-Service ein.

Auf dem Proxmox-Host als root ausfuhren:

bash -c "$(curl -fsSL https://gitea.teamthiele.de/ethiele/3ddruckkalkulation/raw/branch/main/proxmox-setup.sh)"

Nach Abschluss ist die App unter http://<CONTAINER-IP>:5001 erreichbar. Container-ID und Name konnen als Argumente ubergeben werden:

bash -c "$(curl -fsSL https://gitea.teamthiele.de/ethiele/3ddruckkalkulation/raw/branch/main/proxmox-setup.sh)" - 105 mein-container

Container-Zugriff:

pct enter 201                  # Shell im Container
systemctl status 3ddruck-kalkulation.service
journalctl -u 3ddruck-kalkulation.service -f

Update des Containers

proxmox-update.sh aktualisiert den Container via Git und startet den Service neu.

Im Container ausfuhren:

./proxmox-update.sh

Vom Proxmox-Host aus:

./proxmox-update.sh 201

Das Skript holt via curl die aktuelle Version aus dem Gitea-Repo, pullt, installiert ggf. neue Dependencies und restartet den systemd-Service.

Technik

Komponente Technologie
Backend Flask (Python)
Frontend Tabler CSS, Bootstrap 5
Icons Tabler Icons
PDF fpdf2 (importiert als fpdf)
Config YAML (Auto-Save, atomar geschrieben)

Projektstruktur

app.py                         # Flask-Server mit allen Routen
templates/index.html           # Hauptseite (Formular + Ergebnisse)
templates/invoice.html         # Rechnungsseite
templates/settings.html        # Einstellungen
static/style.css               # Minimales Custom-CSS
static/script.js               # Live-Berechnung + Auto-Save
proxmox-setup.sh               # Ersteinrichtung LXC-Container
proxmox-update.sh              # Update LXC-Container
druckkalkulation_config.yaml   # Aktueller Arbeitsstand (ignoriert)
druckkalkulation_projekte.yaml # Gespeicherte Projekte (ignoriert)

Routen

Methode Pfad Beschreibung
GET / Hauptseite mit Formular
POST /calculate JSON-Berechnung aller Positionen (AJAX)
POST /save_config Speichert Werte in YAML
GET /invoice Rechnungsseite
POST /invoice/pdf PDF-Download
POST /export TXT-Download
GET /settings Einstellungsseite
GET /projekte Liste der gespeicherten Projekte (JSON)
POST /projekte/speichern Aktuellen Stand als Projekt sichern
POST /projekte/laden Projekt in den Arbeitsstand laden
POST /projekte/loeschen Projekt entfernen
POST /projekte/neu Arbeitsstand auf leeren Auftrag zurücksetzen
POST /upload_logo Logo hochladen
POST /delete_logo Logo loschen

Hinweise

  • fpdf2 wird als fpdf importiert (alte API, from fpdf import FPDF)
  • Alle UI-Texte sind Deutsch
  • Die YAML-Config wird automatisch erstellt und bei jedem Tastendruck gespeichert
  • druckkalkulation_config.yaml und static/logo.* sind in .gitignore ausgeschlossen
Description
No description provided
Readme 1.1 MiB
Languages
Python 42.3%
HTML 33.3%
JavaScript 18.5%
Shell 5.1%
CSS 0.8%