Jul 15 - Add Auditor user role
This commit is contained in:
@@ -39,6 +39,7 @@ def index():
|
||||
is_privileged = current_user.role in ['admin', 'director']
|
||||
is_customer = current_user.role == 'customer'
|
||||
is_project_manager = current_user.role == 'project_manager'
|
||||
is_auditor = current_user.role == 'auditor'
|
||||
|
||||
# Resolve facility scope
|
||||
customer_facility_ids = get_customer_scope(current_user) # None for non-customers
|
||||
@@ -309,9 +310,9 @@ def index():
|
||||
unassigned_open = len(unassigned_all)
|
||||
unassigned_handler = _handler_split(unassigned_all)
|
||||
|
||||
# ── Inspector activity today (admin / director / PM only) ─────────────────
|
||||
# ── Inspector activity today (admin / director / PM / auditor only) ───────
|
||||
inspector_activity = []
|
||||
if is_privileged or is_project_manager:
|
||||
if is_privileged or is_project_manager or is_auditor:
|
||||
active_inspectors = (
|
||||
User.query
|
||||
.filter_by(role='inspector', active=True)
|
||||
|
||||
@@ -608,7 +608,7 @@ def execute(inspection_id):
|
||||
return redirect(url_for('inspections.execute', inspection_id=inspection_id))
|
||||
|
||||
staff_for_flag_issue = User.query.filter(
|
||||
User.role.in_(['admin', 'director', 'inspector', 'project_manager']),
|
||||
User.role.in_(['director', 'inspector', 'project_manager', 'auditor']),
|
||||
User.active == True,
|
||||
).order_by(User.full_name, User.username).all()
|
||||
|
||||
|
||||
+21
-14
@@ -15,7 +15,7 @@ from app.models.notification import (
|
||||
EVENT_CUSTOMER_ISSUE_UPDATED,
|
||||
)
|
||||
from app.utils.forms import IssueForm, IssueUpdateForm
|
||||
from app.utils.decorators import supervisor_required
|
||||
from app.utils.decorators import supervisor_required, issue_manager_required
|
||||
from app.utils.notifications import notify, notify_customers_for_facility, notify_by_matrix
|
||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT
|
||||
from app.utils.pdf_export import generate_issues_list_pdf
|
||||
@@ -344,7 +344,7 @@ def index():
|
||||
|
||||
# Staff for quick-assign dropdown — same roles as the full issue form
|
||||
staff = User.query.filter(
|
||||
User.role.in_(['admin', 'director', 'inspector']), User.active == True
|
||||
User.role.in_(['director', 'inspector', 'auditor']), User.active == True
|
||||
).order_by(User.username).all()
|
||||
|
||||
# Reporters dropdown — users who have actually filed at least one issue
|
||||
@@ -419,7 +419,14 @@ def view(issue_id):
|
||||
return redirect(url_for('issues.view', issue_id=issue_id))
|
||||
|
||||
form = IssueUpdateForm(obj=issue)
|
||||
staff = User.query.filter(User.role.in_(['admin', 'director', 'inspector'])).order_by(User.username).all()
|
||||
staff = User.query.filter(User.role.in_(['director', 'inspector', 'auditor'])).order_by(User.username).all()
|
||||
# Preserve any pre-existing assignee who is no longer in the assignable set
|
||||
# (e.g. an admin assigned before admins were removed from the dropdown) so
|
||||
# saving the form doesn't silently unassign them.
|
||||
if issue.assigned_to and issue.assigned_to not in [u.id for u in staff]:
|
||||
current_assignee = db.session.get(User, issue.assigned_to)
|
||||
if current_assignee:
|
||||
staff.append(current_assignee)
|
||||
form.assigned_to.choices = [(0, '— Unassigned —')] + [(u.id, u.display_name) for u in staff]
|
||||
form.status.data = form.status.data or issue.status
|
||||
|
||||
@@ -429,7 +436,7 @@ def view(issue_id):
|
||||
|
||||
issue.status = form.status.data
|
||||
|
||||
if current_user.role in ['admin', 'director']:
|
||||
if current_user.role in ['admin', 'director', 'auditor']:
|
||||
issue.assigned_to = form.assigned_to.data or None
|
||||
|
||||
if form.status.data == 'resolved' and not issue.resolved_at:
|
||||
@@ -447,8 +454,8 @@ def view(issue_id):
|
||||
issue.result_notes = form.result_notes.data or None
|
||||
|
||||
# Handler assignment (who handles it) + vendor/facility details —
|
||||
# admin, director, project_manager only.
|
||||
if current_user.role in ('admin', 'director', 'project_manager'):
|
||||
# admin, director, project_manager, auditor only.
|
||||
if current_user.role in ('admin', 'director', 'project_manager', 'auditor'):
|
||||
handler = form.handler_type.data or 'internal'
|
||||
if handler not in ('internal', 'facility', 'vendor'):
|
||||
handler = 'internal'
|
||||
@@ -690,7 +697,7 @@ def unfollow(issue_id):
|
||||
@bp.route('/new', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def create():
|
||||
if current_user.role not in ('admin', 'director', 'customer'):
|
||||
if current_user.role not in ('admin', 'director', 'customer', 'auditor'):
|
||||
abort(403)
|
||||
|
||||
from app.models.project import Project, CustomerAssignment
|
||||
@@ -712,7 +719,7 @@ def create():
|
||||
else:
|
||||
facilities = Facility.query.filter_by(active=True).order_by(Facility.name).all()
|
||||
projects = Project.query.filter_by(active=True).order_by(Project.name).all()
|
||||
staff = User.query.filter(User.role.in_(['admin', 'director', 'inspector'])).order_by(User.username).all()
|
||||
staff = User.query.filter(User.role.in_(['director', 'inspector', 'auditor'])).order_by(User.username).all()
|
||||
|
||||
form.facility_id.choices = [(f.id, f.name) for f in facilities]
|
||||
form.assigned_to.choices = [(0, '— Unassigned —')] + [(u.id, u.display_name) for u in staff]
|
||||
@@ -812,7 +819,7 @@ def create():
|
||||
|
||||
@bp.route('/<int:issue_id>/verify', methods=['POST'])
|
||||
@login_required
|
||||
@supervisor_required
|
||||
@issue_manager_required
|
||||
def verify(issue_id):
|
||||
"""Supervisor sign-off: confirms resolution is satisfactory and closes the issue."""
|
||||
issue = db.session.get(Issue, issue_id)
|
||||
@@ -846,7 +853,7 @@ def verify(issue_id):
|
||||
|
||||
@bp.route('/bulk-verify', methods=['POST'])
|
||||
@login_required
|
||||
@supervisor_required
|
||||
@issue_manager_required
|
||||
def bulk_verify():
|
||||
"""Verify multiple pending-verification issues in a single action."""
|
||||
issue_ids = request.form.getlist('issue_ids', type=int)
|
||||
@@ -891,9 +898,9 @@ def request_verification(issue_id):
|
||||
flash('Access denied.', 'danger')
|
||||
return redirect(url_for('issues.index'))
|
||||
|
||||
# Only the assignee, director, or admin can request verification
|
||||
# Only the assignee, director, admin, or auditor can request verification
|
||||
can_act = (
|
||||
current_user.role in ['admin', 'director']
|
||||
current_user.role in ['admin', 'director', 'auditor']
|
||||
or issue.assigned_to == current_user.id
|
||||
)
|
||||
if not can_act:
|
||||
@@ -936,7 +943,7 @@ def request_verification(issue_id):
|
||||
|
||||
@bp.route('/verification-queue')
|
||||
@login_required
|
||||
@supervisor_required
|
||||
@issue_manager_required
|
||||
def verification_queue():
|
||||
"""Supervisor queue of all issues awaiting verification, grouped by facility."""
|
||||
from app.models.facility import Facility, Area
|
||||
@@ -1032,7 +1039,7 @@ def delete(issue_id):
|
||||
@login_required
|
||||
def quick_assign(issue_id):
|
||||
"""Inline assignee update from the issues list — returns JSON."""
|
||||
if current_user.role not in ('admin', 'director'):
|
||||
if current_user.role not in ('admin', 'director', 'auditor'):
|
||||
return jsonify({'ok': False, 'error': 'Permission denied'}), 403
|
||||
|
||||
issue = db.session.get(Issue, issue_id)
|
||||
|
||||
Reference in New Issue
Block a user