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>
151 lines
5.1 KiB
Markdown
151 lines
5.1 KiB
Markdown
# 3D-Druck Kostenkalkulation
|
|
|
|
Web-App zur Kalkulation von 3D-Druck-Kosten mit PDF-Rechnung und TXT-Export.
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
## Start
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|
|
- **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
|
|
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
|
|
bash -c "$(curl -fsSL https://gitea.teamthiele.de/ethiele/3ddruckkalkulation/raw/branch/main/proxmox-setup.sh)" - 105 mein-container
|
|
```
|
|
|
|
**Container-Zugriff:**
|
|
|
|
```bash
|
|
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:**
|
|
|
|
```bash
|
|
./proxmox-update.sh
|
|
```
|
|
|
|
**Vom Proxmox-Host aus:**
|
|
|
|
```bash
|
|
./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 | `/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
|