Files
POPS/app/estimation.py
Martin Hohenberg bc6e06a2e2 Add cost/time estimate card to job detail page
app/estimation.py:
  - 20% infill → material volume → weight (density per material)
  - print time at 20 g/h (conservative FDM average)
  - filament cost at FILAMENT_PRICE_PER_KG EUR/kg (env, default 15)
  - +20% misprint buffer, +30% gross profit margin
  - net price (ex. VAT) and gross price (VAT_RATE %, env, default 19)

Card shows three columns: Material & Time / Production cost / Suggested price.
Only rendered when STL volume is available. Assumptions shown in card header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:40:52 +02:00

56 lines
1.8 KiB
Python

"""Job cost and time estimation from STL geometry."""
import os
# g/cm³ per material — used to convert volume → weight
_DENSITY: dict[str, float] = {
"PLA": 1.24,
"PETG": 1.27,
"ABS": 1.04,
"ASA": 1.07,
"TPU": 1.20,
"Nylon": 1.14,
"PC": 1.20,
}
_DEFAULT_DENSITY = 1.24
# Conservative average FDM print speed for weight-based time estimate
_PRINT_SPEED_G_PER_HOUR = 20.0
def estimate(volume_ccm: float, material: str | None) -> dict:
"""
Returns a cost/time estimate dict for one job.
Assumptions:
- 20 % infill → material used = volume * 0.20
- filament density from _DENSITY (default 1.24 g/cm³)
- print speed: 20 g/h (conservative FDM average)
- filament cost: FILAMENT_PRICE_PER_KG EUR / kg (env, default 15)
- misprint buffer: +20 %
- gross profit margin: +30 %
- VAT: VAT_RATE % (env, default 19)
"""
vat_rate = float(os.environ.get("VAT_RATE", 19))
price_per_kg = float(os.environ.get("FILAMENT_PRICE_PER_KG", 15))
density = _DENSITY.get(material or "", _DEFAULT_DENSITY)
material_ccm = volume_ccm * 0.20
weight_g = material_ccm * density
print_minutes = round((weight_g / _PRINT_SPEED_G_PER_HOUR) * 60)
filament_cost = weight_g / 1000 * price_per_kg
after_misprint = filament_cost * 1.20
net_price = after_misprint * 1.30
gross_price = net_price * (1 + vat_rate / 100)
return {
"material_ccm": round(material_ccm, 2),
"weight_g": round(weight_g, 1),
"print_minutes": print_minutes,
"filament_cost": round(filament_cost, 2),
"net_price": round(net_price, 2),
"gross_price": round(gross_price, 2),
"vat_rate": vat_rate,
"price_per_kg": price_per_kg,
}