Aug 25 - Implement new function allow Director (internal & customer) to assign and inspection to another inspector
This commit is contained in:
@@ -575,7 +575,7 @@ Management of the underlying routes is otherwise unchanged; **Start** is the **a
|
||||
| Contracts | ✅ | ✅ | ✅ | read | scoped |
|
||||
| Templates | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| Inspections (execute) | ✅ | ✅ | ✅ | ✅ | read |
|
||||
| Inspection follow-up (request) | ✅ | ✅ | ❌ | ❌ | ✅ own facilities |
|
||||
| Inspection follow-up (request + assign) | ✅ | ✅ | ❌ | ❌ | ✅ own facilities |
|
||||
| Scheduled inspections (plan) | ✅ | ✅ | ✅ | ❌ | ✅ own contracts |
|
||||
| Inspection follow-up (clear) | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| Issues (create/assign) | ✅ | ✅ | ✅ | ✅ | ✅ create own |
|
||||
@@ -886,6 +886,28 @@ Customers can only *request*. `clear_followup` remains admin/director, `reinspec
|
||||
|
||||
**Dispatch** goes through `notify_by_matrix(EVENT_FOLLOWUP_REQUESTED, ...)` — the new `followup_requested` matrix event (admin/director/PM on by default). The inspection's own inspector is notified directly by the route and passed in `exclude_user_ids` so they aren't double-notified; the requester is excluded too. Routing via the matrix (rather than hardcoding managers) is what makes per-contract recipients fire — rule 73. Without it a customer request would reach only the inspector and nobody would own scheduling the re-inspection.
|
||||
|
||||
### Assigning a follow-up to another inspector (Phase 53)
|
||||
|
||||
A follow-up used to belong implicitly to whoever performed the original inspection: they were the one notified, and `GET /api/v1/inspections?follow_up_required=true` filtered on `inspector_id == caller`, so nobody else could even see it. `inspections.follow_up_assigned_to` (FK → users, SET NULL) lets a director — or a **Customer Director**, for their own facilities — hand the re-inspection to someone else.
|
||||
|
||||
**NULL means what it always meant**: the follow-up belongs to the inspection's own inspector. No backfill, no behaviour change for existing rows. `Inspection.follow_up_owner` (assignee *or* inspector) is the single definition of ownership, so the web display, the notification and the API filter cannot disagree.
|
||||
|
||||
**The assignee takes over.** Only the owner is notified, and only the owner sees it — the original inspector's list no longer shows a follow-up that was handed to someone else. In the API that means the two arms must be mutually exclusive:
|
||||
|
||||
```python
|
||||
db.or_(
|
||||
Inspection.follow_up_assigned_to == user.id,
|
||||
db.and_(Inspection.follow_up_assigned_to.is_(None),
|
||||
Inspection.inspector_id == user.id),
|
||||
)
|
||||
```
|
||||
|
||||
Without the `is_(None)` on the second arm the original inspector keeps seeing it and two people turn up to do the same re-inspection.
|
||||
|
||||
**The generic "inspectors see only their own inspections" filter has to be deferred** when `follow_up_required=true` is requested — an assigned follow-up lives on an inspection somebody *else* performed, so applying authorship first hides exactly the rows the assignee needs.
|
||||
|
||||
**The picker is contract-scoped** (`_followup_assignees_for()`), for the same reason the flag-issue list is (rule 93): a Customer Director must never see, or assign work to, another client's inspector. Only the two INSPECTOR roles are offered — directors/PMs/auditors hold no `InspectorAssignment`, so they could not open the re-inspection anyway. The POST re-validates against that list, and a facility with no contract offers nobody (fail-closed, follow-up stays with the original inspector). `clear_followup` (single and bulk) clears the assignment too.
|
||||
|
||||
### Per-Account Overrides for Customer Roles (Phase 51)
|
||||
|
||||
`notify_by_matrix()` consults `UserNotificationMatrix` (§5) for the two customer-side role columns. One query per dispatch (`overrides_for_event`), then `users = [u for u in users if overrides.get(u.id, enabled)]` — an account with no row falls back to the global column, which is what makes both directions work.
|
||||
@@ -1015,7 +1037,24 @@ phase1_projects_roles → phase6_features → phase7_mobile_api → phase8_notif
|
||||
→ phase49_external_inspector
|
||||
→ phase50_default_modern
|
||||
→ phase51_user_notif_matrix
|
||||
→ phase52_template_contracts ← HEAD
|
||||
→ phase52_template_contracts
|
||||
→ phase53_followup_assignee ← HEAD
|
||||
|
||||
#### phase53 — assign a follow-up to another inspector
|
||||
|
||||
Revision id `phase53_followup_assignee`. Adds `inspections.follow_up_assigned_to` (FK → `users.id`, ON DELETE SET NULL) — see §11 "Assigning a follow-up to another inspector".
|
||||
|
||||
**No backfill.** NULL means the follow-up belongs to the inspection's own inspector, which is exactly what every existing row already means, so this cannot change who owns anything on deploy.
|
||||
|
||||
**This is the THIRD FK from `inspections` to `users`** (rule 86). `Inspection.follow_up_assignee` pins `foreign_keys` explicitly; `User.inspections` was already pinned in phase46. Get this wrong and the mapper is ambiguous — and it raises on first ORM *use*, not at import, so the app starts cleanly and then every request 500s.
|
||||
|
||||
`INFORMATION_SCHEMA` column + constraint checks — safe to re-run. `downgrade()` drops the FK then the column, returning every follow-up to its original inspector.
|
||||
|
||||
**Deploy order:**
|
||||
```bash
|
||||
flask db upgrade
|
||||
sudo systemctl restart gunicorn
|
||||
```
|
||||
|
||||
#### phase52 — restrict forms to specific contracts
|
||||
|
||||
|
||||
Reference in New Issue
Block a user