diff --git a/CLAUDE.md b/CLAUDE.md index 5477394..39d5f74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -426,7 +426,7 @@ inspections.scheduled_inspection_id FK→scheduled_inspections SET NULL ← P - **Reminders** are dispatched by the cron endpoint (see §11): advance (1 day before) + due-date to the inspector, overdue to admin/director — each fires at most once per occurrence via the `*_notified` flags. Uses `notify()` with `event_type=EVENT_SCHEDULED_INSPECTION`. - Dashboard shows an **upcoming (next 7 days) / overdue** panel for non-customers (inspectors see only their own). -Management (`/scheduled-inspections/new|edit|delete`) is `@project_manager_required`; **Start** is the assigned inspector or a manager; inspectors' list/dashboard views are scoped to their own `inspector_id`. +Management (`/scheduled-inspections/new|edit|delete`) is `@project_manager_required`; **Start** is the **assigned inspector ONLY** (`sched.inspector_id == current_user.id`) — managers do NOT get a Start button and `GET //start` 403s for anyone who isn't the assignee (the inspection is theirs to do; a manager who must run it assigns it to themselves). The Start button is hidden for non-assignees on both the scheduled-inspections list and the dashboard panel. Inspectors' list/dashboard views are scoped to their own `inspector_id`. --- @@ -486,7 +486,7 @@ Management (`/scheduled-inspections/new|edit|delete`) is `@project_manager_requi | `audit` | `/audit` | list (admin only), view, purge | | `reports` | `/reports` | index, facility report, scorecard, CSV/PDF/Excel export, issues-aging, sla-compliance, followup-closure, facility summary PDF | | `scheduled_reports` | `/scheduled-reports` | CRUD + manual trigger (accessible via Reports sub-nav) | -| `scheduled_inspections` | `/scheduled-inspections` | list, new/edit/delete (PM+), `GET //start` (assigned inspector or manager → creates linked inspection), `POST /run` (cron reminders, `token=DIGEST_SECRET`) | +| `scheduled_inspections` | `/scheduled-inspections` | list, new/edit/delete (PM+), `GET //start` (**assigned inspector only** → creates linked inspection; 403 for non-assignees incl. managers), `POST /run` (cron reminders, `token=DIGEST_SECRET`) | | `support` | `/support` | `GET /chat` (loads latest saved session; `?new=1` to start fresh), `POST /chat/message` (AJAX→Groq; **persists** user+assistant turns, returns `session_id`), `GET /my-conversations`, `GET /my-conversations/` (customer chat history), `GET /admin/conversations`, `GET /admin/conversations/` (staff, read-only), `GET /admin/knowledge` + `/new`, `//edit`, `//delete` (admin/director — chatbot knowledge base), `POST /tickets`, `GET /my-tickets`, `GET/POST /my-tickets/`, `GET /admin/tickets`, `GET/POST /admin/tickets/` | | `broadcast` | `/admin/broadcast` | `GET /` (compose + history), `POST /send` (admin-only; fans out one Notification per targeted user) | | `devices` | `/admin/devices` | `GET /` (device list from `api_device_tokens`), `POST /notify` (admin-only) | diff --git a/app/routes/scheduled_inspections.py b/app/routes/scheduled_inspections.py index 11600aa..88a070a 100644 --- a/app/routes/scheduled_inspections.py +++ b/app/routes/scheduled_inspections.py @@ -4,7 +4,9 @@ app/routes/scheduled_inspections.py Planned / recurring inspection assignments (phase36). Management (list/new/edit/delete) : admin, director, project_manager -Start (execute the planned inspection): the assigned inspector, or admin/director/pm +Start (execute the planned inspection): the assigned inspector ONLY (the person + who must do it) — not managers. A manager + who needs to run it assigns it to themselves. Cron reminders : POST /run?token=DIGEST_SECRET (no login) Fulfillment (marking a schedule done and rolling recurring ones forward) happens @@ -224,11 +226,11 @@ def start(schedule_id): sched = db.session.get(ScheduledInspection, schedule_id) if sched is None: abort(404) - if current_user.role == 'customer': - abort(403) - # Only the assigned inspector, or a manager, may start it. - if current_user.role == 'inspector' and sched.inspector_id != current_user.id: + # Only the assigned inspector may start it — this inspection is theirs to do. + # Managers (admin/director/pm) manage the schedule but do not start it for + # someone else; if a manager needs to do the inspection, assign it to them. + if not sched.inspector_id or sched.inspector_id != current_user.id: abort(403) if not sched.active: diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 27216a7..b275bcb 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -58,8 +58,8 @@ {{ s.inspector.display_name if s.inspector else '—' }} {{ s.next_due_date.strftime('%b %d') }} - {% if current_user.role in ['admin','director','project_manager','auditor'] - or (current_user.role == 'inspector' and s.inspector_id == current_user.id) %} + {# Start is shown only to the assignee — the inspection is theirs to do. #} + {% if s.inspector_id and s.inspector_id == current_user.id %} Start {% endif %} diff --git a/app/templates/scheduled_inspections/list.html b/app/templates/scheduled_inspections/list.html index 9fa2786..635def4 100644 --- a/app/templates/scheduled_inspections/list.html +++ b/app/templates/scheduled_inspections/list.html @@ -60,8 +60,8 @@ {% endif %} - {% if s.active and (current_user.role in ['admin','director','project_manager','auditor'] - or (current_user.role == 'inspector' and s.inspector_id == current_user.id)) %} + {# Start is shown only to the assignee — the inspection is theirs to do. #} + {% if s.active and s.inspector_id and s.inspector_id == current_user.id %} Start