05/25 Improvement 2

This commit is contained in:
2026-05-25 12:33:06 -04:00
parent 9574d15f20
commit e703675370
3 changed files with 8 additions and 5 deletions
+1 -1
View File
@@ -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 | | 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 | | 32 | **f-string fallback strings must use double-quotes inside single-quoted f-strings** | Python 3.11 raises `SyntaxError` on nested same-delimiter quotes |
| 3338 | *(field ID casting, photo sentinel, notify event_type, follow-up, OperationalError)* | See prior rule entries | | 3338 | *(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/<id>`, `PATCH /issues/<id>/status`, `PATCH /issues/<id>/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/<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 `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 | | 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` | | 42 | **`~Inspection.follow_ups.any()` not `== None` for dynamic relationships** | `follow_ups` is `lazy='dynamic'`; use `~.any()` which emits `NOT EXISTS` |
+6 -3
View File
@@ -80,7 +80,8 @@ def index():
) )
if current_user.role == 'inspector': 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': elif current_user.role == 'customer':
customer_facility_ids = get_customer_scope(current_user) customer_facility_ids = get_customer_scope(current_user)
if not customer_facility_ids: if not customer_facility_ids:
@@ -171,8 +172,10 @@ def view(issue_id):
if issue is None: if issue is None:
abort(404) abort(404)
if current_user.role == 'inspector' and issue.assigned_to != current_user.id: if current_user.role == 'inspector' and \
flash('Access denied. You can only view issues assigned to you.', 'danger') issue.assigned_to != current_user.id and \
issue.reported_by != current_user.id:
flash('Access denied.', 'danger')
return redirect(url_for('issues.index')) return redirect(url_for('issues.index'))
if current_user.role == 'customer': if current_user.role == 'customer':
cids = get_customer_scope(current_user) or [] cids = get_customer_scope(current_user) or []
+1 -1
View File
@@ -175,7 +175,7 @@
{% if p %} {% if p %}
<li class="page-item {{ 'active' if p == issues.page }}"> <li class="page-item {{ 'active' if p == issues.page }}">
<a class="page-link" <a class="page-link"
href="{{ url_for('issues.index', page=p, severity=severity_filter, status=status_filter, facility_id=facility_filter) }}">{{ p }}</a> href="{{ url_for('issues.index', page=p, severity=severity_filter, status=status_filter, sla=sla_filter, facility_id=facility_filter) }}">{{ p }}</a>
</li> </li>
{% else %}<li class="page-item disabled"><span class="page-link"></span></li>{% endif %} {% else %}<li class="page-item disabled"><span class="page-link"></span></li>{% endif %}
{% endfor %} {% endfor %}