Aug 17 - Update forms will be assigned per contract
This commit is contained in:
@@ -348,7 +348,6 @@ def index():
|
||||
def start():
|
||||
form = StartInspectionForm()
|
||||
|
||||
templates = InspectionTemplate.query.filter_by(active=True).order_by(InspectionTemplate.name).all()
|
||||
projects = Project.query.filter_by(active=True).order_by(Project.name).all()
|
||||
|
||||
# Scope projects to inspector's assigned contracts
|
||||
@@ -360,7 +359,6 @@ def start():
|
||||
}
|
||||
projects = [p for p in projects if p.id in assigned_pids]
|
||||
|
||||
form.template_id.choices = [(t.id, t.name) for t in templates]
|
||||
form.project_id.choices = [(p.id, p.name) for p in projects]
|
||||
|
||||
# Seed facility choices: use submitted project_id, session value, or first project
|
||||
@@ -374,6 +372,13 @@ def start():
|
||||
else:
|
||||
selected_project_id = projects[0].id if projects else None
|
||||
|
||||
# phase52 — forms are offered per CONTRACT: shared forms plus any attached
|
||||
# to the selected contract. This is also the POST validation (SelectField
|
||||
# validates against its choices), so a crafted template_id for another
|
||||
# customer's form is rejected here, not merely hidden in the UI.
|
||||
templates = InspectionTemplate.available_query(selected_project_id).all()
|
||||
form.template_id.choices = [(t.id, t.name) for t in templates]
|
||||
|
||||
if selected_project_id:
|
||||
facilities = Facility.query.filter_by(active=True, project_id=selected_project_id).order_by(Facility.name).all()
|
||||
else:
|
||||
@@ -406,6 +411,19 @@ def start():
|
||||
if template is None:
|
||||
abort(404)
|
||||
|
||||
# Belt-and-braces: the choices above already reject a form that is not
|
||||
# available on this contract, but that guard lives in how the list was
|
||||
# built. Re-assert it against the FACILITY actually chosen, so a future
|
||||
# change to the choice-building cannot quietly open a cross-customer
|
||||
# hole here.
|
||||
_fac = db.session.get(Facility, form.facility_id.data)
|
||||
if not template.available_for_project(_fac.project_id if _fac else None):
|
||||
logger_msg = ('INSPECTION START BLOCKED | template=%s not available for '
|
||||
'facility=%s | user=%s')
|
||||
current_app.logger.warning(logger_msg, template.id,
|
||||
form.facility_id.data, current_user.username)
|
||||
abort(403)
|
||||
|
||||
# Inspector facility scope check — prevent crafted POST from selecting
|
||||
# a facility outside their assigned contracts.
|
||||
if current_user.is_inspector:
|
||||
@@ -463,6 +481,25 @@ def facilities_for_project(project_id):
|
||||
return jsonify([{'id': f.id, 'name': f.name} for f in facilities])
|
||||
|
||||
|
||||
# ── AJAX: forms available on a given contract (phase52) ──────────────────────
|
||||
|
||||
@bp.route('/templates_for_project/<int:project_id>')
|
||||
@login_required
|
||||
def templates_for_project(project_id):
|
||||
"""Forms usable on this contract — shared ones plus any attached to it.
|
||||
|
||||
Powers the Contract -> Form cascade on the start-inspection page, the same
|
||||
way facilities_for_project powers Contract -> Facility. Read-only, and the
|
||||
real gate is still the POST validation in start(); this only keeps the
|
||||
picker honest as the contract changes.
|
||||
"""
|
||||
templates = InspectionTemplate.available_query(project_id).all()
|
||||
return jsonify([
|
||||
{'id': t.id, 'name': t.name, 'shared': t.is_shared}
|
||||
for t in templates
|
||||
])
|
||||
|
||||
|
||||
# ── Execute ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@bp.route('/<int:inspection_id>/execute', methods=['GET', 'POST'])
|
||||
|
||||
Reference in New Issue
Block a user