From e703675370fcbe8f53d6695a7a348e504b24a39e Mon Sep 17 00:00:00 2001 From: Nguyen HP Laptop Date: Mon, 25 May 2026 12:33:06 -0400 Subject: [PATCH] 05/25 Improvement 2 --- CLAUDE.md | 2 +- app/routes/issues.py | 9 ++++++--- app/templates/issues/list.html | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2e27164..f9a120f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -666,7 +666,7 @@ timeout = 30 | 31 | **Do not sync an issue when its parent `LocalInspection.syncStatus == "failed"`** | Submitting without `inspection_id` creates orphaned server records | | 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 | **Issue API scope: inspectors see assigned OR reported issues** | `GET /issues`, `GET /issues/`, `PATCH /issues//status`, `PATCH /issues//photos` all enforce `assigned_to == user.id OR reported_by == user.id` for the inspector role | +| 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/`, `PATCH /issues//status`, `PATCH /issues//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 | | 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` | diff --git a/app/routes/issues.py b/app/routes/issues.py index 00cdeec..4502bc3 100644 --- a/app/routes/issues.py +++ b/app/routes/issues.py @@ -80,7 +80,8 @@ def index(): ) if current_user.role == 'inspector': - q = q.filter(Issue.assigned_to == current_user.id) + q = q.filter(db.or_(Issue.assigned_to == current_user.id, + Issue.reported_by == current_user.id)) elif current_user.role == 'customer': customer_facility_ids = get_customer_scope(current_user) if not customer_facility_ids: @@ -171,8 +172,10 @@ def view(issue_id): if issue is None: abort(404) - if current_user.role == 'inspector' and issue.assigned_to != current_user.id: - flash('Access denied. You can only view issues assigned to you.', 'danger') + if current_user.role == 'inspector' and \ + issue.assigned_to != current_user.id and \ + issue.reported_by != current_user.id: + flash('Access denied.', 'danger') return redirect(url_for('issues.index')) if current_user.role == 'customer': cids = get_customer_scope(current_user) or [] diff --git a/app/templates/issues/list.html b/app/templates/issues/list.html index cde2b87..17cd518 100644 --- a/app/templates/issues/list.html +++ b/app/templates/issues/list.html @@ -175,7 +175,7 @@ {% if p %}
  • {{ p }} + href="{{ url_for('issues.index', page=p, severity=severity_filter, status=status_filter, sla=sla_filter, facility_id=facility_filter) }}">{{ p }}
  • {% else %}
  • {% endif %} {% endfor %}