Aug 19 - Update code to catch up with ST
This commit is contained in:
+64
-11
@@ -2,7 +2,8 @@ import logging
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, abort
|
||||
from flask_login import login_required, current_user
|
||||
from app import db
|
||||
from app.models.inspection import InspectionTemplate, ChecklistItem
|
||||
from app.models.inspection import InspectionTemplate, ChecklistItem, TemplateContract
|
||||
from app.models.project import Project
|
||||
from app.utils.forms import InspectionTemplateForm, ChecklistItemForm
|
||||
from app.utils.decorators import supervisor_required
|
||||
import json
|
||||
@@ -13,6 +14,17 @@ bp = Blueprint('templates', __name__, url_prefix='/templates')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _populate_contract_choices(form):
|
||||
"""Contract options for the "Available on contracts" multi-select.
|
||||
|
||||
Selecting none leaves the form SHARED (usable on every contract) — that is
|
||||
the default and what every template did before phase52. See
|
||||
TemplateContract.
|
||||
"""
|
||||
contracts = Project.query.filter_by(active=True).order_by(Project.name).all()
|
||||
form.contract_ids.choices = [(p.id, p.name) for p in contracts]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Template CRUD
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -21,7 +33,12 @@ logger = logging.getLogger(__name__)
|
||||
@login_required
|
||||
def index():
|
||||
templates = InspectionTemplate.query.order_by(InspectionTemplate.name).all()
|
||||
return render_template('templates/list.html', templates=templates)
|
||||
# Contract options for the Edit Template modal's "Available on contracts"
|
||||
# picker (phase52) — this modal is the edit UI reached from the list, so it
|
||||
# needs the same control the full editor has.
|
||||
contracts = Project.query.filter_by(active=True).order_by(Project.name).all()
|
||||
return render_template('templates/list.html',
|
||||
templates=templates, contracts=contracts)
|
||||
|
||||
|
||||
@bp.route('/new', methods=['GET', 'POST'])
|
||||
@@ -29,6 +46,7 @@ def index():
|
||||
@supervisor_required
|
||||
def create_template():
|
||||
form = InspectionTemplateForm()
|
||||
_populate_contract_choices(form)
|
||||
|
||||
if form.validate_on_submit():
|
||||
template = InspectionTemplate(
|
||||
@@ -38,11 +56,15 @@ def create_template():
|
||||
created_by=current_user.id
|
||||
)
|
||||
db.session.add(template)
|
||||
db.session.flush() # need template.id before linking contracts
|
||||
template.set_contracts(form.contract_ids.data)
|
||||
db.session.commit()
|
||||
logger.info('TEMPLATES | create | user=%s | template_id=%s name=%r',
|
||||
current_user.username, template.id, template.name)
|
||||
logger.info('TEMPLATES | create | user=%s | template_id=%s name=%r contracts=%s',
|
||||
current_user.username, template.id, template.name,
|
||||
template.contract_ids or 'shared')
|
||||
log_action(ACTION_CREATE, 'Template', template.id, template.name,
|
||||
f'frequency={template.frequency}')
|
||||
f'frequency={template.frequency}; '
|
||||
f'contracts={template.contract_ids or "shared"}')
|
||||
|
||||
flash(f'Template "{template.name}" created successfully.', 'success')
|
||||
return redirect(url_for('templates.form_editor', template_id=template.id))
|
||||
@@ -72,16 +94,23 @@ def edit_template(template_id):
|
||||
if template is None:
|
||||
abort(404)
|
||||
form = InspectionTemplateForm(obj=template)
|
||||
_populate_contract_choices(form)
|
||||
if request.method == 'GET':
|
||||
# obj= cannot read the association rows; seed the multi-select from them.
|
||||
form.contract_ids.data = template.contract_ids
|
||||
|
||||
if form.validate_on_submit():
|
||||
template.name = form.name.data
|
||||
template.description = form.description.data
|
||||
template.frequency = form.frequency.data
|
||||
template.set_contracts(form.contract_ids.data)
|
||||
db.session.commit()
|
||||
logger.info('TEMPLATES | edit | user=%s | template_id=%s name=%r',
|
||||
current_user.username, template.id, template.name)
|
||||
logger.info('TEMPLATES | edit | user=%s | template_id=%s name=%r contracts=%s',
|
||||
current_user.username, template.id, template.name,
|
||||
template.contract_ids or 'shared')
|
||||
log_action(ACTION_UPDATE, 'Template', template.id, template.name,
|
||||
f'frequency={template.frequency}')
|
||||
f'frequency={template.frequency}; '
|
||||
f'contracts={template.contract_ids or "shared"}')
|
||||
flash(f'Template "{template.name}" updated successfully.', 'success')
|
||||
return redirect(url_for('templates.view_template', template_id=template.id))
|
||||
|
||||
@@ -120,11 +149,29 @@ def rename_template(template_id):
|
||||
template.description = request.form.get('description', '').strip() or None
|
||||
template.frequency = new_frequency
|
||||
|
||||
# phase52 — contract restrictions are edited from this modal too, since it
|
||||
# is the Edit Template dialog people actually reach from the list. The
|
||||
# hidden marker distinguishes "the form posted an empty selection" (make
|
||||
# the template shared) from "the form has no contracts field at all", which
|
||||
# must leave the existing restrictions untouched rather than silently
|
||||
# sharing the template with every customer.
|
||||
if request.form.get('contracts_present') == '1':
|
||||
valid_pids = {
|
||||
p.id for p in Project.query.filter_by(active=True).all()
|
||||
}
|
||||
posted = {
|
||||
pid for pid in request.form.getlist('contract_ids', type=int)
|
||||
if pid in valid_pids
|
||||
}
|
||||
template.set_contracts(posted)
|
||||
|
||||
db.session.commit()
|
||||
logger.info('TEMPLATES | rename | user=%s | template_id=%s name=%r',
|
||||
current_user.username, template.id, template.name)
|
||||
logger.info('TEMPLATES | rename | user=%s | template_id=%s name=%r contracts=%s',
|
||||
current_user.username, template.id, template.name,
|
||||
template.contract_ids or 'shared')
|
||||
log_action(ACTION_UPDATE, 'Template', template.id, template.name,
|
||||
f'frequency={template.frequency}; via=rename')
|
||||
f'frequency={template.frequency}; '
|
||||
f'contracts={template.contract_ids or "shared"}; via=rename')
|
||||
flash(f'Template "{template.name}" updated successfully.', 'success')
|
||||
return redirect(url_for('templates.index'))
|
||||
|
||||
@@ -188,6 +235,12 @@ def duplicate_template(template_id):
|
||||
db.session.add(new_tpl)
|
||||
db.session.flush() # get new_tpl.id before committing
|
||||
|
||||
# phase52 — carry the contract restrictions across. Duplicating a
|
||||
# customer's bespoke form must not produce a copy that is silently shared
|
||||
# with every other customer; copying a shared form still yields a shared
|
||||
# one (no links to copy).
|
||||
new_tpl.set_contracts(src.contract_ids)
|
||||
|
||||
# Duplicate all checklist items
|
||||
for item in src.checklist_items.order_by(ChecklistItem.display_order).all():
|
||||
new_item = ChecklistItem(
|
||||
|
||||
Reference in New Issue
Block a user