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:
ang3l12
2026-07-13 11:41:22 -06:00
commit dea316b113
111 changed files with 13817 additions and 0 deletions

87
docker-compose.yml Normal file
View File

@@ -0,0 +1,87 @@
name: pesco-ncr
services:
mysql:
image: mysql:8.4
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE:-pesco_ncr}
MYSQL_USER: ${MYSQL_USER:-ncr_app}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
POWERBI_RO_PASSWORD: ${POWERBI_RO_PASSWORD}
ports:
# Published so the on-prem Power BI gateway can reach the reporting views.
# Remove or firewall this mapping if external reporting access is not needed.
- "${MYSQL_PUBLISHED_PORT:-3306}:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./db/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root -p$$MYSQL_ROOT_PASSWORD --silent"]
interval: 5s
timeout: 5s
retries: 30
start_period: 40s
restart: unless-stopped
api:
build: ./backend
environment:
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8080}
LOG_LEVEL: ${LOG_LEVEL:-INFO}
AUTH_MODE: ${AUTH_MODE:-entra}
ENTRA_TENANT_ID: ${ENTRA_TENANT_ID:-}
ENTRA_CLIENT_ID: ${ENTRA_CLIENT_ID:-}
ENTRA_CLIENT_SECRET: ${ENTRA_CLIENT_SECRET:-}
ENTRA_ALLOWED_GROUP_ID: ${ENTRA_ALLOWED_GROUP_ID:-}
ENTRA_API_AUDIENCE: ${ENTRA_API_AUDIENCE:-}
INITIAL_ADMIN_EMAILS: ${INITIAL_ADMIN_EMAILS:-}
MYSQL_HOST: mysql
MYSQL_PORT: 3306
MYSQL_DATABASE: ${MYSQL_DATABASE:-pesco_ncr}
MYSQL_USER: ${MYSQL_USER:-ncr_app}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ATTACHMENTS_DIR: ${ATTACHMENTS_DIR:-/data/attachments}
MAX_UPLOAD_MB: ${MAX_UPLOAD_MB:-25}
NOTIFICATIONS_ENABLED_DEFAULT: ${NOTIFICATIONS_ENABLED_DEFAULT:-true}
JOB_LOOKUP_PROVIDER: ${JOB_LOOKUP_PROVIDER:-null}
VISUAL_DB_HOST: ${VISUAL_DB_HOST:-}
VISUAL_DB_PORT: ${VISUAL_DB_PORT:-1433}
VISUAL_DB_NAME: ${VISUAL_DB_NAME:-}
VISUAL_DB_USER: ${VISUAL_DB_USER:-}
VISUAL_DB_PASSWORD: ${VISUAL_DB_PASSWORD:-}
VISUAL_SITE_ID: ${VISUAL_SITE_ID:-}
SEED_DEMO_DATA: ${SEED_DEMO_DATA:-false}
volumes:
- attachments_data:/data/attachments
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8000/api/health"]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
restart: unless-stopped
frontend:
build: ./frontend
ports:
- "${HTTP_PORT:-8080}:80"
environment:
AUTH_MODE: ${AUTH_MODE:-entra}
ENTRA_TENANT_ID: ${ENTRA_TENANT_ID:-}
ENTRA_CLIENT_ID: ${ENTRA_CLIENT_ID:-}
ENTRA_API_SCOPE: ${ENTRA_API_SCOPE:-}
depends_on:
api:
condition: service_healthy
restart: unless-stopped
volumes:
mysql_data:
attachments_data: