from flask import Flask, render_template, request, redirect, url_for from config import Config from app.extensions import db, login_manager, migrate def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) db.init_app(app) login_manager.init_app(app) migrate.init_app(app, db) login_manager.login_view = 'auth.login' login_manager.login_message = 'Bitte melden Sie sich an.' from app.models.user import User from app.models.company import Company from app.models.license import License from app.models.module import Module from app.models.lv import LVPosition from app.models.project import Project from app.models.aufmass import Aufmass from app.models.project_access import ProjectAccess from app.models.position import Position from app.models.contract import Contract from app.models.view_profile import ViewProfile from app.models.company_module import CompanyModule from app.models.user_module import UserModulePermission from app.models.aufmass_typ import AufmassTyp from app.models.aufmass_history import AufmassHistory @login_manager.user_loader def load_user(user_id): user = User.query.get(int(user_id)) if user: from flask import session session.setdefault('font_size', user.font_size or '1') return user from app.routes.auth import auth_bp from app.routes.admin import admin_bp from app.routes.lv import lv_bp from app.routes.aufmass import aufmass_bp from app.routes.export import export_bp from app.routes.modules import modules_bp from app.routes.contracts import contracts_bp from app.routes.views import views_bp from app.routes.superadmin import superadmin_bp from app.routes.custom_modules import custom_modules_bp app.register_blueprint(auth_bp, url_prefix='/auth') app.register_blueprint(admin_bp, url_prefix='/admin') app.register_blueprint(lv_bp, url_prefix='/lv') app.register_blueprint(aufmass_bp, url_prefix='/projekt') app.register_blueprint(export_bp, url_prefix='/export') app.register_blueprint(modules_bp, url_prefix='/modules') app.register_blueprint(contracts_bp, url_prefix='/contracts') app.register_blueprint(views_bp) app.register_blueprint(superadmin_bp, url_prefix='/superadmin') app.register_blueprint(custom_modules_bp, url_prefix='/custom-modules') @app.route('/aufmass/') @app.route('/aufmass/') def _aufmass_redirect(subpath=''): return redirect('/projekt/' + subpath), 301 @app.route('/') def index(): from flask_login import current_user if current_user.is_authenticated: if current_user.is_superadmin(): return redirect(url_for('superadmin.dashboard')) return redirect(url_for('admin.dashboard')) return redirect(url_for('auth.login')) @app.route('/font-size/') def set_font_size(size): from flask import session from flask_login import current_user if size in ('0.8', '0.9', '1', '1.1', '1.25', '1.5'): session['font_size'] = size if current_user.is_authenticated: current_user.font_size = size try: db.session.commit() except: db.session.rollback() return redirect(request.referrer or url_for('admin.dashboard')) @app.template_filter('german_number') def german_number_filter(value, precision=2, zero_dash=False): if value is None or (zero_dash and value == 0): return '\u2013' try: s = f'{float(value):.{precision}f}' parts = s.split('.') int_part = parts[0] dec_part = parts[1] if len(parts) > 1 else '0'*precision int_part = '{:,}'.format(int(int_part)).replace(',', '.') return f'{int_part},{dec_part}' except (ValueError, TypeError): return '\u2013' if zero_dash else '0,00' @app.errorhandler(404) def not_found(e): return render_template('errors/404.html'), 404 @app.errorhandler(500) def server_error(e): return render_template('errors/500.html'), 500 with app.app_context(): db.create_all() _seed_defaults() return app def _seed_defaults(): from app.extensions import db from app.models.module import Module def upsert(name, titel, kategorie, icon, standard, sortierung=0): m = Module.query.filter_by(name=name).first() if m: m.titel = titel m.kategorie = kategorie m.icon = icon m.standard = standard m.sortierung = sortierung else: db.session.add(Module(name=name, titel=titel, kategorie=kategorie, icon=icon, standard=standard, sortierung=sortierung)) # 1. Tote Module entfernen (inkl. FK-Referenzen) for dead_name in ('mfg', 'stoersammler'): old = Module.query.filter_by(name=dead_name).first() if old: from app.models.license import LicenseModule from app.models.company_module import CompanyModule LicenseModule.query.filter_by(module_id=old.id).delete() CompanyModule.query.filter_by(module_id=old.id).delete() db.session.delete(old) db.session.commit() # 2. Alle Module upserten upsert('graben', 'Graben', 'Tiefbau', '🔲', standard=True) upsert('gruben', 'Gruben', 'Tiefbau', '🕳️', standard=True) upsert('gf_montage', 'GF-Montage', 'Glasfaser', '🔗', standard=True) upsert('ftth', 'FTTH', 'Glasfaser', '🏠', standard=True) upsert('kabelzug', 'Kabelzug', 'Tiefbau', '🔌', standard=True) upsert('absperrung', 'Absperrung', 'Tiefbau', '🚧', standard=True) upsert('sas_mecka', 'SAS Meckenbeuren', 'Spezial', '📍', standard=False) upsert('neff_achberg', 'Neff-Achberg', 'Spezial', '🏗️', standard=False) upsert('cu', 'CU', 'Kupfer', '📞', standard=True) upsert('stoerung', 'Störung', 'Service', '🔧', standard=True) upsert('tvum', 'TV/UM', 'Glasfaser', '📺', standard=True) upsert('planung', 'Planung', 'Planung', '📐', standard=True) upsert('zw_rv', 'ZW/RV', 'Tiefbau', '🔩', standard=True) upsert('doku', 'Dokumentation', 'Planung', '📄', standard=True) upsert('sto_sammler', 'Störungs-Sammler', 'Service', '📋', standard=True) db.session.commit() from app.models.aufmass_typ import AufmassTyp if AufmassTyp.query.count() == 0: db.session.add(AufmassTyp(name='Teilaufmaß/AZ', sortierung=1)) db.session.add(AufmassTyp(name='Schlussaufmaß', sortierung=2)) db.session.commit()