Files

415 lines
16 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from app.extensions import db
from app.models.custom_module import CustomModule, CustomModuleAssignment
from app.models.project import Project
from app.models.aufmass import Aufmass
from app.models.position import Position
from app.models.user import User
from app.models.company import Company
from app.services.custom_module_renderer import render_form
from app.services.custom_module_executor import execute_rules
import json
custom_modules_bp = Blueprint('custom_modules', __name__, url_prefix='/custom-modules')
def _can_manage(user, module=None):
if user.is_superadmin():
return True
if user.is_firmadmin():
if module is None:
return True
if module.is_template:
return False
return module.company_id == user.company_id
return False
def _can_use(user, module):
if user.is_superadmin():
return True
if user.is_firmadmin() and module.company_id == user.company_id:
return True
if not module.is_active:
return False
return CustomModuleAssignment.query.filter_by(
module_id=module.id, user_id=user.id
).first() is not None
@custom_modules_bp.route('/')
@login_required
def index():
if not _can_manage(current_user):
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
if current_user.is_superadmin():
templates = CustomModule.query.filter_by(is_template=True).order_by(CustomModule.sort_index).all()
company_modules = CustomModule.query.filter_by(is_template=False).order_by(CustomModule.company_id, CustomModule.sort_index).all()
companies = Company.query.order_by(Company.name).all()
else:
templates = CustomModule.query.filter_by(is_template=True, is_active=True).order_by(CustomModule.sort_index).all()
company_modules = CustomModule.query.filter_by(
company_id=current_user.company_id, is_template=False
).order_by(CustomModule.sort_index).all()
companies = []
return render_template('custom_modules/index.html',
templates=templates,
company_modules=company_modules,
companies=companies)
@custom_modules_bp.route('/neu', methods=['GET', 'POST'])
@login_required
def neu():
if not _can_manage(current_user):
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
if request.method == 'POST':
name = request.form.get('name', '').strip()
if not name:
flash('Name ist erforderlich.', 'danger')
return render_template('custom_modules/edit.html', module=None)
module = CustomModule(
company_id=current_user.company_id if not current_user.is_superadmin() else None,
name=name,
description=request.form.get('description', ''),
kategorie=request.form.get('kategorie', 'allgemein'),
icon=request.form.get('icon', '🔧'),
is_template=bool(request.form.get('is_template')) if current_user.is_superadmin() else False,
form_json='[]',
rules_json='[]',
created_by=current_user.id,
)
db.session.add(module)
db.session.commit()
flash(f'Modul "{name}" erstellt.', 'success')
return redirect(url_for('custom_modules.edit', module_id=module.id))
return render_template('custom_modules/edit.html', module=None,
is_superadmin=current_user.is_superadmin())
@custom_modules_bp.route('/<int:module_id>/bearbeiten', methods=['GET', 'POST'])
@login_required
def edit(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
if request.method == 'POST':
module.name = request.form.get('name', module.name).strip()
module.description = request.form.get('description', '')
module.kategorie = request.form.get('kategorie', 'allgemein')
module.icon = request.form.get('icon', '🔧')
module.is_active = bool(request.form.get('is_active', True))
if current_user.is_superadmin():
module.is_template = bool(request.form.get('is_template', module.is_template))
db.session.commit()
flash('Modul aktualisiert.', 'success')
return redirect(url_for('custom_modules.index'))
users = []
if module.company_id:
users = User.query.filter_by(company_id=module.company_id, aktiv=True).order_by(User.vorname).all()
assignments = {a.user_id: a for a in module.assignments}
return render_template('custom_modules/edit.html', module=module,
users=users, assignments=assignments,
is_superadmin=current_user.is_superadmin())
@custom_modules_bp.route('/<int:module_id>/builder')
@login_required
def builder(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
return render_template('custom_modules/builder.html', module=module)
@custom_modules_bp.route('/<int:module_id>/loeschen', methods=['POST'])
@login_required
def loeschen(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
name = module.name
db.session.delete(module)
db.session.commit()
flash(f'Modul "{name}" gelöscht.', 'success')
return redirect(url_for('custom_modules.index'))
@custom_modules_bp.route('/<int:module_id>/importieren', methods=['POST'])
@login_required
def importieren(module_id):
template = CustomModule.query.get_or_404(module_id)
if not template.is_template:
flash('Nur Vorlagen können importiert werden.', 'danger')
return redirect(url_for('custom_modules.index'))
if not current_user.is_firmadmin():
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('custom_modules.index'))
existing = CustomModule.query.filter_by(
company_id=current_user.company_id,
original_template_id=template.id
).first()
if existing:
flash(f'Vorlage "{template.name}" bereits als "{existing.name}" importiert.', 'warning')
return redirect(url_for('custom_modules.index'))
copy = CustomModule(
company_id=current_user.company_id,
original_template_id=template.id,
name=template.name,
description=template.description,
kategorie=template.kategorie,
icon=template.icon,
form_json=template.form_json,
rules_json=template.rules_json,
is_template=False,
created_by=current_user.id,
)
db.session.add(copy)
db.session.commit()
flash(f'Vorlage "{template.name}" importiert jetzt kannst du sie anpassen.', 'success')
return redirect(url_for('custom_modules.edit', module_id=copy.id))
@custom_modules_bp.route('/<int:module_id>/als-vorlage', methods=['POST'])
@login_required
def als_vorlage(module_id):
module = CustomModule.query.get_or_404(module_id)
if not current_user.is_superadmin():
flash('Keine Berechtigung.', 'danger')
return redirect(url_for('admin.dashboard'))
template = CustomModule(
company_id=None,
original_template_id=None,
name=module.name,
description=module.description,
kategorie=module.kategorie,
icon=module.icon,
form_json=module.form_json,
rules_json=module.rules_json,
is_template=True,
created_by=current_user.id,
)
db.session.add(template)
db.session.commit()
flash(f'Modul "{module.name}" als globale Vorlage gespeichert.', 'success')
return redirect(url_for('custom_modules.index'))
@custom_modules_bp.route('/<int:module_id>/user/<int:user_id>/toggle', methods=['POST'])
@login_required
def user_toggle(module_id, user_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
return jsonify({'error': 'Keine Berechtigung'}), 403
user = User.query.get_or_404(user_id)
assignment = CustomModuleAssignment.query.filter_by(
module_id=module.id, user_id=user.id
).first()
data = request.get_json(silent=True) or {}
active = data.get('active', False)
can_edit = data.get('can_edit', False)
if active:
if not assignment:
assignment = CustomModuleAssignment(
module_id=module.id, user_id=user.id, can_edit=can_edit
)
db.session.add(assignment)
db.session.commit()
return jsonify({'message': f'{user.full_name} hat jetzt Zugriff.', 'active': True})
return jsonify({'active': True})
else:
if assignment:
db.session.delete(assignment)
db.session.commit()
return jsonify({'message': f'Zugriff für {user.full_name} entzogen.', 'active': False})
return jsonify({'active': False})
@custom_modules_bp.route('/<int:module_id>/api/form-json', methods=['GET', 'PUT'])
@login_required
def api_form_json(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
return jsonify({'error': 'Keine Berechtigung'}), 403
if request.method == 'PUT':
try:
data = request.get_json(force=True)
module.form_json = json.dumps(data, ensure_ascii=False)
db.session.commit()
return jsonify({'ok': True})
except Exception as e:
return jsonify({'error': str(e)}), 400
return jsonify(json.loads(module.form_json or '[]'))
@custom_modules_bp.route('/<int:module_id>/api/rules-json', methods=['GET', 'PUT'])
@login_required
def api_rules_json(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
return jsonify({'error': 'Keine Berechtigung'}), 403
if request.method == 'PUT':
try:
data = request.get_json(force=True)
module.rules_json = json.dumps(data, ensure_ascii=False)
db.session.commit()
return jsonify({'ok': True})
except Exception as e:
return jsonify({'error': str(e)}), 400
return jsonify(json.loads(module.rules_json or '[]'))
@custom_modules_bp.route('/<int:module_id>/api/preview')
@login_required
def api_preview(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
return '<div class="notification is-danger">Keine Berechtigung.</div>'
html = render_form(module.form_json, module_id)
return html
@custom_modules_bp.route('/api/available')
@login_required
def api_available():
modules = CustomModule.query.filter(
CustomModule.is_active == True,
CustomModule.is_template == False,
CustomModule.company_id == current_user.company_id
).order_by(CustomModule.sort_index).all()
result = []
for m in modules:
if current_user.is_firmadmin() or current_user.is_superadmin():
result.append({'id': m.id, 'name': m.name, 'icon': m.icon, 'kategorie': m.kategorie})
elif _can_use(current_user, m):
result.append({'id': m.id, 'name': m.name, 'icon': m.icon, 'kategorie': m.kategorie})
return jsonify(result)
@custom_modules_bp.route('/<int:module_id>/formular')
@login_required
def formular(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_use(current_user, module):
return '<div class="notification is-danger">Keine Berechtigung für dieses Modul.</div>'
aufmass_id = request.args.get('aufmass_id')
html = render_form(module.form_json, module_id, aufmass_id=aufmass_id)
return html
@custom_modules_bp.route('/<int:module_id>/berechnen', methods=['POST'])
@login_required
def berechnen(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_use(current_user, module):
return '<div class="notification is-danger">Keine Berechtigung.</div>'
aufmass_id = request.args.get('aufmass_id')
if not aufmass_id:
return '<div class="notification is-danger">Kein Aufmaß ausgewählt.<br><button class="button is-small is-light mt-2" onclick="closeModulModal()">Schließen</button></div>'
aufmass = Aufmass.query.get_or_404(aufmass_id)
project = Project.query.get(aufmass.project_id)
if not project:
return '<div class="notification is-danger">Projekt nicht gefunden.<br><button class="button is-small is-light mt-2" onclick="closeModulModal()">Schließen</button></div>'
project_id = project.id
company_id = project.company_id
form_data = {k: v for k, v in request.form.items()}
rules = json.loads(module.rules_json) if isinstance(module.rules_json, str) else module.rules_json
positions_data = execute_rules(form_data, module.rules_json, company_id)
if not positions_data:
return '''
<div class="notification is-warning" style="margin-top:10px">
Keine Bedingungen erfüllt es wurden keine Positionen erzeugt.
<button class="delete" onclick="closeModulModal()"></button>
</div>
<button class="button is-small is-light mt-2" onclick="closeModulModal()">Schließen</button>'''
created = 0
for pd in positions_data:
pos = Position(
aufmass_id=aufmass.id,
project_id=project_id,
pos_nr=pd.get('pos_nr', ''),
kurztext=pd.get('kurztext', ''),
langtext=pd.get('langtext', ''),
einheit=pd.get('einheit', 'ST'),
menge=pd.get('menge', 0),
faktor=pd.get('faktor', 1),
laenge=pd.get('laenge', 0),
breite=pd.get('breite', 0),
tiefe=pd.get('tiefe', 0),
einzelpreis=pd.get('einzelpreis', 0),
bemerkung=pd.get('bemerkung', ''),
sort_index=pd.get('sort_index', 0),
)
db.session.add(pos)
created += 1
db.session.commit()
return f'''<div class="notification is-success">
<strong>{created} Positionen</strong> ins Aufmaß übernommen.
<span class="is-pulled-right tag is-light is-info">Seite wird neu geladen...</span>
</div>
<script>
setTimeout(function() {{
var m = document.getElementById('modul-modal');
if (m) m.classList.remove('is-active');
location.reload();
}}, 1200);
</script>'''
@custom_modules_bp.route('/api/sort-batch', methods=['POST'])
@login_required
def sort_batch():
if not _can_manage(current_user):
return jsonify({'error': 'Keine Berechtigung'}), 403
data = request.get_json(force=True)
if not isinstance(data, list):
return jsonify({'error': 'Erwarte Liste von {id, sort_index}'}), 400
for item in data:
module = CustomModule.query.get(item.get('id'))
if not module:
continue
if not _can_manage(current_user, module):
continue
module.sort_index = item.get('sort_index', 0)
db.session.commit()
return jsonify({'ok': True, 'updated': len(data)})
@custom_modules_bp.route('/<int:module_id>/sort', methods=['POST'])
@login_required
def sort(module_id):
module = CustomModule.query.get_or_404(module_id)
if not _can_manage(current_user, module):
return jsonify({'error': 'Keine Berechtigung'}), 403
data = request.get_json(force=True)
sort_index = data.get('sort_index', 0)
module.sort_index = sort_index
db.session.commit()
return jsonify({'ok': True})