05/25 Update inspectors contract assignment
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)
|
||||
> **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)
|
||||
|
||||
---
|
||||
|
||||
@@ -179,6 +179,10 @@ areas: id, facility_id (FK), name, area_type
|
||||
projects: id, name, description, project_manager_id, active, created_at
|
||||
customer_assignments: id, user_id, project_id, facility_id (nullable)
|
||||
UniqueConstraint(user_id, project_id, facility_id)
|
||||
inspector_assignments: id, user_id, project_id, created_at
|
||||
UniqueConstraint(user_id, project_id, name='uq_inspector_project')
|
||||
ForeignKey user_id → users(id) ON DELETE CASCADE
|
||||
ForeignKey project_id → projects(id) ON DELETE CASCADE
|
||||
```
|
||||
|
||||
### Inspection
|
||||
@@ -318,7 +322,8 @@ api_device_tokens: id, user_id, device_id, apns_token, device_name, app_version
|
||||
`log_action(action, entity_type, entity_id, entity_label, details)` — call **after** `db.session.commit()`. **This function calls `db.session.commit()` internally.** Calling it before the primary commit will prematurely persist any dirty ORM state in the session.
|
||||
|
||||
### `scope.py`
|
||||
`get_customer_scope(user)` — returns `list[int]` facility IDs for customers, `None` for staff.
|
||||
`get_customer_scope(user)` — returns `list[int]` facility IDs for customers, `None` for non-customers.
|
||||
`get_inspector_scope(user)` — returns `list[int]` facility IDs for inspectors (empty list = no assignments = no access), `None` for non-inspectors. Derived from `InspectorAssignment` rows → project → active facilities.
|
||||
|
||||
### `forms.py`
|
||||
All WTForms classes. `AreaForm.area_type` includes `floor`. `UserForm` excludes `customer` role.
|
||||
@@ -522,7 +527,8 @@ phase1_projects_roles → phase6_features → phase7_mobile_api → phase8_notif
|
||||
→ phase15_audit_log_indexes → phase16_notifications_columns
|
||||
→ phase17_notification_event_type
|
||||
→ phase18_issue_reported_by
|
||||
→ phase19_issue_mobile_photos ← HEAD
|
||||
→ phase19_issue_mobile_photos
|
||||
→ phase20_inspector_assignments ← HEAD
|
||||
```
|
||||
|
||||
### phase19_issue_mobile_photos
|
||||
@@ -684,6 +690,9 @@ timeout = 30
|
||||
| 54 | **Bulk issue verification via `POST /issues/bulk-verify`** | `@supervisor_required`. Accepts `issue_ids` list from form. Skips issues not in `resolved` or `pending_verification` state. Calls `log_action()` after `db.session.commit()` per rule 10. |
|
||||
| 55 | **Scheduled "issues" report groups by facility with SLA status** | `_build_report_data()` now produces `issues_by_facility` (list of `(facility_name, [(issue, sla), ...])`) and `sla_breached`/`sla_at_risk` counts alongside the flat `issues` list. CSV builder uses `resolved_facility` (not `area.facility`) to avoid crash when `area_id` is None. |
|
||||
| 56 | **Customer role: `POST` to `issues.view` returns 403** | The `view()` route checks `request.method == 'POST'` inside the customer scope block and calls `abort(403)`. Customers have read-only access; the template already hides the update form, but server-side enforcement is required against crafted requests. |
|
||||
| 57 | **Inspector contract scoping: `get_inspector_scope()` — strict, no fallback** | Inspectors with NO `InspectorAssignment` rows see nothing (empty list, not `None`). Returns `None` only for non-inspector roles. All routes and API endpoints that currently filter by `inspector_id` or `assigned_to/reported_by` must instead filter by the facility list returned by `get_inspector_scope()`. |
|
||||
| 58 | **Inspector scope covers all data in contracted facilities, not just own work** | Facility list, inspection list, issue list — all scoped to contracted facilities. Dashboard personal stats (today's work, avg score, trend) additionally filter by `inspector_id` so the productivity view stays personal. Issues show ALL facility issues, not just assigned ones. |
|
||||
| 59 | **`assign_inspector_contracts` route replaces the entire assignment set on POST** | The form sends the full checked list; existing assignments not in the POST body are deleted, new ones are inserted. Callers must always POST the complete desired set, not a diff. |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user