fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE $where", $params); $free = $db->fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE student_id IS NULL" . ($year ? " AND school_year = ?" : ""), $year ? [$year] : []); $used = $db->fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE student_id IS NOT NULL" . ($year ? " AND school_year = ?" : ""), $year ? [$year] : []); $limit = (int)($_GET['limit'] ?? 100); $offset = (int)($_GET['offset'] ?? 0); $licenses = $db->fetchAll( "SELECT l.id, l.code, l.school_year, l.student_id, l.teacher_id, l.redeemed_at, l.created_at, s.username as student_name, t.display_name as teacher_name FROM licenses l LEFT JOIN students s ON s.id = l.student_id LEFT JOIN teachers t ON t.id = l.teacher_id WHERE $where ORDER BY l.id DESC LIMIT $limit OFFSET $offset", $params ); Response::ok([ 'total' => (int)$total['c'], 'free' => (int)$free['c'], 'used' => (int)$used['c'], 'licenses' => $licenses ]); } // Lehrer: Lizenzen einer Klasse $teacherId = Session::requireTeacher(); $classId = (int)($_GET['class_id'] ?? 0); if (!$classId) Response::error('class_id erforderlich'); $class = $db->fetchOne('SELECT id FROM classes WHERE id = ? AND teacher_id = ?', [$classId, $teacherId]); if (!$class) Response::error('Klasse nicht gefunden', 404); $licenses = $db->fetchAll( 'SELECT l.id, l.code, l.school_year, l.student_id, l.redeemed_at, s.username as student_name, s.display_name as student_display FROM licenses l JOIN students s ON s.id = l.student_id WHERE s.class_id = ? ORDER BY s.username', [$classId] ); // Auch freie Lizenzen des Lehrers (noch nicht zugewiesen) $freeLicenses = $db->fetchAll( 'SELECT id, code, school_year FROM licenses WHERE teacher_id = ? AND student_id IS NULL ORDER BY code', [$teacherId] ); Response::ok(['assigned' => $licenses, 'free' => $freeLicenses]); } // === POST === if ($method === 'POST') { $body = json_decode(file_get_contents('php://input'), true); $action = $body['action'] ?? ''; // Lizenz einlösen (Lehrer gibt Code ein → wird ihm zugeordnet) if ($action === 'redeem') { $teacherId = Session::requireTeacher(); $code = strtoupper(trim($body['code'] ?? '')); if (!$code) Response::error('Lizenzcode erforderlich'); $license = $db->fetchOne('SELECT id, student_id, teacher_id FROM licenses WHERE code = ?', [$code]); if (!$license) Response::error('Lizenzcode ungültig'); if ($license['student_id']) Response::error('Diese Lizenz ist bereits bereits zugewiesen'); if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) Response::error('Diese Lizenz gehört einer anderen Lehrperson'); $db->execute('UPDATE licenses SET teacher_id = ?, redeemed_at = NOW() WHERE id = ?', [$teacherId, $license['id']]); Response::ok(['licenseId' => (int)$license['id']]); } // Mehrere Lizenzen auf einmal einlösen if ($action === 'redeem_batch') { $teacherId = Session::requireTeacher(); $codes = $body['codes'] ?? []; if (!is_array($codes) || empty($codes)) Response::error('Lizenzcodes erforderlich'); $redeemed = 0; $errors = []; foreach ($codes as $code) { $code = strtoupper(trim($code)); if (!$code) continue; $license = $db->fetchOne('SELECT id, student_id, teacher_id FROM licenses WHERE code = ?', [$code]); if (!$license) { $errors[] = "$code: ungültig"; continue; } if ($license['student_id']) { $errors[] = "$code: bereits zugewiesen"; continue; } if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) { $errors[] = "$code: gehört anderer Lehrperson"; continue; } $db->execute('UPDATE licenses SET teacher_id = ?, redeemed_at = NOW() WHERE id = ?', [$teacherId, $license['id']]); $redeemed++; } Response::ok(['redeemed' => $redeemed, 'errors' => $errors]); } // Lizenz einem Schüler zuweisen if ($action === 'assign') { $teacherId = Session::requireTeacher(); $licenseId = (int)($body['licenseId'] ?? 0); $studentId = (int)($body['studentId'] ?? 0); if (!$licenseId || !$studentId) Response::error('licenseId und studentId erforderlich'); $license = $db->fetchOne('SELECT id, teacher_id FROM licenses WHERE id = ? AND student_id IS NULL', [$licenseId]); if (!$license) Response::error('Lizenz nicht gefunden oder bereits zugewiesen'); if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) Response::error('Lizenz gehört anderer Lehrperson'); // Prüfen dass Schüler dem Lehrer gehört $student = $db->fetchOne( 'SELECT s.id FROM students s JOIN classes c ON c.id = s.class_id WHERE s.id = ? AND c.teacher_id = ?', [$studentId, $teacherId] ); if (!$student) Response::error('Schüler*in nicht gefunden'); $db->execute('UPDATE licenses SET student_id = ?, teacher_id = ? WHERE id = ?', [$studentId, $teacherId, $licenseId]); Response::ok(); } // Auto-Assign: nächste freie Lizenz einem Schüler zuweisen if ($action === 'auto_assign') { $teacherId = Session::requireTeacher(); $studentId = (int)($body['studentId'] ?? 0); if (!$studentId) Response::error('studentId erforderlich'); $student = $db->fetchOne( 'SELECT s.id FROM students s JOIN classes c ON c.id = s.class_id WHERE s.id = ? AND c.teacher_id = ?', [$studentId, $teacherId] ); if (!$student) Response::error('Schüler*in nicht gefunden'); // Nächste freie Lizenz des Lehrers $license = $db->fetchOne( 'SELECT id FROM licenses WHERE teacher_id = ? AND student_id IS NULL ORDER BY id LIMIT 1', [$teacherId] ); if (!$license) Response::error('Keine freien Lizenzen verfügbar'); $db->execute('UPDATE licenses SET student_id = ? WHERE id = ?', [$studentId, $license['id']]); Response::ok(['licenseId' => (int)$license['id']]); } // Super-Admin: neue Lizenzen generieren if ($action === 'generate') { $teacherId = Session::requireTeacher(); $count = min(1000, max(1, (int)($body['count'] ?? 100))); $year = (int)($body['year'] ?? date('Y')); $schoolYear = ($year % 100) . '/' . (($year % 100) + 1); $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; $generated = 0; for ($i = 0; $i < $count; $i++) { $code = ''; for ($j = 0; $j < 3; $j++) { if ($j > 0) $code .= '-'; for ($k = 0; $k < 4; $k++) $code .= $chars[random_int(0, strlen($chars) - 1)]; } $code .= '-' . $year; try { $db->execute('INSERT INTO licenses (code, school_year) VALUES (?, ?)', [$code, $schoolYear]); $generated++; } catch (\Exception $e) { $i--; // Collision, retry } } Response::ok(['generated' => $generated]); } Response::error('Unbekannte Aktion'); } Response::error('Methode nicht erlaubt', 405);