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('//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('//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('//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('//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('//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('//user//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('//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('//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('//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 '
Keine Berechtigung.
' 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('//formular') @login_required def formular(module_id): module = CustomModule.query.get_or_404(module_id) if not _can_use(current_user, module): return '
Keine Berechtigung für dieses Modul.
' 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('//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 '
Keine Berechtigung.
' aufmass_id = request.args.get('aufmass_id') if not aufmass_id: return '
Kein Aufmaß ausgewählt.
' aufmass = Aufmass.query.get_or_404(aufmass_id) project = Project.query.get(aufmass.project_id) if not project: return '
Projekt nicht gefunden.
' 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 '''
Keine Bedingungen erfüllt – es wurden keine Positionen erzeugt.
''' 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'''
{created} Positionen ins Aufmaß übernommen. Seite wird neu geladen...
''' @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('//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})