PIVOT 1.1.0

This commit is contained in:
marijn committed 2026-10-14 13:34:37 +02:00
commit 8a53234eb6
161 files changed
+31109

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
"""
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