Files

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'))