Initial commit

This commit is contained in:
Kristof committed 2026-10-14 12:29:29 +02:00
commit 536ff43ce4
158 files changed
+30355

No files matched your search

+603
View File
@@ -0,0 +1,603 @@
import sys
from flask import Blueprint, render_template, request, session, redirect, url_for, flash, jsonify
from database import execute_query
from services import CIP_inleveren
from services import CIP_uitdelen
from blueprints.auth import login_required
from rechten import recht_vereist
from database import execute_query, execute_insert, get_db_connection
from services import toestellen as tst
from blueprints.google_admin import google_service
from googleapiclient.errors import HttpError
# We noemen de blueprint 'devices'
devices_bp = Blueprint('devices', __name__)
#-------------------------------------------------
# --- ROUTE TOESTELLEN (Inventaris Toestellen) ---
#-------------------------------------------------
@devices_bp.route("/inventaris", methods=["GET", "POST"])
@login_required
@recht_vereist('toestellen_beheren')
def inventaris_toestellen():
query = request.form.get("query", "") if request.method == "POST" else request.args.get("query", "")
message = None
search_term = f"%{query}%"
try:
tst.ensure_geblokkeerd()
geblokkeerd = "t.Geblokkeerd"
except Exception as e: # bv. geen ALTER-rechten: pagina blijft werken
sys.stderr.write(f"Kolom Toestellen.Geblokkeerd: {e}\n")
geblokkeerd = "0"
sql = f"""
SELECT * FROM (
SELECT t.Toestel_ID, t.Schooleigen_ID AS KAMXM_nummer, sp.Signpostnummer, t.Serienummer, t.Type,
DATE_FORMAT(sp.Garantie, '%d-%m-%Y') AS Garantie, t.Status, {geblokkeerd} AS Geblokkeerd,
{tst.LAATSTE_OPMERKING} AS Opmerking
FROM Toestellen t
LEFT JOIN Signpost_DB sp ON sp.Toestel_ID = t.Toestel_ID
) x
WHERE CONCAT_WS(' ', KAMXM_nummer, Signpostnummer, Serienummer, Type, Opmerking) LIKE %s
ORDER BY KAMXM_nummer IS NULL, KAMXM_nummer
"""
results = execute_query(sql, (search_term,))
if query and not results:
message = f"Geen resultaten gevonden voor '{query}'."
elif not results:
message = "Er zijn momenteel geen toestellen in de inventaris."
return render_template("toestellen.html", results=results, query=query, message=message)
@devices_bp.route("/google_import", methods=["POST"])
@login_required
@recht_vereist('toestellen_beheren')
def google_import():
"""Chromebooks uit Google Admin in de inventaris zetten: nieuwe serienummers toevoegen en een ontbrekend model
aanvullen. Er wordt niets verwijderd of overschreven."""
from config import Config
# Leeg = alle Chromebooks van het domein; anders enkel deze OU's (met sub-OU's)
ous = [o.strip() for o in str(Config.CHROMEBOOK_OUS or '').split(',') if o.strip()]
try:
toestellen = google_service.lijst_chromebooks(ous)
except HttpError as e:
fout = str(e)
if 'INVALID_OU_ID' in fout or 'Invalid Input: OrgUnit' in fout or ('400' in fout and ous):
flash(f"Een OU bestaat niet in Google: {', '.join(ous)}. Pas 'OU's van de Chromebooks' aan bij "
"🔌 Koppelingen van deze school (volledig pad, bv. /ChromeBooks Medewerkers).", "error")
else:
flash(f"Chromebooks ophalen bij Google mislukt: {e}", "error")
return redirect(url_for("devices.inventaris_toestellen"))
except Exception as e:
flash(f"Chromebooks ophalen bij Google mislukt: {e}", "error")
return redirect(url_for("devices.inventaris_toestellen"))
try:
tst.ensure_geblokkeerd()
kol = "Geblokkeerd"
except Exception:
kol = "NULL AS Geblokkeerd"
bestaand = {(r['Serienummer'] or '').strip().upper(): r for r in
execute_query(f"SELECT Toestel_ID, Serienummer, Type, {kol} FROM Toestellen WHERE Serienummer IS NOT NULL")}
nieuw = aangevuld = blok_bijgewerkt = 0
fouten = []
# Het veld Item-ID (Asset ID) uit Google wordt bewust NIET overgenomen: het wordt soms voor andere info gebruikt
# (bv. een lokaal) en is dan niet uniek. Het toestelnummer van de school vul je in de inventaris of via Signpost.
for d in toestellen:
serie = d['serialNumber'].strip()
model = (d.get('model') or '').strip()[:100] or None
try:
rij = bestaand.get(serie.upper())
in_google = 1 if d.get('status') == 'DISABLED' else 0
if not rij:
nieuw_id = execute_insert("INSERT INTO Toestellen (Serienummer, Type, Status) VALUES (%s, %s, 'Beschikbaar')",
(serie, model))
if in_google and kol == "Geblokkeerd":
tst.zet_geblokkeerd(nieuw_id, True)
bestaand[serie.upper()] = {'Type': model}
nieuw += 1
continue
if model and not rij['Type']:
execute_query("UPDATE Toestellen SET Type = %s WHERE Toestel_ID = %s", (model, rij['Toestel_ID']),
commit=True)
aangevuld += 1
# Blokkeerstatus overnemen uit Google (daar kan het ook rechtstreeks gewijzigd zijn)
if rij.get('Geblokkeerd') is not None and int(rij['Geblokkeerd'] or 0) != in_google:
tst.zet_geblokkeerd(rij['Toestel_ID'], in_google)
blok_bijgewerkt += 1
except Exception as e:
fouten.append(f"{serie}: {e}")
sys.stderr.write(f"Chromebook-import {serie}: {e}\n")
bereik = f" uit {', '.join(ous)}" if ous else ""
flash(f"{len(toestellen)} Chromebook(s) gevonden in Google{bereik}: {nieuw} nieuw in de inventaris, "
f"{aangevuld} aangevuld, {len(toestellen) - nieuw - aangevuld - len(fouten)} stonden er al."
+ (f" Blokkeerstatus bijgewerkt bij {blok_bijgewerkt} toestel(len)." if blok_bijgewerkt else ""), "success")
if fouten:
flash(f"{len(fouten)} toestel(len) konden niet verwerkt worden: " + "; ".join(fouten[:5])
+ (" …" if len(fouten) > 5 else ""), "error")
return redirect(url_for("devices.inventaris_toestellen"))
@devices_bp.route("/google_info", methods=["GET"])
@login_required
@recht_vereist('toestellen_beheren')
def google_info():
"""JSON met gebruiksinfo uit Google Admin voor één toestel (voor de knop 'Google-info')."""
try:
info, fout = google_service.chromebook_info(request.args.get('serie', ''))
except Exception as e:
info, fout = None, f"Google gaf een fout: {e}"
if fout:
return jsonify(ok=False, fout=fout)
# Naam van de leerling erbij als het e-mailadres in de leerlingenlijst staat
emails = [g['email'] for g in info['gebruikers'] if g['email']]
namen = {}
if emails:
try:
# Gebruikersnaam staat er soms als volledig e-mailadres, soms enkel het deel vóór de @
sleutels = list(dict.fromkeys([e.lower() for e in emails] + [e.split('@')[0].lower() for e in emails]))
plek = ','.join(['%s'] * len(sleutels))
for r in execute_query(f"SELECT Gebruikersnaam, Voornaam, Naam FROM Leerlingen WHERE Gebruikersnaam IN ({plek})",
tuple(sleutels)):
namen[(r['Gebruikersnaam'] or '').lower()] = f"{r.get('Voornaam') or ''} {r.get('Naam') or ''}".strip()
except Exception:
pass
for g in info['gebruikers']:
g['naam'] = namen.get(g['email'].lower()) or namen.get(g['email'].split('@')[0].lower(), '')
return jsonify(ok=True, info=info)
#-----------------------------------------------------
# Functie achter bewerk toestel knop
#-----------------------------------------------------
@devices_bp.route("/bewerk_toestel", methods=["POST"])
@login_required
@recht_vereist('toestellen_beheren')
def bewerk_toestel():
"""Verwerkt de POST-gegevens om een toestelrecord bij te werken."""
# Gebruik de gegevens uit het formulier
toestel_id = request.form.get("toestel_id")
schooleigen_id = (request.form.get("schooleigen_id") or "").strip() or None
serienummer = request.form.get("serienummer")
signpostnummer = request.form.get("signpostnummer")
type_toestel = request.form.get("type")
garantie = request.form.get("garantie")
opmerking = request.form.get("opmerking")
query = request.form.get("query", "")
if not toestel_id:
flash("Fout: Uniek nummer ontbreekt. Kan toestel niet bijwerken.", "error")
return redirect(url_for("devices.inventaris_toestellen"))
try:
tst.lees_datum(garantie) # eerst valideren, zodat er niets half bewaard wordt
execute_query("UPDATE Toestellen SET Schooleigen_ID = %s, Serienummer = %s, Type = %s WHERE Toestel_ID = %s",
(schooleigen_id, (serienummer or '').strip() or None, (type_toestel or '').strip() or None,
toestel_id), commit=True)
tst.bewaar_signpost(toestel_id, signpostnummer, garantie)
tst.voeg_opmerking_toe(toestel_id, opmerking, session.get('user'))
flash(f"Toestel {schooleigen_id or serienummer} succesvol bijgewerkt.", "success")
except ValueError as err:
flash(str(err), "error")
except Exception as err:
flash(f"Databasefout bij het bewerken van het toestel: {err}", "error")
# BELANGRIJK: Gebruik de blueprint prefix 'devices.' in de redirect
return redirect(url_for("devices.inventaris_toestellen", query=query))
#-------------------------------------------------
# --- ROUTE EIGENDOM (koppeling toestel-leerling) ---
#-------------------------------------------------
@devices_bp.route("/eigendom", methods=["GET", "POST"])
@login_required
@recht_vereist('eigendom_bekijken')
def inventaris_eigendom():
query = (request.form.get("query") or request.args.get("query") or "").strip()
# Alle huidige leerlingen, met het toestel dat ze nu hebben (of zonder toestel). Vertrokken leerlingen enkel
# als ze nog een toestel hebben. Een leerling met twee toestellen staat er twee keer in.
# Met zoekterm: elk woord moet voorkomen in internnummer, naam, gebruikersnaam, SS-nummer, toestel of opmerkingen.
try:
from services.sync_service import zorg_vertrokken_kolom
zorg_vertrokken_kolom()
vertrokken = "l.Vertrokken_op IS NULL OR "
except Exception:
vertrokken = ""
sql = f"""
SELECT * FROM (
SELECT e.Eigendom_ID, l.Internnummer AS internnummer, l.Voornaam, l.Naam, l.Gebruikersnaam, l.SS_nummer,
t.Schooleigen_ID AS kamxm_nummer, t.Serienummer, e.Datum_toekenning,
IFNULL(e.Status, 0) AS locked, (e.Eigendom_ID IS NOT NULL) AS heeft_toestel,
(SELECT GROUP_CONCAT(o.Opmerking ORDER BY o.Timestamp SEPARATOR ' | ')
FROM Opmerkingen o WHERE o.Toestel_ID = t.Toestel_ID) AS opmerkingen
FROM Leerlingen l
LEFT JOIN Eigendom e ON e.Internnummer = l.Internnummer AND e.Datum_inlevering IS NULL
LEFT JOIN Toestellen t ON t.Toestel_ID = e.Toestel_ID
WHERE {vertrokken}e.Eigendom_ID IS NOT NULL
) x
"""
woorden = query.split()[:5]
if woorden:
sql += " WHERE " + " AND ".join(
["LOWER(CONCAT_WS(' ', internnummer, Voornaam, Naam, Gebruikersnaam, SS_nummer, IFNULL(kamxm_nummer, ''), "
"IFNULL(Serienummer, ''), IFNULL(opmerkingen, ''))) LIKE %s"] * len(woorden))
sql += " ORDER BY heeft_toestel DESC, Datum_toekenning DESC, Naam, Voornaam, internnummer"
results = execute_query(sql, tuple(f"%{w.lower()}%" for w in woorden))
return render_template("inventaris.html", results=results, query=query)
@devices_bp.route("/eigendom/historie/<internnummer>", methods=["GET"])
@login_required
@recht_vereist('eigendom_bekijken')
def eigendom_historie(internnummer):
"""Alle toestellen die een leerling ooit gehad heeft (ook ingeleverde), nieuwste eerst."""
from datetime import date
rows = execute_query("""
SELECT e.Eigendom_ID, e.Datum_toekenning, e.Datum_inlevering, IFNULL(e.Status, 0) AS locked,
t.Schooleigen_ID, t.Serienummer, t.Type,
(SELECT GROUP_CONCAT(o.Opmerking ORDER BY o.Timestamp SEPARATOR ' | ')
FROM Opmerkingen o WHERE o.Toestel_ID = t.Toestel_ID) AS opmerkingen
FROM Eigendom e INNER JOIN Toestellen t ON t.Toestel_ID = e.Toestel_ID
WHERE e.Internnummer = %s
ORDER BY e.Datum_inlevering IS NOT NULL, e.Datum_toekenning DESC, e.Eigendom_ID DESC""", (internnummer,)) or []
leerling = (execute_query("SELECT Voornaam, Naam, Gebruikersnaam FROM Leerlingen WHERE Internnummer = %s",
(internnummer,)) or [{}])[0]
def d(x):
return x.strftime('%d-%m-%Y') if hasattr(x, 'strftime') else (str(x) if x else '')
uit = []
for r in rows:
begin, einde = r['Datum_toekenning'], r['Datum_inlevering']
dagen = None
if hasattr(begin, 'toordinal'):
dagen = ((einde if hasattr(einde, 'toordinal') else date.today()) - begin).days
uit.append({'toestel': r['Schooleigen_ID'] or '', 'serienummer': r['Serienummer'] or '', 'type': r['Type'] or '',
'van': d(begin), 'tot': d(einde), 'huidig': not einde, 'dagen': dagen,
'geblokkeerd': int(r['locked'] or 0) == 1, 'opmerkingen': r['opmerkingen'] or ''})
return jsonify(ok=True, internnummer=internnummer,
naam=f"{leerling.get('Voornaam') or ''} {leerling.get('Naam') or ''}".strip(),
gebruikersnaam=leerling.get('Gebruikersnaam') or '', toestellen=uit)
@devices_bp.route("/inventaris/historie/<int:toestel_id>", methods=["GET"])
@login_required
@recht_vereist('toestellen_beheren', 'eigendom_bekijken')
def toestel_historie(toestel_id):
"""Alle leerlingen die dit toestel ooit gehad hebben, nieuwste eerst."""
from datetime import date
rows = execute_query("""
SELECT e.Internnummer, e.Datum_toekenning, e.Datum_inlevering, IFNULL(e.Status, 0) AS locked,
l.Voornaam, l.Naam, l.Gebruikersnaam
FROM Eigendom e LEFT JOIN Leerlingen l ON l.Internnummer = e.Internnummer
WHERE e.Toestel_ID = %s
ORDER BY e.Datum_inlevering IS NOT NULL, e.Datum_toekenning DESC, e.Eigendom_ID DESC""", (toestel_id,)) or []
def d(x):
return x.strftime('%d-%m-%Y') if hasattr(x, 'strftime') else (str(x) if x else '')
uit = []
for r in rows:
begin, einde = r['Datum_toekenning'], r['Datum_inlevering']
dagen = ((einde if hasattr(einde, 'toordinal') else date.today()) - begin).days if hasattr(begin, 'toordinal') else None
uit.append({'internnummer': r['Internnummer'], 'naam': f"{r['Voornaam'] or ''} {r['Naam'] or ''}".strip(),
'gebruikersnaam': r['Gebruikersnaam'] or '', 'van': d(begin), 'tot': d(einde), 'huidig': not einde,
'dagen': dagen, 'geblokkeerd': int(r['locked'] or 0) == 1})
return jsonify(ok=True, eigenaars=uit)
#-------------------------------------------------
# --- ROUTE UITDELEN ---
#-------------------------------------------------
@devices_bp.route('/uitdelen', methods=['GET'])
@login_required
@recht_vereist('toestellen_uitdelen')
def uitdelen_page():
return render_template('uitdelen.html', message=None)
@devices_bp.route('/zoek_leerlingen', methods=['GET'])
@login_required
@recht_vereist('toestellen_uitdelen', 'toestellen_beheren')
def zoek_leerlingen():
"""Suggesties voor het veld 'Internnummer' (max. 15): op internnummer, naam, voornaam of gebruikersnaam.
Geeft ook het toestel dat de leerling nu heeft."""
q = (request.args.get('q') or '').strip()
if not q:
return jsonify([])
woorden = q.split()[:4]
# Elk woord moet voorkomen in internnummer, gebruikersnaam of naam (in eender welke volgorde)
# LOWER(): zoeken zonder onderscheid tussen hoofd- en kleine letters, ongeacht de collation van CONCAT_WS
voorwaarden = " AND ".join(["LOWER(CONCAT_WS(' ', l.Internnummer, l.SS_nummer, l.Gebruikersnaam, l.Voornaam, l.Naam)) LIKE %s"] * len(woorden))
rows = execute_query(
"SELECT l.Internnummer, l.Voornaam, l.Naam, l.Gebruikersnaam, "
" GROUP_CONCAT(COALESCE(t.Schooleigen_ID, t.Serienummer) SEPARATOR ', ') AS Toestellen "
"FROM Leerlingen l "
"LEFT JOIN Eigendom e ON e.Internnummer = l.Internnummer AND e.Datum_inlevering IS NULL "
"LEFT JOIN Toestellen t ON t.Toestel_ID = e.Toestel_ID "
f"WHERE {voorwaarden} "
"GROUP BY l.Internnummer, l.Voornaam, l.Naam, l.Gebruikersnaam "
"ORDER BY (l.Internnummer LIKE %s) DESC, l.Naam, l.Voornaam LIMIT 15",
tuple(f"%{w.lower()}%" for w in woorden) + (f"{q}%",))
return jsonify([{'intern': r['Internnummer'], 'naam': f"{r['Voornaam'] or ''} {r['Naam'] or ''}".strip(),
'gebruiker': r['Gebruikersnaam'] or '', 'toestellen': r['Toestellen'] or ''} for r in rows])
@devices_bp.route('/zoek_toestellen', methods=['GET'])
@login_required
@recht_vereist('toestellen_uitdelen', 'toestellen_beheren')
def zoek_toestellen():
"""Suggesties voor het zoekveld 'Toestelnummer' (max. 15): op toestelnummer of serienummer."""
q = (request.args.get('q') or '').strip()
if len(q) < 1:
return jsonify([])
# Uitdelen: beschikbare toestellen eerst; inleveren: uitgeleende toestellen eerst
voorkeur = {'uitgeleend': "e.Internnummer IS NULL, ", 'beschikbaar': "e.Internnummer IS NOT NULL, "}.get(
request.args.get('voorkeur'), "")
rows = execute_query(
"SELECT t.Schooleigen_ID, t.Serienummer, t.Type, t.Status, e.Internnummer "
"FROM Toestellen t LEFT JOIN Eigendom e ON e.Toestel_ID = t.Toestel_ID AND e.Datum_inlevering IS NULL "
"WHERE t.Schooleigen_ID LIKE %s OR t.Serienummer LIKE %s "
f"ORDER BY {voorkeur}t.Schooleigen_ID IS NULL, (t.Schooleigen_ID LIKE %s) DESC, t.Schooleigen_ID, t.Serienummer "
"LIMIT 15",
(f"%{q}%", f"%{q}%", f"{q}%"))
return jsonify([{'nummer': r['Schooleigen_ID'] or '', 'serie': r['Serienummer'] or '', 'type': r['Type'] or '',
'status': r['Status'] or '', 'bij': r['Internnummer'] or ''} for r in rows])
# Functie van knop Ingeven (Uitdelen)
@devices_bp.route('/uitdelen_submit', methods=['POST'])
@login_required
@recht_vereist('toestellen_uitdelen')
def uitdelen_submit():
internnummer = request.form.get('internnummer')
#TODO--------------------------------------------
try:
internnummer= int(internnummer)
except:
internnummer = str(internnummer)
#--------------------------------------------
serienummer = request.form.get('serienummer')
action_type = request.form.get('action')
success = CIP_uitdelen.uitdelen(internnummer, serienummer)
if success:
flash(f"Toestel met nummer {serienummer} succesvol uitgedeeld!", "success")
else:
# Als het mislukt, tonen we de foutmelding die in de service is gezet
flash(CIP_uitdelen.foutmelding, "error")
return redirect(url_for('devices.uitdelen_page'))
#-------------------------------------------------
# --- ROUTE INLEVEREN ---
#-------------------------------------------------
@devices_bp.route('/inleveren', methods=['GET'])
@login_required
@recht_vereist('toestellen_uitdelen')
def inleveren_page():
return render_template('inleveren.html', message=None)
# Functie van knop Ingeven (Inleveren)
@devices_bp.route('/inleveren_submit', methods=['POST'])
@login_required
@recht_vereist('toestellen_uitdelen')
def inleveren_submit():
internnummer = request.form.get('internnummer')
serienummer = request.form.get('serienummer')
tab2_schade_text = request.form.get('schade_type', "")
action_type = request.form.get('action')
try:
internnummer= int(internnummer)
except:
internnummer = str(internnummer)
if action_type:
CIP_inleveren.Inleveren(internnummer, serienummer, action_type, tab2_schade_text)
return render_template('inleveren.html', message=CIP_inleveren.foutmelding)
#---------------------------------
# --- STATUS UPDATES ---
#---------------------------------
@devices_bp.route("/update_toestel_status", methods=["POST"])
@login_required
@recht_vereist('toestellen_beheren')
def wijzig_status():
toestel_id = request.form.get("toestel_id")
new_status = request.form.get("new_status")
query = request.form.get("query", "")
if toestel_id and new_status in ["Beschikbaar", "Uitgeleend", "Defect"]:
execute_query("UPDATE Toestellen SET Status = %s WHERE Toestel_ID = %s", (new_status, toestel_id), commit=True)
rij = execute_query("SELECT Schooleigen_ID, Serienummer FROM Toestellen WHERE Toestel_ID = %s", (toestel_id,))
label = (rij[0]['Schooleigen_ID'] or rij[0]['Serienummer']) if rij else toestel_id
flash(f"Status van {label} gewijzigd naar {new_status}", "success")
return redirect(url_for("devices.inventaris_toestellen", query=query))
#-------------------------------------------------
# --- ROUTE BLOKKEREN / DEBLOKKEREN ---
#-------------------------------------------------
@devices_bp.route('/eigendom/bewerken', methods=['POST'])
@login_required
@recht_vereist('toestellen_uitdelen')
def eigendom_bewerken():
"""Een lopende toekenning aanpassen: andere leerling en/of ander toestel."""
query = request.form.get('query', '')
terug = redirect(url_for('devices.inventaris_eigendom', query=query or None))
eigendom_id = request.form.get('eigendom_id', type=int)
rows = execute_query("SELECT * FROM Eigendom WHERE Eigendom_ID = %s AND Datum_inlevering IS NULL", (eigendom_id,))
if not rows:
flash("Deze toekenning bestaat niet (meer) of is al ingeleverd.", "error")
return terug
oud = rows[0]
leerling = tst.zoek_leerling((request.form.get('internnummer') or '').strip())
if not leerling:
flash("Internnummer niet gevonden in de leerlingenlijst.", "error")
return terug
toestel = tst.zoek_toestel((request.form.get('toestelnummer') or '').strip())
if not toestel:
flash("Toestelnummer (of serienummer) niet gevonden in de toestellenlijst.", "error")
return terug
nieuw_toestel = toestel['Toestel_ID'] != oud['Toestel_ID']
if nieuw_toestel:
bezet = execute_query("SELECT Internnummer FROM Eigendom WHERE Toestel_ID = %s AND Datum_inlevering IS NULL "
"AND Eigendom_ID <> %s", (toestel['Toestel_ID'], eigendom_id))
if bezet:
flash(f"Toestel {toestel.get('Schooleigen_ID') or toestel.get('Serienummer')} is al uitgeleend aan "
f"{bezet[0]['Internnummer']}. Lever het eerst in.", "error")
return terug
if not nieuw_toestel and str(leerling['Internnummer']) == str(oud['Internnummer']):
flash("Er is niets gewijzigd.", "info")
return terug
execute_query("UPDATE Eigendom SET Internnummer = %s, Toestel_ID = %s WHERE Eigendom_ID = %s",
(leerling['Internnummer'], toestel['Toestel_ID'], eigendom_id), commit=True)
if nieuw_toestel:
# Het vorige toestel is weer vrij, het nieuwe staat op uitgeleend
execute_query("UPDATE Toestellen SET Status = 'Beschikbaar' WHERE Toestel_ID = %s AND Status = 'Uitgeleend'",
(oud['Toestel_ID'],), commit=True)
execute_query("UPDATE Toestellen SET Status = 'Uitgeleend' WHERE Toestel_ID = %s", (toestel['Toestel_ID'],),
commit=True)
naam = f"{leerling.get('Voornaam') or ''} {leerling.get('Naam') or ''}".strip() or leerling['Internnummer']
flash(f"Toekenning aangepast: {toestel.get('Schooleigen_ID') or toestel.get('Serienummer')} → {naam} "
f"({leerling['Internnummer']}).", "success")
return terug
@devices_bp.route('/toggle_lock', methods=['POST'])
@login_required
@recht_vereist('toestel_blokkeren')
def toggle_lock():
# Haal de gegevens op uit het formulier en strip ze direct
internnummer = request.form.get('internnummer', '').strip()
query = request.form.get('query', '')
# 1. Haal de huidige leerling/toestel koppeling op
rows = execute_query(
"SELECT e.Eigendom_ID, e.Status, e.Toestel_ID, t.Schooleigen_ID, t.Serienummer FROM Eigendom e "
"JOIN Toestellen t ON t.Toestel_ID = e.Toestel_ID "
"WHERE e.Internnummer = %s AND e.Datum_inlevering IS NULL ORDER BY e.Datum_toekenning DESC",
(internnummer,))
if not rows:
flash("Geen gegevens gevonden in de Eigendom tabel.", "error")
return redirect(url_for('devices.inventaris_eigendom', query=query))
current_status = int(rows[0]['Status']) if rows[0]['Status'] is not None else 0
eigendom_id = rows[0]['Eigendom_ID']
kamxm_nummer = str(rows[0]['Schooleigen_ID'] or '').strip()
# 2. Serienummer van het toestel
serienummer = str(rows[0]['Serienummer']).strip() if rows[0]['Serienummer'] else None
if not serienummer:
flash(f"Kan geen serienummer vinden voor toestel {kamxm_nummer}.", "error")
return redirect(url_for('devices.inventaris_eigendom', query=query))
# 3. Vraag het Device ID op bij Google met een 'gecleand' serienummer
# We gebruiken strip() om de \t (tab) en spaties te verwijderen
clean_serial = serienummer.strip()
device_id, error = google_service.get_device_id_by_serial(clean_serial)
if device_id:
# Als status 0 is (unlocked) -> moet hij naar disable (True)
should_lock = (current_status == 0)
success, api_message = google_service.toggle_device_status(device_id, lock=should_lock)
if success:
new_status = 1 if should_lock else 0
execute_query("UPDATE Eigendom SET Status=%s WHERE Eigendom_ID=%s", (new_status, eigendom_id), commit=True)
try:
tst.zet_geblokkeerd(rows[0]['Toestel_ID'], should_lock)
except Exception as e:
sys.stderr.write(f"Toestellen.Geblokkeerd bijwerken: {e}\n")
flash(api_message, "success")
else:
flash(f"Google API Fout: {api_message}", "error")
else:
# Hier wordt de 'Invalid Input' nu voorkomen of getoond als het serienummer echt fout is
flash(f"Google Zoekfout: {error}", "error")
return redirect(url_for('devices.inventaris_eigendom', query=query))
@devices_bp.route('/inventaris/blokkeren', methods=['POST'])
@login_required
@recht_vereist('toestel_blokkeren')
def toestel_blokkeren():
"""Blokkeert of deblokkeert één Chromebook in Google (knop in Alle toestellen).
actie = 'blokkeren' of 'deblokkeren'. Met fetch (X-Requested-With) komt er JSON terug, anders een redirect."""
from rechten import log
ajax = request.headers.get('X-Requested-With') == 'fetch'
query = request.form.get('query', '')
actie = request.form.get('actie', '')
def klaar(ok, melding, geblokkeerd=None):
if ajax:
return jsonify(ok=ok, melding=melding, geblokkeerd=geblokkeerd)
flash(melding, "success" if ok else "error")
return redirect(url_for('devices.inventaris_toestellen', query=query))
if actie not in ('blokkeren', 'deblokkeren'):
return klaar(False, "Onbekende actie.")
blokkeren = actie == 'blokkeren'
try:
toestel_id = int(request.form.get('toestel_id', ''))
except ValueError:
return klaar(False, "Onbekend toestel.")
rows = execute_query("SELECT Toestel_ID, Schooleigen_ID, Serienummer FROM Toestellen WHERE Toestel_ID = %s",
(toestel_id,))
if not rows:
return klaar(False, "Toestel niet gevonden.")
t = rows[0]
label = t['Schooleigen_ID'] or t['Serienummer'] or str(toestel_id)
if not (t['Serienummer'] or '').strip():
return klaar(False, f"Toestel {label} heeft geen serienummer; vul het eerst in via ✏️ Bewerken.")
try:
toestel, fout = google_service.zoek_chromebook(t['Serienummer'])
except Exception as e:
toestel, fout = None, f"Google gaf een fout: {e}"
if not toestel:
return klaar(False, fout)
status = toestel['status']
if status == 'DEPROVISIONED':
return klaar(False, f"Toestel {label} is uitgeschreven in Google Admin en kan niet (ge)blokkeerd worden.")
gewenst = 'DISABLED' if blokkeren else 'ACTIVE'
if status != gewenst:
ok, melding = google_service.toggle_device_status(toestel['id'], lock=blokkeren)
if not ok:
return klaar(False, melding)
try:
tst.zet_geblokkeerd(toestel_id, blokkeren)
except Exception as e:
sys.stderr.write(f"Toestellen.Geblokkeerd bijwerken: {e}\n")
log(f"Chromebook {label} {'geblokkeerd' if blokkeren else 'gedeblokkeerd'}")
if status == gewenst:
return klaar(True, f"Toestel {label} was in Google al {'geblokkeerd' if blokkeren else 'actief'}.", blokkeren)
return klaar(True, f"Toestel {label} is {'geblokkeerd' if blokkeren else 'gedeblokkeerd'}.", blokkeren)
#-------------------------------------------------
# --- HELPER gebruikt in Toggle lock functie ---
#-------------------------------------------------
def get_serienummer_from_kamxm(kamxm_nummer):
if not kamxm_nummer:
return None
result = execute_query("SELECT Serienummer FROM Toestellen WHERE Schooleigen_ID=%s", (kamxm_nummer,))
if result and result[0]['Serienummer']:
# Alvast een basis strip()
return str(result[0]['Serienummer']).strip()
return None