05/27 Update Claude.md
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Audience:** AI assistants and developers working on this codebase.
|
||||
> **Purpose:** Authoritative reference for architecture, conventions, gotchas, and decisions.
|
||||
> **Last reviewed:** May 2026 (Phase 19 complete + post-phase-19 improvements: security hardening, inspection UX, inspector dashboard widget, bulk verification, scheduled issues digest with SLA grouping, customer read-only issue portal, inspector contract scoping, contract visibility in issues/inspections UI, Contract→Facility cascade filters)
|
||||
> **Last reviewed:** May 2026 (Phase 19 complete + mobile API gap-fill Phases A–E: issue comments, dashboard stats with severity breakdown, area_name/assigned_to_name in issue payload, notification inbox, area_id on issues, CSRF-exempt pattern for all new blueprints)
|
||||
|
||||
---
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
6. [Role & Permission Matrix](#6-role--permission-matrix)
|
||||
7. [Blueprint Prefixes & Route Inventory](#7-blueprint-prefixes--route-inventory)
|
||||
8. [Utility Modules](#8-utility-modules)
|
||||
9. [Mobile API (Phase 7 / Phase A / Phase B / Phase C)](#9-mobile-api-phase-7--phase-a--phase-b--phase-c)
|
||||
9. [Mobile API (Phase 7 / Phase A–E)](#9-mobile-api-phase-7--phase-ae)
|
||||
10. [iPad Native App](#10-ipad-native-app)
|
||||
11. [Notification System](#11-notification-system)
|
||||
12. [SLA Engine](#12-sla-engine)
|
||||
@@ -88,8 +88,10 @@ lt_janitorial_quality_control/
|
||||
│ │ ├── facilities.py # /api/v1/facilities/* (Phase A)
|
||||
│ │ ├── templates.py # /api/v1/templates/* (Phase A)
|
||||
│ │ ├── inspections.py # /api/v1/inspections/* (Phase B)
|
||||
│ │ ├── issues.py # /api/v1/issues/* (Phase B + Phase 19)
|
||||
│ │ ├── issues.py # /api/v1/issues/* (Phase B + Phase 19 + Phase E)
|
||||
│ │ ├── photos.py # /api/v1/photos/upload (Phase B)
|
||||
│ │ ├── stats.py # /api/v1/stats/dashboard (Phase B stats)
|
||||
│ │ ├── comments.py # /api/v1/issues/<id>/comments (Phase D)
|
||||
│ │ ├── decorators.py # @jwt_required
|
||||
│ │ ├── errors.py # JSON error helpers + error handler registration
|
||||
│ │ └── jwt_utils.py # generate_access_token()
|
||||
@@ -310,6 +312,8 @@ api_device_tokens: id, user_id, device_id, apns_token, device_name, app_version
|
||||
| `api_issues` | `/api/v1` | `GET /issues`, `POST /issues`, `GET /issues/<id>`, `PATCH /issues/<id>/status`, `PATCH /issues/<id>/photos` ← Phase 19 |
|
||||
| `api_photos` | `/api/v1` | `POST /photos/upload` |
|
||||
| `api_notifications` | `/api/v1` | `GET /notifications`, `PATCH /notifications/mark-read` |
|
||||
| `api_stats` | `/api/v1` | `GET /stats/dashboard` — inspector-scoped KPIs with severity breakdown (Phase B) |
|
||||
| `api_comments` | `/api/v1` | `GET /issues/<id>/comments`, `POST /issues/<id>/comments` (Phase D) |
|
||||
|
||||
---
|
||||
|
||||
@@ -339,7 +343,7 @@ ReportLab-based. 12-column grid must be preserved — never collapse in PDF view
|
||||
|
||||
---
|
||||
|
||||
## 9. Mobile API (Phase 7 / Phase A / Phase B / Phase C)
|
||||
## 9. Mobile API (Phase 7 / Phase A–E)
|
||||
|
||||
### CSRF Exemption Pattern — Critical
|
||||
|
||||
@@ -384,7 +388,26 @@ ReportLab-based. 12-column grid must be preserved — never collapse in PDF view
|
||||
|
||||
| Endpoint | Auth | Description |
|
||||
|---|---|---|
|
||||
| `PATCH /api/v1/issues/<id>/photos` | jwt_required | Attach extra evidence photos to an issue. Accepts `{ "result_photos": ["uploads/..."] }`. Stores in `mobile_photo_paths` (NOT `result_photos`). Idempotent — merges with existing paths, never overwrites. Access: inspector must be `assigned_to` or `reported_by`. |
|
||||
| `PATCH /api/v1/issues/<id>/photos` | jwt_required | Attach extra evidence photos to an issue. Accepts `{ "result_photos": ["uploads/..."] }`. Stores in `mobile_photo_paths` (NOT `result_photos`). Idempotent — merges with existing paths, never overwrites. |
|
||||
|
||||
### Phase B (Stats) Endpoint
|
||||
|
||||
| Endpoint | Auth | Description |
|
||||
|---|---|---|
|
||||
| `GET /api/v1/stats/dashboard` | jwt_required | Inspector-scoped KPIs: `today_inspections`, `completed_today`, `open_issues`, `avg_score_30d`, `pending_followups`, `sla_breached`, `sla_at_risk`, `severity_breakdown` (dict: critical/high/medium/low). Inspectors scoped to contracted facilities. Admins/directors/PMs get org-wide numbers. Customers get 403. |
|
||||
|
||||
### Phase D (Comments) Endpoints
|
||||
|
||||
| Endpoint | Auth | Description |
|
||||
|---|---|---|
|
||||
| `GET /api/v1/issues/<id>/comments` | jwt_required | All comments oldest-first. Returns: `id`, `issue_id`, `author_name`, `author_role`, `status_at_time`, `body`, `created_at`. Inspectors limited to contracted facilities. |
|
||||
| `POST /api/v1/issues/<id>/comments` | jwt_required | Add a comment. Body: `{ "body": "..." }`. Fires `notify_by_matrix('issue_comment')`. Calls `log_action()` after commit. |
|
||||
|
||||
### Phase E Additions to Existing Endpoints
|
||||
|
||||
`_issue_payload()` in `issues.py` now returns `area_name` and `assigned_to_name` (both nullable). These populate `LocalIssue.areaNameCache` and `LocalIssue.assignedToName` on the iPad after every `pullAssignedIssues()`. `refreshStatusFromServer()` also refreshes them on demand.
|
||||
|
||||
`stats.py` now returns `severity_breakdown` dict alongside the existing KPIs. Derived from the already-loaded `open_issues_all` list — zero extra DB queries.
|
||||
|
||||
### Issue API — `_issue_payload()` fields
|
||||
|
||||
@@ -394,8 +417,16 @@ ReportLab-based. 12-column grid must be preserved — never collapse in PDF view
|
||||
'facility_id', 'facility_name', 'reported_at', 'resolved_at',
|
||||
'mobile_local_id',
|
||||
'photo_path', # primary evidence photo (first iPad photo or web upload)
|
||||
'mobile_photo_paths', # extra evidence photos from iPad (list) ← Phase 19
|
||||
'mobile_photo_paths', # extra evidence photos from iPad (list)
|
||||
'result_photos', # resolution photos added via web form (list)
|
||||
# Phase A additions:
|
||||
'result_notes', # resolution notes entered by web staff
|
||||
'verified_at', # ISO 8601 datetime when fix was verified (nullable)
|
||||
'verification_note', # note from the verifier (nullable)
|
||||
'reported_by_name', # display_name of User who filed the issue (nullable)
|
||||
# Phase E additions:
|
||||
'area_name', # name of the Area the issue was flagged in (nullable)
|
||||
'assigned_to_name', # display_name of currently assigned User (nullable)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -681,7 +712,7 @@ timeout = 30
|
||||
| 32 | **f-string fallback strings must use double-quotes inside single-quoted f-strings** | Python 3.11 raises `SyntaxError` on nested same-delimiter quotes |
|
||||
| 33–38 | *(field ID casting, photo sentinel, notify event_type, follow-up, OperationalError)* | See prior rule entries |
|
||||
| 39 | **Inspector issue scope: assigned OR reported — web and API must match** | `issues.index()`, `issues.view()`, and all API issue endpoints (`GET /issues`, `GET /issues/<id>`, `PATCH /issues/<id>/status`, `PATCH /issues/<id>/photos`) enforce `assigned_to == user.id OR reported_by == user.id` for the inspector role |
|
||||
| 40 | **`_issue_payload()` must return `photo_path`, `mobile_photo_paths`, and `result_photos`** | iPad reads `photo_path` + `mobile_photo_paths` into `photoServerPaths`; omitting `mobile_photo_paths` means extra evidence photos are invisible on the iPad after sync |
|
||||
| 40 | **`_issue_payload()` must return all documented fields** | iPad reads `photo_path` + `mobile_photo_paths` into `photoServerPaths`. Phase A–E added `result_notes`, `verified_at`, `verification_note`, `reported_by_name`, `area_name`, `assigned_to_name`. Omitting any field silently breaks the corresponding iPad display. |
|
||||
| 41 | **`log_action()` commits internally — always call after `db.session.commit()`** | audit.py calls `db.session.commit()` to write the AuditLog row |
|
||||
| 42 | **`~Inspection.follow_ups.any()` not `== None` for dynamic relationships** | `follow_ups` is `lazy='dynamic'`; use `~.any()` which emits `NOT EXISTS` |
|
||||
| 43 | **`issues.index()` outerjoin must precede all filters** | Both customer-scope and facility_filter blocks reference `Area.facility_id` |
|
||||
|
||||
Reference in New Issue
Block a user