Files
aufmass-web/_aufmass_web/app/routes/superadmin.py
T

377 lines
17 KiB
Python
Raw 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, 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/<int:company_id>/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/<int:company_id>/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/<int:company_id>')
@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/<int:company_id>/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/<int:user_id>/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/<int:user_id>/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/<int:user_id>/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/<int:company_id>/module/<int:module_id>/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/<int:company_id>/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/<int:license_id>/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/<int:license_id>/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/<int:license_id>/module/<int:module_id>/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/<int:company_id>/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})