377 lines
17 KiB
Python
377 lines
17 KiB
Python
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})
|