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