Jul 10 - Update facility's area with QR code

This commit is contained in:
2026-07-10 14:05:54 -04:00
parent 008dd94962
commit 512a80837a
8 changed files with 635 additions and 6 deletions
+87 -1
View File
@@ -226,6 +226,91 @@ def facility_qr_print_all():
facilities=facilities,
selected_contract=selected_contract)
# ── Public Area QR code ───────────────────────────────────────────────────────
# Mirrors the facility QR routes above, but scoped to a single area. Customer
# scope is enforced via the area's parent facility.
def _public_area_url(area):
"""Absolute URL the area QR encodes — the login-free area summary page."""
area.ensure_public_token()
if not area.public_token:
return None
return url_for('public.area_summary',
token=area.public_token, _external=True)
def _area_for_qr_or_403(area_id):
"""Load an area for a QR action, enforcing customer facility scope."""
area = db.session.get(Area, area_id)
if area is None:
abort(404)
if current_user.role == 'customer':
cids = get_customer_scope(current_user) or []
if area.facility_id not in cids:
abort(403)
return area
@bp.route('/areas/<int:area_id>/qr.png')
@login_required
def area_qr_png(area_id):
"""Return the area's QR code as a PNG image."""
area = _area_for_qr_or_403(area_id)
created = not area.public_token
url = _public_area_url(area)
if created:
db.session.commit()
import io
import qrcode
img = qrcode.make(url, box_size=10, border=2)
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
from flask import Response
return Response(buf.getvalue(), mimetype='image/png', headers={
'Cache-Control': 'private, max-age=3600',
})
@bp.route('/areas/<int:area_id>/qr')
@login_required
def area_qr_page(area_id):
"""Printable page: area name + facility + QR + public URL + instructions."""
area = _area_for_qr_or_403(area_id)
public_url = _public_area_url(area)
db.session.commit() # persist token if it was just generated
return render_template('facilities/area_qr.html',
area=area, facility=area.facility,
public_url=public_url)
@bp.route('/areas/<int:area_id>/qr/regenerate', methods=['POST'])
@login_required
def area_qr_regenerate(area_id):
"""Mint a NEW token for this area, invalidating any printed QR code.
Allowed for admin/director, and for customers on their own assigned
facilities. Project managers and inspectors cannot regenerate.
"""
area = _area_for_qr_or_403(area_id)
if current_user.role not in ('admin', 'director', 'customer'):
abort(403)
area.public_token = Area.generate_public_token()
db.session.commit()
logger.info('FACILITIES | area_qr_regenerate | user=%s | area_id=%s',
current_user.username, area.id)
log_action(ACTION_UPDATE, 'Area', area.id, area.name,
'regenerated public QR token (old code invalidated)')
flash('QR code regenerated. Any previously printed codes for this area no '
'longer work — reprint and repost.', 'warning')
return redirect(url_for('facilities.area_qr_page', area_id=area.id))
@bp.route('/<int:facility_id>/edit', methods=['GET', 'POST'])
@login_required
@supervisor_required
@@ -297,7 +382,8 @@ def create_area(facility_id):
area_type=form.area_type.data,
facility_id=facility.id
)
area.ensure_public_token() # QR landing-page token
db.session.add(area)
db.session.commit()
logger.info('FACILITIES | create_area | user=%s | area_id=%s name=%r facility=%r',