Aug 6 - Add external inspector role
This commit is contained in:
+24
-12
@@ -57,7 +57,8 @@ def index():
|
||||
# Inspectors get a scoped view of their own inspections and related issues.
|
||||
# Customers get a facility-scoped view.
|
||||
# Internal management roles (director+) get the full unscoped view.
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector', 'customer']:
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector',
|
||||
'external_inspector', 'customer']:
|
||||
from flask import flash, redirect, url_for
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
@@ -66,7 +67,7 @@ def index():
|
||||
|
||||
# Resolve scoping for customers (facility list) and inspectors (inspector_id)
|
||||
customer_facility_ids = get_customer_scope(current_user) # None = unrestricted
|
||||
is_inspector = current_user.role == 'inspector'
|
||||
is_inspector = current_user.is_inspector
|
||||
|
||||
# Inspector filter — admin / director / project_manager only
|
||||
inspector_filter = None
|
||||
@@ -262,7 +263,8 @@ def index():
|
||||
|
||||
inspectors = []
|
||||
if current_user.role in ('admin', 'director', 'project_manager'):
|
||||
inspectors = User.query.filter_by(role='inspector', active=True)\
|
||||
inspectors = User.query.filter(User.role.in_(User.INSPECTOR_ROLES),
|
||||
User.active == True)\
|
||||
.order_by(User.full_name, User.username).all()
|
||||
|
||||
facility_scores_list = [{
|
||||
@@ -307,7 +309,8 @@ def index():
|
||||
@bp.route('/facility/<int:facility_id>')
|
||||
@login_required
|
||||
def facility_report(facility_id):
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector', 'customer']:
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector',
|
||||
'external_inspector', 'customer']:
|
||||
from flask import flash, redirect, url_for
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
@@ -320,7 +323,7 @@ def facility_report(facility_id):
|
||||
from flask import flash, redirect, url_for
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('reports.index'))
|
||||
if current_user.role == 'inspector':
|
||||
if current_user.is_inspector:
|
||||
# Inspectors may only view the facility report for facilities where
|
||||
# they have personally conducted at least one inspection.
|
||||
has_access = Inspection.query.filter_by(
|
||||
@@ -369,7 +372,8 @@ def facility_report(facility_id):
|
||||
def facility_scorecard(facility_id):
|
||||
"""Comprehensive per-facility scorecard: score trend, SLA compliance,
|
||||
issue breakdown by severity, inspection frequency."""
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector', 'customer']:
|
||||
if current_user.role not in ['admin', 'director', 'project_manager', 'inspector',
|
||||
'external_inspector', 'customer']:
|
||||
from flask import flash, redirect, url_for
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
@@ -385,7 +389,7 @@ def facility_scorecard(facility_id):
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('reports.index'))
|
||||
|
||||
if current_user.role == 'inspector':
|
||||
if current_user.is_inspector:
|
||||
has_access = Inspection.query.filter_by(
|
||||
facility_id=facility_id,
|
||||
inspector_id=current_user.id,
|
||||
@@ -701,7 +705,8 @@ def _build_inspector_stats(start, end):
|
||||
all_ids = set(total_map.keys())
|
||||
active_inspectors = (
|
||||
User.query
|
||||
.filter(User.id.in_(all_ids), User.active == True, User.role == 'inspector')
|
||||
.filter(User.id.in_(all_ids), User.active == True,
|
||||
User.role.in_(User.INSPECTOR_ROLES))
|
||||
.order_by(User.full_name, User.username)
|
||||
.all()
|
||||
) if all_ids else []
|
||||
@@ -717,6 +722,10 @@ def _build_inspector_stats(start, end):
|
||||
inspector_stats.append({
|
||||
'id': u.id,
|
||||
'display_name': u.display_name,
|
||||
# phase49 — customer / third-party inspectors appear in the same
|
||||
# table as our own crew, badged so the numbers can be read in
|
||||
# context. Consumed by the HTML table and the Excel export.
|
||||
'external': u.is_external_inspector,
|
||||
'total': tot,
|
||||
'completed': comp,
|
||||
'completion_rate': round(comp / tot * 100) if tot else 0,
|
||||
@@ -759,7 +768,7 @@ def inspector_performance():
|
||||
|
||||
if selected_id:
|
||||
selected_inspector = db.session.get(User, selected_id)
|
||||
if selected_inspector and selected_inspector.role == 'inspector':
|
||||
if selected_inspector and selected_inspector.is_inspector:
|
||||
selected_kpis = next((s for s in inspector_stats if s['id'] == selected_id), None)
|
||||
|
||||
trend_rows = db.session.query(
|
||||
@@ -857,7 +866,7 @@ def export_inspector_performance():
|
||||
.filter(
|
||||
Inspection.inspection_date >= start,
|
||||
Inspection.inspection_date <= end,
|
||||
User.role == 'inspector',
|
||||
User.role.in_(User.INSPECTOR_ROLES),
|
||||
)
|
||||
if selected_id:
|
||||
detail_q = detail_q.filter(Inspection.inspector_id == selected_id)
|
||||
@@ -931,7 +940,10 @@ def export_inspector_performance():
|
||||
for row_idx, s in enumerate(inspector_stats, start=3):
|
||||
stripe = sub_fill if row_idx % 2 == 0 else None
|
||||
row_data = [
|
||||
s['display_name'],
|
||||
# phase49 — external inspectors share this table with our own crew;
|
||||
# suffixed rather than given a column so the index-based styling
|
||||
# below (score = col 5, vs_avg = col 6, …) stays correct.
|
||||
s['display_name'] + (' (External)' if s.get('external') else ''),
|
||||
s['total'],
|
||||
s['completed'],
|
||||
s['completion_rate'],
|
||||
@@ -1616,7 +1628,7 @@ def facility_summary_pdf(facility_id):
|
||||
cids = get_customer_scope(current_user) or []
|
||||
if facility_id not in cids:
|
||||
abort(403)
|
||||
elif current_user.role == 'inspector':
|
||||
elif current_user.is_inspector:
|
||||
has = Inspection.query.filter_by(facility_id=facility_id,
|
||||
inspector_id=current_user.id).first()
|
||||
if not has:
|
||||
|
||||
Reference in New Issue
Block a user