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>
This commit is contained in:
Martin Hohenberg
2026-06-18 22:40:52 +02:00
parent 4a3c370317
commit bc6e06a2e2
4 changed files with 120 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ from app.models.printer_type import PrinterType
from app.models.pricing import PricingConfig
from app.models.todo import Todo
from app.constants import COLORS, MATERIALS
from app.estimation import estimate
from app.filamentdb import available_colors, available_materials, get_stock, spool_counts
from app.stl import get_stl_dir, list_stl_files
from app.stl_analysis import analyse, get_cache_map # noqa: F401 used in job_detail route
@@ -247,11 +248,13 @@ def job_detail(job_id: int, request: Request, db: Session = Depends(get_db)):
if not job:
raise HTTPException(status_code=404, detail="Job not found")
stl = db.query(StlCache).filter_by(filename=job.file_name).first() if job.file_name else None
estimation = estimate(stl.volume_ccm, job.job_material) if stl and stl.volume_ccm else None
return templates.TemplateResponse("job_detail.html", {
"request": request,
"active": "jobs",
"job": job,
"stl": stl,
"estimation": estimation,
"printers": db.query(Printer).order_by(Printer.name).all(),
"brackets": db.query(JobBracket).order_by(JobBracket.name).all(),
"materials": available_materials() or MATERIALS,