Jul 16 - Fill the gaps between Single-tenant mode and Multi-tenant mode
This commit is contained in:
+20
-12
@@ -15,7 +15,8 @@ from app.models.notification import (
|
||||
EVENT_CUSTOMER_ISSUE_UPDATED,
|
||||
)
|
||||
from app.utils.forms import IssueForm, IssueUpdateForm
|
||||
from app.utils.decorators import supervisor_required, project_manager_required
|
||||
from app.utils.decorators import (supervisor_required, project_manager_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.tenancy.gates import quota_soft_check
|
||||
@@ -346,7 +347,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
|
||||
@@ -421,7 +422,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
|
||||
|
||||
@@ -431,7 +439,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:
|
||||
@@ -449,7 +457,7 @@ def view(issue_id):
|
||||
issue.result_notes = form.result_notes.data or None
|
||||
|
||||
# Vendor / contractor assignment — admin, director, project_manager only
|
||||
if current_user.role in ('admin', 'director', 'project_manager'):
|
||||
if current_user.role in ('admin', 'director', 'project_manager', 'auditor'):
|
||||
issue.vendor_name = form.vendor_name.data.strip() or None
|
||||
issue.vendor_contact = form.vendor_contact.data.strip() or None
|
||||
issue.vendor_notes = form.vendor_notes.data.strip() or None
|
||||
@@ -695,7 +703,7 @@ def unfollow(issue_id):
|
||||
@login_required
|
||||
@quota_soft_check('issues')
|
||||
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
|
||||
@@ -717,7 +725,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]
|
||||
@@ -803,7 +811,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)
|
||||
@@ -837,7 +845,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)
|
||||
@@ -884,7 +892,7 @@ def request_verification(issue_id):
|
||||
|
||||
# Only the assignee, director, or admin 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:
|
||||
@@ -927,7 +935,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
|
||||
@@ -1028,7 +1036,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