from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify, current_app from flask_login import login_required, current_user from app.extensions import db from app.models.company import Company from app.models.user import User from app.models.project import Project from app.models.contract import Contract from app.models.module import Module from app.models.company_module import CompanyModule from app.models.user_module import UserModulePermission from app.models.project_access import ProjectAccess from app.models.license import License, LicenseModule from app.models.aufmass import Aufmass from app.models.position import Position from app.models.settings import Settings superadmin_bp = Blueprint('superadmin', __name__) def _require_superadmin(): if not current_user.is_superadmin(): flash('Nur für Superadmins.', 'danger') return False return True @superadmin_bp.route('/') @login_required def dashboard(): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) firmsen = Company.query.order_by(Company.name).all() gesamt_user = User.query.count() gesamt_projekte = Project.query.count() all_licenses = License.query.all() gesamt_lizenzen = len(all_licenses) gesamt_max_mitarbeiter = sum((l.max_mitarbeiter or 0) for l in all_licenses if not l.unlimited_users) # Unlimited companies count as "max" being their actual user count for l in all_licenses: if l.unlimited_users: gesamt_max_mitarbeiter += l.used_users gesamt_max_module_slots = sum((l.max_module_slots or 0) for l in all_licenses if not l.unlimited_modules) for l in all_licenses: if l.unlimited_modules: gesamt_max_module_slots += l.used_module_slots gesamt_module_anzahl = Module.query.count() belegte_module_slots = db.session.query(UserModulePermission.id)\ .filter(UserModulePermission.aktiv==True).count() for f in firmsen: f._user_count = User.query.filter_by(company_id=f.id).count() lic = License.query.filter_by(company_id=f.id, aktiv=True).first() if lic: f._license_slots = '\u221e' if lic.unlimited_users else f'{f._user_count} / {lic.max_mitarbeiter}' f._module_slots = '\u221e' if lic.unlimited_modules else f'{lic.used_module_slots} / {lic.max_module_slots}' f._licensed_modules = LicenseModule.query.filter_by(license_id=lic.id, aktiv=True).count() else: f._license_slots = '–' f._module_slots = '–' f._licensed_modules = 0 reg_enabled = Settings.get('registration_enabled', 'false') == 'true' return render_template('superadmin/dashboard.html', titel='Superadmin', firmsen=firmsen, gesamt_user=gesamt_user, gesamt_projekte=gesamt_projekte, gesamt_lizenzen=gesamt_lizenzen, gesamt_max_mitarbeiter=gesamt_max_mitarbeiter, gesamt_max_module_slots=gesamt_max_module_slots, gesamt_module_anzahl=gesamt_module_anzahl, belegte_module_slots=belegte_module_slots, registration_enabled=reg_enabled) @superadmin_bp.route('/firma/neu', methods=['GET', 'POST']) @login_required def firma_create(): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) if request.method == 'POST': name = request.form.get('name', '').strip() if not name: flash('Firmenname ist erforderlich.', 'danger') return render_template('superadmin/firma_form.html', titel='Neue Firma', company=None) slug = name.lower().replace(' ', '-').replace('ä','ae').replace('ö','oe').replace('ü','ue')[:100] base_slug = slug counter = 1 while Company.query.filter_by(slug=slug).first(): slug = f"{base_slug}-{counter}" counter += 1 company = Company( name=name, slug=slug, strasse=request.form.get('strasse', '').strip(), house_number=request.form.get('house_number', '').strip(), plz=request.form.get('plz', '').strip(), ort=request.form.get('ort', '').strip(), telefon=request.form.get('telefon', '').strip(), email=request.form.get('email', '').strip(), aktiv=True ) db.session.add(company) db.session.flush() # Optional: Firmadmin-User direkt anlegen admin_email = request.form.get('admin_email', '').strip() admin_password = request.form.get('admin_password', '').strip() if admin_email and admin_password: if User.query.filter_by(email=admin_email).first(): flash('E-Mail existiert bereits. Firma wurde trotzdem angelegt.', 'warning') else: user = User( company_id=company.id, email=admin_email, vorname=request.form.get('admin_vorname', '').strip(), nachname=request.form.get('admin_nachname', '').strip(), rolle='firmadmin', darf_projekte_anlegen=True, darf_lv_verwalten=True, darf_preise_sehen=True, darf_aufmass_verwalten=True, ) user.set_password(admin_password) db.session.add(user) db.session.commit() flash(f'Firma "{name}" angelegt.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) return render_template('superadmin/firma_form.html', titel='Neue Firma', company=None) @superadmin_bp.route('/firma//edit', methods=['GET', 'POST']) @login_required def firma_edit(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) if request.method == 'POST': company.name = request.form.get('name', company.name).strip() company.strasse = request.form.get('strasse', '').strip() company.house_number = request.form.get('house_number', '').strip() company.plz = request.form.get('plz', '').strip() company.ort = request.form.get('ort', '').strip() company.telefon = request.form.get('telefon', '').strip() company.email = request.form.get('email', '').strip() db.session.commit() flash('Firmendaten aktualisiert.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) return render_template('superadmin/firma_form.html', titel=f'Firma bearbeiten: {company.name}', company=company) @superadmin_bp.route('/firma//user/neu', methods=['POST']) @login_required def firma_user_create(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) email = request.form.get('email', '').strip() if not email: flash('E-Mail ist erforderlich.', 'danger') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) if User.query.filter_by(email=email).first(): flash('E-Mail existiert bereits.', 'danger') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) password = request.form.get('password', '').strip() if not password: flash('Passwort ist erforderlich.', 'danger') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) rolle = request.form.get('rolle', 'mitarbeiter') if rolle not in ('mitarbeiter', 'firmadmin'): rolle = 'mitarbeiter' user = User( company_id=company.id, email=email, vorname=request.form.get('vorname', '').strip(), nachname=request.form.get('nachname', '').strip(), rolle=rolle, darf_projekte_anlegen=bool(request.form.get('darf_projekte_anlegen')), darf_lv_verwalten=bool(request.form.get('darf_lv_verwalten')), darf_preise_sehen=bool(request.form.get('darf_preise_sehen')), darf_aufmass_verwalten=bool(request.form.get('darf_aufmass_verwalten')), darf_evergabe_nutzen=bool(request.form.get('darf_evergabe_nutzen')), darf_kopfdaten_holen=bool(request.form.get('darf_kopfdaten_holen')), darf_aufmass_uebertragen=bool(request.form.get('darf_aufmass_uebertragen')), ) user.set_password(password) db.session.add(user) db.session.commit() flash(f'Benutzer {user.full_name} angelegt.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company.id)) @superadmin_bp.route('/firma/') @login_required def firma_detail(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) users = User.query.filter_by(company_id=company.id).order_by(User.email).all() projekte = Project.query.filter_by(company_id=company.id).order_by(Project.erstellt_am.desc()).all() licenses = License.query.filter_by(company_id=company.id).order_by(License.erstellt_am.desc()).all() contracts = Contract.query.filter_by(company_id=company.id).order_by(Contract.name).all() modules = Module.query.order_by(Module.sortierung).all() company_modules = {cm.module_id for cm in CompanyModule.query.filter_by(company_id=company.id, aktiv=True).all()} all_modules = Module.query.order_by(Module.sortierung).all() return render_template('superadmin/firma_detail.html', company=company, users=users, projekte=projekte, licenses=licenses, contracts=contracts, modules=modules, company_modules=company_modules, all_modules=all_modules, titel=f'Firma: {company.name}') @superadmin_bp.route('/firma//toggle') @login_required def firma_toggle(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) company.aktiv = not company.aktiv db.session.commit() flash(f'Firma {"aktiviert" if company.aktiv else "deaktiviert"}.', 'success') return redirect(url_for('superadmin.dashboard')) @superadmin_bp.route('/user//toggle') @login_required def user_toggle(user_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) user = User.query.get_or_404(user_id) user.aktiv = not user.aktiv db.session.commit() flash(f'User {"aktiviert" if user.aktiv else "deaktiviert"}.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=user.company_id)) @superadmin_bp.route('/user//make-superadmin') @login_required def user_make_superadmin(user_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) user = User.query.get_or_404(user_id) if user.is_superadmin(): user.rolle = 'firmadmin' else: user.rolle = 'superadmin' db.session.commit() flash(f'User {user.email} ist jetzt {user.rolle}.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=user.company_id)) @superadmin_bp.route('/user//loeschen', methods=['POST']) @login_required def user_loeschen(user_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) user = User.query.get_or_404(user_id) if user.id == current_user.id: flash('Sie können sich nicht selbst löschen.', 'danger') return redirect(url_for('superadmin.firma_detail', company_id=user.company_id)) email = user.email ProjectAccess.query.filter_by(user_id=user.id).delete() UserModulePermission.query.filter_by(user_id=user.id).delete() db.session.delete(user) db.session.commit() flash(f'Benutzer {email} gelöscht.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=user.company_id)) @superadmin_bp.route('/company//module//toggle') @login_required def company_module_toggle(company_id, module_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) cm = CompanyModule.query.filter_by(company_id=company_id, module_id=module_id).first() if cm: cm.aktiv = not cm.aktiv else: cm = CompanyModule(company_id=company_id, module_id=module_id, aktiv=True) db.session.add(cm) db.session.commit() return redirect(url_for('superadmin.firma_detail', company_id=company_id)) @superadmin_bp.route('/firma//license/create', methods=['POST']) @login_required def license_create(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) max_mitarbeiter = request.form.get('max_mitarbeiter', type=int) or 5 max_module_slots = request.form.get('max_module_slots', type=int) or 5 unlimited_users = bool(request.form.get('unlimited_users')) unlimited_modules = bool(request.form.get('unlimited_modules')) uid = License.generate_uid(company.name) lic = License(company_id=company_id, uid=uid, max_mitarbeiter=max_mitarbeiter, max_module_slots=max_module_slots, unlimited_users=unlimited_users, unlimited_modules=unlimited_modules) db.session.add(lic) db.session.flush() # Assign selected modules mod_ids = request.form.getlist('modules') for mid in mod_ids: db.session.add(LicenseModule(license_id=lic.id, module_id=int(mid), aktiv=True)) db.session.commit() flash('Lizenz angelegt.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company_id)) @superadmin_bp.route('/license//edit', methods=['POST']) @login_required def license_edit(license_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) lic = License.query.get_or_404(license_id) lic.max_mitarbeiter = request.form.get('max_mitarbeiter', type=int) or lic.max_mitarbeiter lic.max_module_slots = request.form.get('max_module_slots', type=int) or lic.max_module_slots lic.unlimited_users = bool(request.form.get('unlimited_users')) lic.unlimited_modules = bool(request.form.get('unlimited_modules')) # Update module assignments new_mod_ids = set(int(x) for x in request.form.getlist('modules')) for lm in lic.modules.filter_by(aktiv=True).all(): if lm.module_id not in new_mod_ids: lm.aktiv = False existing_ids = {lm.module_id for lm in lic.modules.filter_by(aktiv=True).all()} for mid in new_mod_ids: if mid not in existing_ids: db.session.add(LicenseModule(license_id=lic.id, module_id=mid, aktiv=True)) db.session.commit() flash('Lizenz aktualisiert.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=lic.company_id)) @superadmin_bp.route('/license//delete', methods=['POST']) @login_required def license_delete(license_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) lic = License.query.get_or_404(license_id) company_id = lic.company_id db.session.delete(lic) db.session.commit() flash('Lizenz gelöscht.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company_id)) @superadmin_bp.route('/license//module//toggle') @login_required def license_module_toggle(license_id, module_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) lm = LicenseModule.query.filter_by(license_id=license_id, module_id=module_id).first() if lm: lm.aktiv = not lm.aktiv else: lm = LicenseModule(license_id=license_id, module_id=module_id, aktiv=True) db.session.add(lm) db.session.commit() lic = License.query.get(license_id) return redirect(url_for('superadmin.firma_detail', company_id=lic.company_id)) @superadmin_bp.route('/firma//evergabe/toggle') @login_required def firma_evergabe_toggle(company_id): if not _require_superadmin(): return redirect(url_for('admin.dashboard')) company = Company.query.get_or_404(company_id) company.evergabe_aktiviert = not company.evergabe_aktiviert db.session.commit() flash(f'E-Vergabe Addon {"freigeschaltet" if company.evergabe_aktiviert else "deaktiviert"}.', 'success') return redirect(url_for('superadmin.firma_detail', company_id=company_id)) @superadmin_bp.route('/registration/toggle') @login_required def registration_toggle(): if not _require_superadmin(): if request.headers.get('HX-Request'): return jsonify({'error': 'Zugriff verweigert'}), 403 return redirect(url_for('admin.dashboard')) current = Settings.get('registration_enabled', 'false') == 'true' Settings.set('registration_enabled', 'false' if current else 'true') current_app.config['REGISTRATION_ENABLED'] = not current if request.headers.get('HX-Request'): return jsonify({'ok': True, 'registration_enabled': not current}) flash(f'Registrierung {"aktiviert" if not current else "deaktiviert"}.', 'success') return redirect(url_for('superadmin.dashboard')) @superadmin_bp.route('/registration/status') @login_required def registration_status(): if not _require_superadmin(): return jsonify({'error': 'Zugriff verweigert'}), 403 enabled = Settings.get('registration_enabled', 'false') == 'true' return jsonify({'registration_enabled': enabled})