604 lines
29 KiB
Python
604 lines
29 KiB
Python
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
|
|
|
|
|
|
|