"""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//", 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"})