05/25 Update inspectors contract assignment
This commit is contained in:
+27
-14
@@ -23,7 +23,7 @@ from app.models.facility import Facility, Area
|
||||
from app.models.project import Project
|
||||
from app.api.errors import api_ok, api_error
|
||||
from app.api.decorators import jwt_required
|
||||
from app.utils.scope import get_customer_scope
|
||||
from app.utils.scope import get_customer_scope, get_inspector_scope
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -92,27 +92,35 @@ def list_facilities():
|
||||
"""
|
||||
user = g.api_user
|
||||
|
||||
# Customer role: honour facility-level scoping
|
||||
facility_ids = get_customer_scope(user)
|
||||
customer_fids = get_customer_scope(user)
|
||||
inspector_fids = get_inspector_scope(user)
|
||||
|
||||
if facility_ids is not None:
|
||||
if customer_fids is not None:
|
||||
# Customer — scope to assigned facilities only
|
||||
if not facility_ids:
|
||||
if not customer_fids:
|
||||
logger.info('API FACILITIES | user=%s | role=customer | no_assignments',
|
||||
user.username)
|
||||
return api_ok({'facilities': [], 'count': 0})
|
||||
|
||||
facilities = (
|
||||
Facility.query
|
||||
.filter(
|
||||
Facility.id.in_(facility_ids),
|
||||
Facility.active == True, # noqa: E712
|
||||
)
|
||||
.filter(Facility.id.in_(customer_fids), Facility.active == True)
|
||||
.order_by(Facility.name)
|
||||
.all()
|
||||
)
|
||||
elif inspector_fids is not None:
|
||||
# Inspector — scope to contracted facilities
|
||||
if not inspector_fids:
|
||||
logger.info('API FACILITIES | user=%s | role=inspector | no_assignments',
|
||||
user.username)
|
||||
return api_ok({'facilities': [], 'count': 0})
|
||||
facilities = (
|
||||
Facility.query
|
||||
.filter(Facility.id.in_(inspector_fids), Facility.active == True)
|
||||
.order_by(Facility.name)
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
# Internal staff — all active facilities
|
||||
# All other staff — all active facilities
|
||||
facilities = (
|
||||
Facility.query
|
||||
.filter(Facility.active == True) # noqa: E712
|
||||
@@ -159,9 +167,14 @@ def list_areas(facility_id):
|
||||
if facility is None or not facility.active:
|
||||
return api_error('Facility not found', 404)
|
||||
|
||||
# Customer scope validation — ensure the customer is assigned to this facility
|
||||
facility_ids = get_customer_scope(user)
|
||||
if facility_ids is not None and facility_id not in facility_ids:
|
||||
# Scope validation — customers and inspectors may only access their facilities
|
||||
customer_fids = get_customer_scope(user)
|
||||
inspector_fids = get_inspector_scope(user)
|
||||
if customer_fids is not None and facility_id not in customer_fids:
|
||||
logger.warning('API FACILITIES/AREAS | access denied | user=%s | facility_id=%d',
|
||||
user.username, facility_id)
|
||||
return api_error('Access denied', 403)
|
||||
if inspector_fids is not None and facility_id not in inspector_fids:
|
||||
logger.warning('API FACILITIES/AREAS | access denied | user=%s | facility_id=%d',
|
||||
user.username, facility_id)
|
||||
return api_error('Access denied', 403)
|
||||
|
||||
+27
-13
@@ -35,6 +35,7 @@ from app.api.decorators import jwt_required
|
||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE
|
||||
from app.utils.notifications import notify_by_matrix
|
||||
from app.utils.time_utils import now_eastern
|
||||
from app.utils.scope import get_inspector_scope
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -112,14 +113,13 @@ def list_issues():
|
||||
query = Issue.query
|
||||
|
||||
if user.role == 'inspector':
|
||||
# Inspectors see issues assigned to them OR issues they reported.
|
||||
# The reported_by path covers issues created on the iPad that haven't
|
||||
# been assigned yet (assigned_to is NULL until a director assigns them).
|
||||
# Pre-phase18 rows with reported_by = NULL still surface via assigned_to.
|
||||
query = query.filter(
|
||||
fids = get_inspector_scope(user)
|
||||
if not fids:
|
||||
return api_ok({'issues': [], 'total': 0, 'limit': limit, 'offset': offset})
|
||||
query = query.outerjoin(Area, Issue.area_id == Area.id).filter(
|
||||
db.or_(
|
||||
Issue.assigned_to == user.id,
|
||||
Issue.reported_by == user.id,
|
||||
Issue.facility_id.in_(fids),
|
||||
db.and_(Issue.area_id.isnot(None), Area.facility_id.in_(fids)),
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -204,6 +204,11 @@ def create_issue():
|
||||
if facility is None:
|
||||
return api_error('Facility not found', 404)
|
||||
|
||||
if user.role == 'inspector':
|
||||
fids = get_inspector_scope(user)
|
||||
if not fids or facility_id not in fids:
|
||||
return api_error('Access denied — facility is not in your assigned contracts', 403)
|
||||
|
||||
inspection_id = data.get('inspection_id')
|
||||
if inspection_id:
|
||||
inspection = db.session.get(Inspection, inspection_id)
|
||||
@@ -290,8 +295,11 @@ def get_issue(issue_id):
|
||||
if issue is None:
|
||||
return api_error('Issue not found', 404)
|
||||
|
||||
if user.role == 'inspector' and issue.assigned_to != user.id and issue.reported_by != user.id:
|
||||
return api_error('Access denied', 403)
|
||||
if user.role == 'inspector':
|
||||
fids = get_inspector_scope(user)
|
||||
facility = issue.resolved_facility
|
||||
if not fids or not facility or facility.id not in fids:
|
||||
return api_error('Access denied', 403)
|
||||
|
||||
return api_ok(_issue_payload(issue))
|
||||
|
||||
@@ -320,8 +328,11 @@ def update_issue_status(issue_id):
|
||||
if issue is None:
|
||||
return api_error('Issue not found', 404)
|
||||
|
||||
if user.role == 'inspector' and issue.assigned_to != user.id and issue.reported_by != user.id:
|
||||
return api_error('Access denied — you can only update issues assigned to or reported by you', 403)
|
||||
if user.role == 'inspector':
|
||||
fids = get_inspector_scope(user)
|
||||
facility = issue.resolved_facility
|
||||
if not fids or not facility or facility.id not in fids:
|
||||
return api_error('Access denied', 403)
|
||||
|
||||
data = request.get_json(silent=True) or {}
|
||||
new_status = (data.get('status') or '').strip().lower()
|
||||
@@ -382,8 +393,11 @@ def update_issue_photos(issue_id):
|
||||
if issue is None:
|
||||
return api_error('Issue not found', 404)
|
||||
|
||||
if user.role == 'inspector' and issue.assigned_to != user.id and issue.reported_by != user.id:
|
||||
return api_error('Access denied', 403)
|
||||
if user.role == 'inspector':
|
||||
fids = get_inspector_scope(user)
|
||||
facility = issue.resolved_facility
|
||||
if not fids or not facility or facility.id not in fids:
|
||||
return api_error('Access denied', 403)
|
||||
|
||||
data = request.get_json(silent=True) or {}
|
||||
raw = data.get('result_photos')
|
||||
|
||||
Reference in New Issue
Block a user