""" Beheer › Database: tabellen van de school aanvullen vanuit Excel. Toestellen: GET /beheer/database/toestellen → pagina (upload + sjabloon) GET /beheer/database/toestellen/sjabloon.xlsx → sjabloon downloaden POST /beheer/database/toestellen/controleren → Excel lezen en voorbeeld tonen (niets bewaard) POST /beheer/database/toestellen/importeren → bevestigen: het voorbeeld uitvoeren """ import json from flask import Blueprint, render_template, request, Response, redirect, url_for, flash, session from blueprints.auth import login_required from database import execute_query from rechten import recht_vereist, log from services import toestellen_import as ti from tenancy import huidige_school database_bp = Blueprint('database_beheer', __name__) @database_bp.route('/toestellen', methods=['GET']) @login_required @recht_vereist('database_toestellen') def toestellen(): try: aantal = execute_query("SELECT COUNT(*) AS n FROM Toestellen")[0]['n'] except Exception: aantal = None return render_template('database_toestellen.html', plan=None, aantal=aantal, kolommen=ti.KOLOMMEN, statussen=ti.STATUSSEN, max_rijen=ti.MAX_RIJEN) @database_bp.route('/toestellen/sjabloon.xlsx', methods=['GET']) @login_required @recht_vereist('database_toestellen') def toestellen_sjabloon(): school = huidige_school() data = ti.maak_sjabloon(school.naam) return Response(data, mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers={'Content-Disposition': f"attachment; filename=Sjabloon_toestellen_{school.code}.xlsx"}) @database_bp.route('/toestellen/controleren', methods=['POST']) @login_required @recht_vereist('database_toestellen') def toestellen_controleren(): terug = redirect(url_for('database_beheer.toestellen')) bestand = request.files.get('bestand') if not bestand or not bestand.filename: flash("Kies eerst een Excel-bestand.", "error") return terug if not bestand.filename.lower().endswith(('.xlsx', '.xlsm')): flash("Enkel Excel-bestanden (.xlsx) kunnen ingelezen worden. Sla een .xls of .csv eerst op als .xlsx.", "error") return terug modus = 'overschrijven' if request.form.get('modus') == 'overschrijven' else 'aanvullen' try: rijen, onbekend = ti.lees_excel(bestand) except ti.ImportFout as e: flash(str(e), "error") return terug if not rijen: flash("Het bestand bevat geen toestellen (enkel kolomkoppen of lege rijen).", "warning") return terug try: plan = ti.maak_plan(rijen, modus) except Exception as e: flash(f"Controleren mislukt: {e}", "error") return terug return render_template('database_toestellen.html', plan=plan, bestandsnaam=bestand.filename, onbekend=onbekend, rijen_json=json.dumps(rijen, ensure_ascii=False), kolommen=ti.KOLOMMEN, statussen=ti.STATUSSEN, max_rijen=ti.MAX_RIJEN, aantal=None) @database_bp.route('/toestellen/importeren', methods=['POST']) @login_required @recht_vereist('database_toestellen') def toestellen_importeren(): terug = redirect(url_for('database_beheer.toestellen')) modus = 'overschrijven' if request.form.get('modus') == 'overschrijven' else 'aanvullen' try: rijen = json.loads(request.form.get('rijen') or '[]') if not isinstance(rijen, list) or len(rijen) > ti.MAX_RIJEN or not all(isinstance(r, dict) for r in rijen): raise ValueError except ValueError: flash("De gegevens van het voorbeeld zijn ongeldig. Upload het bestand opnieuw.", "error") return terug try: plan, nieuw, bijgewerkt = ti.voer_uit(rijen, modus, session.get('user')) except Exception as e: flash(f"Importeren mislukt, er is niets bewaard: {e}", "error") return terug t = plan['tellers'] log(f"Toestellen geïmporteerd uit Excel: {nieuw} nieuw, {bijgewerkt} bijgewerkt ({modus})") ongewijzigd = f", {t['ongewijzigd']} ongewijzigd" if t['ongewijzigd'] else "" flash(f"Import klaar: {nieuw} toestel(len) toegevoegd, {bijgewerkt} bijgewerkt{ongewijzigd}.", "success") if t['fout']: flash(f"{t['fout']} rij(en) met een fout werden overgeslagen.", "warning") return terug