Complete Non-Conformance Report system replacing the PowerApps/SharePoint prototype: FastAPI + SQLAlchemy 2 (async) + Alembic + MySQL 8 backend, React 18 + Vite + TypeScript + MUI frontend, Entra ID auth (MSAL / JWKS, group-gated), Microsoft Graph delegated Mail.Send notifications (OBO), six-stage workflow state machine with server-side enforcement, atomic NCR-YYYY-NNNN numbering, attachments with camera capture, immutable field-level audit trail, admin reopen, reports + CSV export, WeasyPrint PDF traveler, Power BI reporting views + read-only DB user, documented VISUAL ERP job-lookup stub, pytest suite (26 tests), docker-compose deployment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
55 lines
972 B
Python
55 lines
972 B
Python
from decimal import Decimal
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class CountByName(BaseModel):
|
|
name: str
|
|
count: int
|
|
|
|
|
|
class CountByMonth(BaseModel):
|
|
month: str # YYYY-MM
|
|
count: int
|
|
|
|
|
|
class CostByMonth(BaseModel):
|
|
month: str
|
|
labor: Decimal
|
|
material: Decimal
|
|
service: Decimal
|
|
other: Decimal
|
|
total: Decimal
|
|
|
|
|
|
class AgingBucket(BaseModel):
|
|
bucket: str
|
|
count: int
|
|
|
|
|
|
class StageCycleTime(BaseModel):
|
|
stage: str
|
|
stage_label: str
|
|
avg_days: float
|
|
samples: int
|
|
|
|
|
|
class TopJob(BaseModel):
|
|
job_number: str
|
|
count: int
|
|
|
|
|
|
class ReportsSummaryOut(BaseModel):
|
|
total_ncrs: int
|
|
open_ncrs: int
|
|
closed_ncrs: int
|
|
total_cost: Decimal
|
|
by_department: list[CountByName]
|
|
by_category: list[CountByName]
|
|
by_month: list[CountByMonth]
|
|
cost_over_time: list[CostByMonth]
|
|
aging: list[AgingBucket]
|
|
cycle_times: list[StageCycleTime]
|
|
end_to_end_avg_days: float | None
|
|
top_jobs: list[TopJob]
|