Initial commit – AufmaßCreater v2.35

This commit is contained in:
2026-06-10 11:03:43 +02:00
commit 84c933ea9c
2823 changed files with 490495 additions and 0 deletions
+376
View File
@@ -0,0 +1,376 @@
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})