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

+96
View File
@@ -0,0 +1,96 @@
"""Leerlingfacturatie: facturen en creditnota's van de leerlingen van de actieve school (Informat Rekeningen API)."""
import csv
import io
import time
from datetime import date
from flask import Blueprint, render_template, request, Response, redirect, url_for, flash
from blueprints.auth import login_required
from rechten import recht_vereist
from services.facturatie_service import FacturatieService, FacturatieFout, schooljaren
from tenancy import huidige_school
facturatie_bp = Blueprint('facturatie', __name__)
STATUS_LABELS = {'open': 'Open', 'gedeeltelijk': 'Gedeeltelijk betaald', 'betaald': 'Betaald', 'credit': 'Creditnota'}
def _schooljaar():
jaren = schooljaren()
gekozen = request.args.get('schooljaar') or ''
return gekozen if gekozen in jaren else jaren[0], jaren
@facturatie_bp.route("/", methods=["GET"])
@login_required
@recht_vereist('leerlingfacturatie')
def overzicht():
school = huidige_school()
schooljaar, jaren = _schooljaar()
rijen, groepen, fout, opgehaald = [], [], None, None
if not school.heeft('INFORMAT_ID', 'INFORMAT_SECRET'):
fout = "Informat is nog niet ingesteld voor deze school (🔌 Koppelingen › Informat)."
else:
try:
rijen, groepen, t = FacturatieService().facturen(schooljaar, vernieuw=bool(request.args.get('vernieuw')))
opgehaald = time.strftime('%d-%m-%Y %H:%M', time.localtime(t))
except FacturatieFout as e:
fout = str(e)
except Exception as e:
fout = f"Facturen ophalen mislukt: {e}"
return render_template("facturatie.html", rijen=rijen, groepen=groepen, fout=fout, opgehaald=opgehaald,
schooljaar=schooljaar, jaren=jaren, school_naam=school.naam, labels=STATUS_LABELS,
today=date.today())
@facturatie_bp.route("/pdf/<toegangsgroep>/<invoice_id>", methods=["GET"])
@login_required
@recht_vereist('leerlingfacturatie')
def pdf(toegangsgroep, invoice_id):
try:
inhoud = FacturatieService().pdf(toegangsgroep, invoice_id)
except FacturatieFout as e:
flash(str(e), "error")
return redirect(url_for('facturatie.overzicht', schooljaar=request.args.get('schooljaar')))
except Exception as e:
flash(f"De PDF kon niet opgehaald worden: {e}", "error")
return redirect(url_for('facturatie.overzicht', schooljaar=request.args.get('schooljaar')))
naam = (request.args.get('naam') or 'factuur').replace('"', '')
return Response(inhoud, mimetype='application/pdf',
headers={'Content-Disposition': f'inline; filename="{naam}.pdf"'})
@facturatie_bp.route("/export.csv", methods=["GET"])
@login_required
@recht_vereist('leerlingfacturatie')
def export():
"""CSV met dezelfde filters als de tabel: ?schooljaar=&groep=&status=&zoek="""
schooljaar, _ = _schooljaar()
try:
rijen, _, _ = FacturatieService().facturen(schooljaar)
except Exception as e:
flash(str(e), "error")
return redirect(url_for('facturatie.overzicht', schooljaar=schooljaar))
groep, status = request.args.get('groep') or '', request.args.get('status') or ''
zoek = (request.args.get('zoek') or '').strip().lower()
if groep:
rijen = [r for r in rijen if str(r['groep_id']) == groep]
if status == 'onbetaald':
rijen = [r for r in rijen if r['status'] in ('open', 'gedeeltelijk')]
elif status:
rijen = [r for r in rijen if r['status'] == status]
if zoek:
rijen = [r for r in rijen if zoek in ' '.join(str(r[k]) for k in ('naam', 'voornaam', 'stamnummer', 'klas', 'groep', 'ogm')).lower()]
bedrag = lambda x: f"{x:.2f}".replace('.', ',')
datum = lambda d: d.strftime('%d/%m/%Y') if d else ''
buf = io.StringIO()
w = csv.writer(buf, delimiter=';')
w.writerow(['Naam', 'Voornaam', 'Stamnummer', 'Klas', 'Factuurgroep', 'Soort', 'Datum', 'Vervaldag', 'Totaal', 'Betaald',
'Open', 'Status', 'Gestructureerde mededeling'])
for r in rijen:
w.writerow([r['naam'], r['voornaam'], r['stamnummer'], r['klas'], r['groep'], 'Creditnota' if r['type'] == 'CREDIT_NOTE' else 'Factuur',
datum(r['datum']), datum(r['vervaldag']), bedrag(r['totaal'] * r['teken']), bedrag(r['betaald'] * r['teken']), bedrag(r['open'] * r['teken']),
STATUS_LABELS.get(r['status'], r['status']), r['ogm']])
return Response('' + buf.getvalue(), mimetype='text/csv; charset=utf-8', headers={
'Content-Disposition': f"attachment; filename=Facturen_{huidige_school().code}_{schooljaar}_{date.today():%Y%m%d}.csv"})