#!/usr/bin/env python3 """Erzeugt die Vorlagen-Excel 3D-Druck-Kostenkalkulator.xlsx (ohne Makros). Reiter: - Eingabe: alle 18 Eingabefelder - Kalkulation: Formeln fuer alle 12 Berechnungen (Zellbezuege zu Eingabe) - Angebot: einfache Druck-Ansicht Ausfuehrung: python3 scripts/generate-template.py """ from pathlib import Path from openpyxl import Workbook from openpyxl.styles import Alignment, Font, PatternFill from openpyxl.utils import get_column_letter OUT = Path(__file__).resolve().parent.parent / "templates" / "3D-Druck-Kostenkalkulator.xlsx" HEADER_FONT = Font(bold=True, color="FFFFFF", size=12) HEADER_FILL = PatternFill("solid", fgColor="1D1D1F") LABEL_FONT = Font(size=11) VALUE_FONT = Font(size=11, bold=False) RESULT_FONT = Font(size=12, bold=True) PRIMARY_FILL = PatternFill("solid", fgColor="0071E3") SUBTLE_FILL = PatternFill("solid", fgColor="F5F5F7") EUR = '"€"#,##0.00' PCT = '0.0"%"' def style_header(ws, row, cols): for c in range(1, cols + 1): cell = ws.cell(row=row, column=c) cell.font = HEADER_FONT cell.fill = HEADER_FILL cell.alignment = Alignment(horizontal="left", vertical="center") def build_input(wb) -> None: ws = wb.create_sheet("Eingabe") ws.column_dimensions["A"].width = 42 ws.column_dimensions["B"].width = 20 ws.column_dimensions["C"].width = 16 ws["A1"] = "Feld" ws["B1"] = "Wert" ws["C1"] = "Einheit" style_header(ws, 1, 3) rows = [ ("Projektname", "Beispiel-Projekt", ""), ("Kunde / Auftrag", "", ""), ("Materialtyp", "PLA", ""), ("Materialkosten pro kg", 25.00, "EUR/kg"), ("Materialverbrauch", 120, "g"), ("Druckzeit", 5.5, "h"), ("Maschinenstundensatz", 3.00, "EUR/h"), ("Stromverbrauch", 0.15, "kWh"), ("Strompreis", 0.35, "EUR/kWh"), ("Nachbearbeitungszeit", 15, "min"), ("Nachbearbeitungs-Stundensatz", 30.00, "EUR/h"), ("Verpackungskosten", 1.00, "EUR"), ("Versandkosten", 4.50, "EUR"), ("Ruestkosten", 2.00, "EUR"), ("Ausschussrisiko", 5, "%"), ("Gewinnaufschlag", 30, "%"), ("Stueckzahl", 1, "Stueck"), ("Individueller Zuschlag/Rabatt", 0, "EUR"), ("MwSt", 19, "%"), ("Notizen", "", ""), ] for i, (label, value, unit) in enumerate(rows, start=2): ws.cell(row=i, column=1, value=label).font = LABEL_FONT ws.cell(row=i, column=2, value=value).font = VALUE_FONT ws.cell(row=i, column=3, value=unit).font = LABEL_FONT if i % 2 == 0: for c in range(1, 4): ws.cell(row=i, column=c).fill = SUBTLE_FILL # Formate ws["B5"].number_format = EUR # Materialkosten/kg ws["B8"].number_format = EUR # Maschinenstundensatz ws["B10"].number_format = EUR # Strompreis ws["B12"].number_format = EUR # Nachbearb. Satz ws["B13"].number_format = EUR # Verpackung ws["B14"].number_format = EUR # Versand ws["B15"].number_format = EUR # Ruestkosten ws["B19"].number_format = EUR # Zuschlag/Rabatt ws["B16"].number_format = PCT # Ausschuss ws["B17"].number_format = PCT # Gewinnaufschlag ws["B20"].number_format = PCT # MwSt def build_calc(wb) -> None: """Formeln referenzieren Eingabe!B-Spalten. Eingabe-Zuordnung (row in Eingabe): B5 materialCostPerKg B6 materialUsageG B7 printTimeH B8 machineRate B9 powerKwh B10 powerPrice B11 postMin B12 postRate B13 packagingCost B14 shippingCost B15 setupCost B16 scrapPct (%) B17 marginPct (%) B18 quantity B19 individualAdjustment B20 vatPct (%) """ ws = wb.create_sheet("Kalkulation") ws.column_dimensions["A"].width = 6 ws.column_dimensions["B"].width = 40 ws.column_dimensions["C"].width = 22 ws["A1"] = "#" ws["B1"] = "Position" ws["C1"] = "Betrag (EUR)" style_header(ws, 1, 3) formulas = [ ("1", "Materialkosten", "=Eingabe!B6*(Eingabe!B5/1000)"), ("2", "Maschinenkosten", "=Eingabe!B7*Eingabe!B8"), ("3", "Energiekosten", "=Eingabe!B7*Eingabe!B9*Eingabe!B10"), ("4", "Nachbearbeitungskosten", "=(Eingabe!B11/60)*Eingabe!B12"), ("5", "Gesamtherstellungskosten", "=C2+C3+C4+C5+Eingabe!B15+Eingabe!B13+Eingabe!B14"), ("6", "Ausschuss-Zuschlag", "=C6*(Eingabe!B16/100)"), ("7", "Zwischensumme netto", "=C6+C7"), ("8", "Marge", "=C8*(Eingabe!B17/100)"), ("9", "Kundenpreis netto", "=C8+C9+Eingabe!B19"), ("10", "Stueckpreis netto", "=C10/MAX(Eingabe!B18,1)"), ("11", "Stueckpreis brutto", "=C11*(1+Eingabe!B20/100)"), ("12", "Gesamtpreis brutto", "=C12*MAX(Eingabe!B18,1)"), ] for i, (num, label, formula) in enumerate(formulas, start=2): ws.cell(row=i, column=1, value=num).font = LABEL_FONT ws.cell(row=i, column=2, value=label).font = LABEL_FONT cell = ws.cell(row=i, column=3, value=formula) cell.font = VALUE_FONT cell.number_format = EUR if i % 2 == 0: for c in range(1, 4): ws.cell(row=i, column=c).fill = SUBTLE_FILL # Finale Hervorhebung fuer Stueckpreis brutto (Zeile 12 -> #11) for c in range(1, 4): ws.cell(row=12, column=c).fill = PRIMARY_FILL ws.cell(row=12, column=c).font = Font(bold=True, color="FFFFFF", size=12) def build_offer(wb) -> None: ws = wb.create_sheet("Angebot") for col, width in zip("ABCD", (32, 12, 22, 16)): ws.column_dimensions[col].width = width ws["A1"] = "ANGEBOT" ws["A1"].font = Font(bold=True, size=20) ws.merge_cells("A1:D1") ws["A3"] = "Projekt" ws["B3"] = "=Eingabe!B2" ws["A4"] = "Kunde" ws["B4"] = "=Eingabe!B3" ws["A5"] = "Datum" ws["B5"] = "=TEXT(TODAY(),\"DD.MM.YYYY\")" for r in (3, 4, 5): ws.cell(row=r, column=1).font = Font(bold=True) ws["A7"] = "Position" ws["B7"] = "Menge" ws["C7"] = "Stueckpreis brutto" ws["D7"] = "Gesamt" style_header(ws, 7, 4) ws["A8"] = "=Eingabe!B2" ws["B8"] = "=Eingabe!B18" ws["C8"] = "=Kalkulation!C12" ws["C8"].number_format = EUR ws["D8"] = "=Kalkulation!C13" ws["D8"].number_format = EUR ws["A10"] = "Gesamt brutto" ws["A10"].font = Font(bold=True) ws["D10"] = "=D8" ws["D10"].font = Font(bold=True) ws["D10"].number_format = EUR ws["A12"] = "Hinweise" ws["A12"].font = Font(bold=True) ws["A13"] = "=Eingabe!B21" def main() -> None: wb = Workbook() # Default-Sheet entfernen wb.remove(wb.active) build_input(wb) build_calc(wb) build_offer(wb) OUT.parent.mkdir(parents=True, exist_ok=True) wb.save(OUT) print(f"Template geschrieben: {OUT}") if __name__ == "__main__": main()