Jul 9 - Update to allow customer to manage their facilities' QR codes

This commit is contained in:
2026-07-09 09:58:43 -04:00
parent 97a559d5c0
commit b2ac816bc2
6 changed files with 58 additions and 28 deletions
+36 -22
View File
@@ -97,7 +97,9 @@ def view_facility(facility_id):
return render_template('facilities/view.html', facility=facility, areas=areas)
# ── Public QR code (staff-only generation) ────────────────────────────────────
# ── Public QR code ────────────────────────────────────────────────────────────
# Staff (admin/director/pm/inspector) may access QR for any facility; customers
# may access QR only for facilities in their assigned scope.
def _public_facility_url(facility):
"""Absolute URL the QR encodes — the login-free occupant summary page."""
@@ -108,15 +110,27 @@ def _public_facility_url(facility):
token=facility.public_token, _external=True)
@bp.route('/<int:facility_id>/qr.png')
@login_required
def facility_qr_png(facility_id):
"""Return the facility's QR code as a PNG image (staff only)."""
if current_user.role == 'customer':
abort(403)
def _facility_for_qr_or_403(facility_id):
"""Load a facility for a QR action, enforcing customer facility scope.
Customers may only touch QR codes for facilities they are assigned to; all
other (staff) roles have unrestricted QR access.
"""
facility = db.session.get(Facility, facility_id)
if facility is None:
abort(404)
if current_user.role == 'customer':
cids = get_customer_scope(current_user) or []
if facility.id not in cids:
abort(403)
return facility
@bp.route('/<int:facility_id>/qr.png')
@login_required
def facility_qr_png(facility_id):
"""Return the facility's QR code as a PNG image."""
facility = _facility_for_qr_or_403(facility_id)
# Token may not exist for pre-phase34 rows viewed before any commit.
created = not facility.public_token
@@ -141,11 +155,7 @@ def facility_qr_png(facility_id):
@login_required
def facility_qr_page(facility_id):
"""Printable page: facility name + QR + public URL + posting instructions."""
if current_user.role == 'customer':
abort(403)
facility = db.session.get(Facility, facility_id)
if facility is None:
abort(404)
facility = _facility_for_qr_or_403(facility_id)
public_url = _public_facility_url(facility)
db.session.commit() # persist token if it was just generated
return render_template('facilities/qr.html',
@@ -154,12 +164,15 @@ def facility_qr_page(facility_id):
@bp.route('/<int:facility_id>/qr/regenerate', methods=['POST'])
@login_required
@supervisor_required
def facility_qr_regenerate(facility_id):
"""Mint a NEW public token, invalidating any previously printed QR code."""
facility = db.session.get(Facility, facility_id)
if facility is None:
abort(404)
"""Mint a NEW public token, invalidating any previously printed QR code.
Allowed for admin/director, and for customers on their own assigned
facilities. Project managers and inspectors cannot regenerate.
"""
facility = _facility_for_qr_or_403(facility_id)
if current_user.role not in ('admin', 'director', 'customer'):
abort(403)
facility.public_token = Facility.generate_public_token()
db.session.commit()
@@ -178,17 +191,18 @@ def facility_qr_regenerate(facility_id):
def facility_qr_print_all():
"""Printable sheet of QR codes for all facilities the user can see.
Optional ?contract_id=<id> limits the sheet to one contract. Customers have
no QR access (403); inspectors are scoped to their contracted facilities.
Optional ?contract_id=<id> limits the sheet to one contract. Inspectors are
scoped to their contracted facilities; customers to their assigned
facilities; managers see all active facilities.
"""
if current_user.role == 'customer':
abort(403)
contract_id = request.args.get('contract_id', type=int)
if current_user.role == 'inspector':
fids = get_inspector_scope(current_user) or []
query = Facility.query.filter(Facility.id.in_(fids), Facility.active == True)
elif current_user.role == 'customer':
fids = get_customer_scope(current_user) or []
query = Facility.query.filter(Facility.id.in_(fids), Facility.active == True)
else:
query = Facility.query.filter(Facility.active == True)