Aug 6 - Add external inspector role

This commit is contained in:
2026-08-06 10:40:33 -04:00
parent 0830585ff5
commit c988f8cabb
32 changed files with 329 additions and 99 deletions
+4
View File
@@ -54,6 +54,10 @@ class UserForm(FlaskForm):
('admin', 'Administrator'),
('director', 'Director'),
('inspector', 'Inspector'),
# phase49 — an inspector employed by the customer or a third party.
# Same capabilities as 'inspector'; scoped to the contracts assigned on
# the Assign Contracts page (see User.INSPECTOR_ROLES).
('external_inspector', 'External Inspector'),
('project_manager', 'Project Manager'),
('auditor', 'Auditor'),
# 'customer' is intentionally excluded — customer accounts are managed via /customers
+7 -4
View File
@@ -565,7 +565,8 @@ def notify_by_matrix(
role_to_db = {
'admin': 'admin',
'director': 'director',
'inspector': 'inspector',
'inspector': 'inspector',
'external_inspector': 'external_inspector',
'project_manager': 'project_manager',
'auditor': 'auditor',
'customer': 'customer',
@@ -594,15 +595,17 @@ def notify_by_matrix(
# OWN inspector (the person who did the work), not the whole inspector
# pool. Without this, enabling the Inspector column for this event would
# notify every inspector on every submission.
if role_key == 'inspector' and event_type == 'inspection_completed':
if (role_key in ('inspector', 'external_inspector')
and event_type == 'inspection_completed'):
target_id = None
if inspection_id:
from app.models.inspection import Inspection
insp = db.session.get(Inspection, inspection_id)
target_id = insp.inspector_id if insp else None
users = [u for u in users if u.id == target_id] if target_id else []
logger.info('MATRIX NOTIFY | event=%s | role=inspector scoped to '
'submitting inspector_id=%s', event_type, target_id)
logger.info('MATRIX NOTIFY | event=%s | role=%s scoped to '
'submitting inspector_id=%s',
event_type, role_key, target_id)
# Scope customer role to facility if provided
if role_key == 'customer' and facility_id:
+8 -1
View File
@@ -8,6 +8,8 @@ Facility-scoping utilities for the Janitorial QC portal.
get_inspector_scope(user) -> list[int] | None
Facility IDs an inspector may access via InspectorAssignment rows.
Applies to BOTH 'inspector' (internal) and 'external_inspector'
(customer / third-party) — see User.INSPECTOR_ROLES.
Returns [] (empty list) when the inspector has no contract assignments,
meaning they see nothing (strict mode).
@@ -90,7 +92,12 @@ def get_inspector_scope(user) -> list[int] | None:
None
Returned for non-inspector roles, indicating unrestricted access.
"""
if user.role != 'inspector':
# phase49: covers BOTH 'inspector' and 'external_inspector'. An external
# (customer / third-party) inspector is scoped by exactly the same
# InspectorAssignment rows — the contracts an admin grants them.
from app.models.user import User
if user.role not in User.INSPECTOR_ROLES:
return None
from app.models.inspector_assignment import InspectorAssignment