Initial commit: PESCO NCR system
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>
This commit is contained in:
84
backend/app/domain.py
Normal file
84
backend/app/domain.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Domain constants: roles, workflow stages, and the transition map.
|
||||
|
||||
Workflow note: an NCR in the "New Request" stage is awaiting Initial
|
||||
Disposition — the initial-disposition review is the action a Disposition
|
||||
Authority performs on a New Request, and it moves the NCR either to
|
||||
Secondary Disposition (when secondary review is required) or straight to
|
||||
Operations. All transitions are validated server-side against
|
||||
ALLOWED_TRANSITIONS; anything else is rejected with HTTP 409.
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Role(str, Enum):
|
||||
REQUESTER = "requester"
|
||||
DISPOSITION_AUTHORITY = "disposition_authority"
|
||||
SECONDARY_DISPOSITION_AUTHORITY = "secondary_disposition_authority"
|
||||
OPERATIONS = "operations"
|
||||
QC_INSPECTOR = "qc_inspector"
|
||||
COSTING = "costing"
|
||||
ADMIN = "admin"
|
||||
|
||||
|
||||
ALL_ROLES: set[str] = {r.value for r in Role}
|
||||
|
||||
ROLE_LABELS: dict[str, str] = {
|
||||
Role.REQUESTER: "Requester",
|
||||
Role.DISPOSITION_AUTHORITY: "Disposition Authority",
|
||||
Role.SECONDARY_DISPOSITION_AUTHORITY: "Secondary Disposition Authority",
|
||||
Role.OPERATIONS: "Operations",
|
||||
Role.QC_INSPECTOR: "QC Inspector",
|
||||
Role.COSTING: "Costing",
|
||||
Role.ADMIN: "Admin",
|
||||
}
|
||||
|
||||
|
||||
class Stage(str, Enum):
|
||||
NEW_REQUEST = "new_request"
|
||||
SECONDARY_DISPOSITION = "secondary_disposition"
|
||||
OPERATIONS = "operations"
|
||||
QC_INSPECTION = "qc_inspection"
|
||||
COSTING = "costing"
|
||||
CLOSED = "closed"
|
||||
|
||||
|
||||
STAGE_LABELS: dict[str, str] = {
|
||||
Stage.NEW_REQUEST: "New Request",
|
||||
Stage.SECONDARY_DISPOSITION: "Secondary Disposition",
|
||||
Stage.OPERATIONS: "Operations",
|
||||
Stage.QC_INSPECTION: "QC Inspection",
|
||||
Stage.COSTING: "Costing",
|
||||
Stage.CLOSED: "Closed",
|
||||
}
|
||||
|
||||
# Stages an admin may reopen a closed NCR back into.
|
||||
REOPEN_TARGET_STAGES: list[Stage] = [
|
||||
Stage.NEW_REQUEST,
|
||||
Stage.SECONDARY_DISPOSITION,
|
||||
Stage.OPERATIONS,
|
||||
Stage.QC_INSPECTION,
|
||||
Stage.COSTING,
|
||||
]
|
||||
|
||||
ALLOWED_TRANSITIONS: dict[Stage, set[Stage]] = {
|
||||
Stage.NEW_REQUEST: {Stage.SECONDARY_DISPOSITION, Stage.OPERATIONS},
|
||||
Stage.SECONDARY_DISPOSITION: {Stage.OPERATIONS},
|
||||
Stage.OPERATIONS: {Stage.QC_INSPECTION},
|
||||
Stage.QC_INSPECTION: {Stage.COSTING},
|
||||
Stage.COSTING: {Stage.CLOSED},
|
||||
# Reopen (admin only, reason required) — enforced separately.
|
||||
Stage.CLOSED: set(REOPEN_TARGET_STAGES),
|
||||
}
|
||||
|
||||
# Role allowed to act on the NCR in each stage (Admin is always allowed;
|
||||
# Secondary Disposition additionally requires being an assigned authority).
|
||||
STAGE_ACTING_ROLE: dict[Stage, Role] = {
|
||||
Stage.NEW_REQUEST: Role.DISPOSITION_AUTHORITY,
|
||||
Stage.SECONDARY_DISPOSITION: Role.SECONDARY_DISPOSITION_AUTHORITY,
|
||||
Stage.OPERATIONS: Role.OPERATIONS,
|
||||
Stage.QC_INSPECTION: Role.QC_INSPECTOR,
|
||||
Stage.COSTING: Role.COSTING,
|
||||
}
|
||||
|
||||
# Owners to notify when an admin reopens an NCR into a given stage.
|
||||
STAGE_OWNER_ROLE: dict[Stage, Role] = STAGE_ACTING_ROLE
|
||||
Reference in New Issue
Block a user