416 lines
19 KiB
Python
416 lines
19 KiB
Python
import os
|
|
from flask import Blueprint, render_template, redirect, url_for, request, flash
|
|
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.license import License, LicenseModule
|
|
from app.models.module import Module
|
|
from app.models.company_module import CompanyModule
|
|
from app.models.user_module import UserModulePermission
|
|
from app.services.license_service import get_aktive_module
|
|
from app.models.project import Project
|
|
from app.models.contract import Contract
|
|
from app.models.lv import LVPosition
|
|
from app.models.project_access import ProjectAccess
|
|
|
|
admin_bp = Blueprint('admin', __name__)
|
|
|
|
def _get_sichtbare_projekte():
|
|
if current_user.is_firmadmin():
|
|
return Project.query.filter_by(company_id=current_user.company_id)
|
|
zugriff_ids = db.session.query(ProjectAccess.project_id).filter_by(
|
|
user_id=current_user.id
|
|
).subquery()
|
|
return Project.query.filter(Project.id.in_(zugriff_ids))
|
|
|
|
@admin_bp.route('/')
|
|
@login_required
|
|
def dashboard():
|
|
if current_user.is_superadmin():
|
|
return redirect(url_for('superadmin.dashboard'))
|
|
modules = get_aktive_module(current_user.company_id, user=current_user)
|
|
projekte_query = _get_sichtbare_projekte()
|
|
projekte_anzahl = projekte_query.filter(Project.status == 'aktiv').count()
|
|
letztes_projekt = projekte_query.order_by(Project.erstellt_am.desc()).first()
|
|
contracts = Contract.query.filter_by(
|
|
company_id=current_user.company_id
|
|
).order_by(Contract.name).all()
|
|
lv_names = [r[0] for r in db.session.query(LVPosition.lv_name).filter_by(
|
|
company_id=current_user.company_id
|
|
).distinct().order_by(LVPosition.lv_name).all()]
|
|
|
|
# Project sums
|
|
projekte = projekte_query.order_by(Project.sm_nr).all()
|
|
from app.models.position import Position
|
|
from app.models.aufmass import Aufmass
|
|
projekte_mit_summe = []
|
|
gesamt_summe = 0.0
|
|
for p in projekte:
|
|
summe = db.session.query(db.func.coalesce(db.func.sum(Position.gesamtpreis), 0)).join(
|
|
Aufmass, Position.aufmass_id == Aufmass.id
|
|
).filter(Aufmass.project_id == p.id).scalar() or 0.0
|
|
projekte_mit_summe.append((p, summe))
|
|
gesamt_summe += summe
|
|
|
|
# Employee count
|
|
mitarbeiter_anzahl = User.query.filter_by(company_id=current_user.company_id).count()
|
|
|
|
# License info
|
|
licenses = License.query.filter_by(company_id=current_user.company_id, aktiv=True).all()
|
|
license_max_ma = sum(l.max_mitarbeiter for l in licenses)
|
|
license_module_count = 0
|
|
license_module_used = 0
|
|
for lic in licenses:
|
|
lms = LicenseModule.query.filter_by(license_id=lic.id, aktiv=True).all()
|
|
license_module_count += len(lms)
|
|
license_count = len(licenses)
|
|
|
|
# Profiles
|
|
from app.models.view_profile import ViewProfile
|
|
profile_list = ViewProfile.query.filter_by(
|
|
user_id=current_user.id, view_type='dashboard'
|
|
).order_by(ViewProfile.is_default.desc(), ViewProfile.name).all()
|
|
active_profile = next((p for p in profile_list if p.is_default), profile_list[0] if profile_list else None)
|
|
|
|
return render_template('admin/dashboard.html', modules=modules, titel='Dashboard',
|
|
projekte_anzahl=projekte_anzahl, letztes_projekt=letztes_projekt,
|
|
contracts=contracts, lv_names=lv_names,
|
|
projekte_mit_summe=projekte_mit_summe, gesamt_summe=gesamt_summe,
|
|
mitarbeiter_anzahl=mitarbeiter_anzahl,
|
|
license_count=license_count, license_max_ma=license_max_ma,
|
|
license_module_count=license_module_count,
|
|
license_module_used=license_module_used,
|
|
profile_list=profile_list, active_profile=active_profile)
|
|
|
|
@admin_bp.route('/profil', methods=['GET', 'POST'])
|
|
@login_required
|
|
def profil():
|
|
if request.method == 'POST':
|
|
current_user.vorname = request.form.get('vorname', '').strip()
|
|
current_user.nachname = request.form.get('nachname', '').strip()
|
|
if request.form.get('password'):
|
|
current_user.set_password(request.form['password'])
|
|
db.session.commit()
|
|
flash('Profil aktualisiert.', 'success')
|
|
return render_template('admin/profil.html', titel='Profil')
|
|
|
|
@admin_bp.route('/firma')
|
|
@login_required
|
|
def firma():
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.dashboard'))
|
|
company = Company.query.get(current_user.company_id)
|
|
users = User.query.filter_by(company_id=current_user.company_id).all()
|
|
licenses = License.query.filter_by(company_id=current_user.company_id).all()
|
|
modules = get_aktive_module(company.id, user=current_user)
|
|
return render_template('admin/firma.html', company=company, users=users, licenses=licenses,
|
|
modules=modules, titel='Firma')
|
|
|
|
@admin_bp.route('/mitarbeiter/neu', methods=['POST'])
|
|
@login_required
|
|
def mitarbeiter_neu():
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
email = request.form.get('email', '').strip()
|
|
if User.query.filter_by(email=email).first():
|
|
flash('E-Mail existiert bereits.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
rolle = request.form.get('rolle', 'mitarbeiter')
|
|
if rolle not in ('mitarbeiter', 'firmadmin'):
|
|
rolle = 'mitarbeiter'
|
|
user = User(
|
|
company_id=current_user.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(request.form.get('password', '123456'))
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
flash(f'Mitarbeiter {user.full_name} angelegt.', 'success')
|
|
return redirect(url_for('admin.firma'))
|
|
|
|
@admin_bp.route('/projekt/schnellanlage', methods=['POST'])
|
|
@login_required
|
|
def projekt_schnellanlage():
|
|
if not current_user.is_firmadmin() and not current_user.is_superadmin() and not current_user.darf_projekte_anlegen:
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.dashboard'))
|
|
contract_id = request.form.get('contract_id', type=int)
|
|
contract = Contract.query.get(contract_id) if contract_id else None
|
|
from datetime import datetime as dt
|
|
project = Project(
|
|
company_id=current_user.company_id,
|
|
contract_id=contract_id,
|
|
sm_nr=request.form.get('sm_nr', '').strip(),
|
|
bezeichnung=request.form.get('bezeichnung', '').strip(),
|
|
vertrag=contract.name if contract else '',
|
|
lv_name=request.form.get('lv_name', '').strip(),
|
|
status='aktiv', erstellt_von=current_user.id,
|
|
)
|
|
db.session.add(project)
|
|
db.session.commit()
|
|
flash(f'Projekt {project.sm_nr} angelegt.', 'success')
|
|
return redirect(url_for('aufmass.aufmass_list', project_id=project.id))
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/toggle')
|
|
@login_required
|
|
def mitarbeiter_toggle(user_id):
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id:
|
|
flash('Zugriff verweigert.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
user.aktiv = not user.aktiv
|
|
db.session.commit()
|
|
flash(f'Benutzer {"aktiviert" if user.aktiv else "deaktiviert"}.', 'success')
|
|
return redirect(url_for('admin.firma'))
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/rechte', methods=['GET', 'POST'])
|
|
@login_required
|
|
def mitarbeiter_rechte(user_id):
|
|
if not current_user.is_firmadmin() and not current_user.is_superadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.dashboard'))
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id and not current_user.is_superadmin():
|
|
flash('Zugriff verweigert.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
if request.method == 'POST':
|
|
user.darf_projekte_anlegen = bool(request.form.get('darf_projekte_anlegen'))
|
|
user.darf_lv_verwalten = bool(request.form.get('darf_lv_verwalten'))
|
|
user.darf_preise_sehen = bool(request.form.get('darf_preise_sehen'))
|
|
user.darf_aufmass_verwalten = bool(request.form.get('darf_aufmass_verwalten'))
|
|
user.darf_evergabe_nutzen = bool(request.form.get('darf_evergabe_nutzen'))
|
|
user.darf_kopfdaten_holen = bool(request.form.get('darf_kopfdaten_holen'))
|
|
user.darf_aufmass_uebertragen = bool(request.form.get('darf_aufmass_uebertragen'))
|
|
db.session.commit()
|
|
flash('Rechte aktualisiert.', 'success')
|
|
return redirect(url_for('admin.firma'))
|
|
projekte = Project.query.filter_by(company_id=current_user.company_id).order_by(Project.sm_nr).all()
|
|
zugriffe = {pa.project_id: pa for pa in ProjectAccess.query.filter_by(user_id=user.id).all()}
|
|
modules = get_aktive_module(current_user.company_id, user=current_user)
|
|
user_modules = {um.module_id for um in UserModulePermission.query.filter_by(user_id=user.id, aktiv=True).all()}
|
|
return render_template('admin/rechte.html', user=user, projekte=projekte, zugriffe=zugriffe,
|
|
modules=modules, user_modules=user_modules, titel='Rechte')
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/projekt-zugriff', methods=['POST'])
|
|
@login_required
|
|
def mitarbeiter_projekt_zugriff(user_id):
|
|
if not current_user.is_firmadmin():
|
|
return 'Keine Berechtigung', 403
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id:
|
|
return 'Zugriff verweigert', 403
|
|
project_id = request.form.get('project_id', type=int)
|
|
zugriff = request.form.get('zugriff', '')
|
|
if not project_id or zugriff not in ('lesen', 'schreiben', ''):
|
|
return 'Ungültige Daten', 400
|
|
existing = ProjectAccess.query.filter_by(user_id=user.id, project_id=project_id).first()
|
|
if zugriff:
|
|
if existing:
|
|
existing.zugriff = zugriff
|
|
else:
|
|
pa = ProjectAccess(user_id=user.id, project_id=project_id, zugriff=zugriff)
|
|
db.session.add(pa)
|
|
elif existing:
|
|
db.session.delete(existing)
|
|
db.session.commit()
|
|
return redirect(url_for('admin.mitarbeiter_rechte', user_id=user.id))
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/bearbeiten', methods=['GET', 'POST'])
|
|
@login_required
|
|
def mitarbeiter_bearbeiten(user_id):
|
|
if not current_user.is_firmadmin() and not current_user.is_superadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id and not current_user.is_superadmin():
|
|
flash('Zugriff verweigert.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
if request.method == 'POST':
|
|
email = request.form.get('email', '').strip()
|
|
if email and email != user.email:
|
|
if User.query.filter_by(email=email).first():
|
|
flash('E-Mail existiert bereits.', 'danger')
|
|
return redirect(url_for('admin.mitarbeiter_bearbeiten', user_id=user.id))
|
|
user.email = email
|
|
user.vorname = request.form.get('vorname', '').strip()
|
|
user.nachname = request.form.get('nachname', '').strip()
|
|
rolle = request.form.get('rolle', '')
|
|
if rolle in ('mitarbeiter', 'firmadmin'):
|
|
user.rolle = rolle
|
|
if request.form.get('password'):
|
|
user.set_password(request.form['password'])
|
|
user.darf_projekte_anlegen = bool(request.form.get('darf_projekte_anlegen'))
|
|
user.darf_lv_verwalten = bool(request.form.get('darf_lv_verwalten'))
|
|
user.darf_preise_sehen = bool(request.form.get('darf_preise_sehen'))
|
|
user.darf_aufmass_verwalten = bool(request.form.get('darf_aufmass_verwalten'))
|
|
user.darf_evergabe_nutzen = bool(request.form.get('darf_evergabe_nutzen'))
|
|
user.darf_kopfdaten_holen = bool(request.form.get('darf_kopfdaten_holen'))
|
|
user.darf_aufmass_uebertragen = bool(request.form.get('darf_aufmass_uebertragen'))
|
|
db.session.commit()
|
|
flash(f'Benutzer {user.full_name} aktualisiert.', 'success')
|
|
if current_user.is_superadmin():
|
|
return redirect(url_for('superadmin.firma_detail', company_id=user.company_id))
|
|
return redirect(url_for('admin.firma'))
|
|
company = Company.query.get(user.company_id)
|
|
return render_template('admin/mitarbeiter_bearbeiten.html', user=user, company=company, titel='Benutzer bearbeiten')
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/loeschen', methods=['POST'])
|
|
@login_required
|
|
def mitarbeiter_loeschen(user_id):
|
|
if not current_user.is_firmadmin() and not current_user.is_superadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id and not current_user.is_superadmin():
|
|
flash('Zugriff verweigert.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
if user.id == current_user.id:
|
|
flash('Sie können sich nicht selbst löschen.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
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('admin.firma'))
|
|
|
|
@admin_bp.route('/mitarbeiter/<int:user_id>/module/<int:module_id>/toggle')
|
|
@login_required
|
|
def mitarbeiter_module_toggle(user_id, module_id):
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
user = User.query.get_or_404(user_id)
|
|
if user.company_id != current_user.company_id:
|
|
flash('Zugriff verweigert.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
if user.is_firmadmin():
|
|
flash('Firmadmin hat automatisch alle Module.', 'info')
|
|
return redirect(url_for('admin.mitarbeiter_rechte', user_id=user.id))
|
|
um = UserModulePermission.query.filter_by(user_id=user.id, module_id=module_id).first()
|
|
if um:
|
|
um.aktiv = not um.aktiv
|
|
else:
|
|
um = UserModulePermission(user_id=user.id, module_id=module_id, aktiv=True)
|
|
db.session.add(um)
|
|
db.session.commit()
|
|
return redirect(url_for('admin.mitarbeiter_rechte', user_id=user.id))
|
|
|
|
@admin_bp.route('/avatar/upload', methods=['POST'])
|
|
@login_required
|
|
def avatar_upload():
|
|
file = request.files.get('avatar')
|
|
if not file:
|
|
flash('Keine Datei ausgewählt.', 'danger')
|
|
return redirect(url_for('admin.profil'))
|
|
import os
|
|
from flask import current_app
|
|
ext = os.path.splitext(file.filename)[1].lower()
|
|
if ext not in ('.png', '.jpg', '.jpeg', '.gif'):
|
|
flash('Nur PNG, JPG, GIF erlaubt.', 'danger')
|
|
return redirect(url_for('admin.profil'))
|
|
upload_dir = os.path.join(current_app.root_path, 'static', 'avatars')
|
|
os.makedirs(upload_dir, exist_ok=True)
|
|
filename = f'avatar_{current_user.id}{ext}'
|
|
file.save(os.path.join(upload_dir, filename))
|
|
for old_ext in ('.png', '.jpg', '.jpeg', '.gif'):
|
|
old_path = os.path.join(upload_dir, f'avatar_{current_user.id}{old_ext}')
|
|
if old_ext != ext and os.path.exists(old_path):
|
|
os.remove(old_path)
|
|
current_user.profile_image = filename
|
|
db.session.commit()
|
|
flash('Profilbild aktualisiert.', 'success')
|
|
return redirect(url_for('admin.profil'))
|
|
|
|
@admin_bp.route('/avatar/entfernen', methods=['POST'])
|
|
@login_required
|
|
def avatar_entfernen():
|
|
import os
|
|
from flask import current_app
|
|
if current_user.profile_image:
|
|
path = os.path.join(current_app.root_path, 'static', 'avatars', current_user.profile_image)
|
|
if os.path.exists(path):
|
|
os.remove(path)
|
|
current_user.profile_image = None
|
|
db.session.commit()
|
|
flash('Profilbild entfernt.', 'success')
|
|
return redirect(url_for('admin.profil'))
|
|
|
|
@admin_bp.route('/firma/logo')
|
|
@login_required
|
|
def firma_logo():
|
|
if not current_user.is_firmadmin():
|
|
return '', 403
|
|
company = Company.query.get(current_user.company_id)
|
|
if not company or not company.logo or not os.path.exists(company.logo):
|
|
return '', 404
|
|
from flask import send_file
|
|
return send_file(company.logo)
|
|
|
|
@admin_bp.route('/firma/logo-upload', methods=['POST'])
|
|
@login_required
|
|
def firma_logo_upload():
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
company = Company.query.get(current_user.company_id)
|
|
if request.form.get('delete'):
|
|
if company.logo and os.path.exists(company.logo):
|
|
os.remove(company.logo)
|
|
company.logo = None
|
|
db.session.commit()
|
|
flash('Firmenlogo entfernt.', 'success')
|
|
return redirect(url_for('admin.firma'))
|
|
file = request.files.get('logo')
|
|
if not file or file.filename == '':
|
|
flash('Keine Datei ausgewählt.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
ext = os.path.splitext(file.filename)[1].lower()
|
|
if ext not in ('.png', '.jpg', '.jpeg', '.gif', '.webp'):
|
|
flash('Nur Bildformate: PNG, JPG, GIF, WebP.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
from flask import current_app
|
|
upload_dir = os.path.join(current_app.root_path, '..', 'data', 'uploads', 'logos')
|
|
os.makedirs(upload_dir, exist_ok=True)
|
|
filename = f'logo_{company.id}{ext}'
|
|
filepath = os.path.join(upload_dir, filename)
|
|
file.save(filepath)
|
|
company.logo = filepath
|
|
db.session.commit()
|
|
flash('Firmenlogo erfolgreich hochgeladen.', 'success')
|
|
return redirect(url_for('admin.firma'))
|
|
|
|
@admin_bp.route('/firma/evergabe', methods=['POST'])
|
|
@login_required
|
|
def firma_evergabe_save():
|
|
if not current_user.is_firmadmin():
|
|
flash('Keine Berechtigung.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
company = Company.query.get(current_user.company_id)
|
|
if not company.evergabe_aktiviert:
|
|
flash('E-Vergabe ist für Ihre Firma nicht freigeschaltet.', 'danger')
|
|
return redirect(url_for('admin.firma'))
|
|
company.evergabe_benutzer = request.form.get('evergabe_benutzer', '').strip()
|
|
company.evergabe_passwort = request.form.get('evergabe_passwort', '').strip()
|
|
company.evergabe_name = request.form.get('evergabe_name', '').strip()
|
|
db.session.commit()
|
|
flash('E-Vergabe Logindaten gespeichert.', 'success')
|
|
return redirect(url_for('admin.firma'))
|