prepare( "SELECT cl.clientCode, cl.coachID, co.username AS coachUsername, CASE WHEN EXISTS ( SELECT 1 FROM completed_questionnaire cq WHERE cq.clientCode = cl.clientCode ) OR EXISTS ( SELECT 1 FROM questionnaire_submission qs WHERE qs.clientCode = cl.clientCode ) THEN 1 ELSE 0 END AS hasResponseData FROM client cl LEFT JOIN coach co ON co.coachID = cl.coachID WHERE $clause ORDER BY hasResponseData DESC, cl.clientCode ASC" ); foreach ($params as $k => $v) $stmt->bindValue($k, $v); $stmt->execute(); $clients = $stmt->fetchAll(PDO::FETCH_ASSOC); qdb_discard($tmpDb, $lockFp); json_success(['clients' => $clients]); } catch (Throwable $e) { if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp); error_log($e->getMessage()); json_error('SERVER_ERROR', 'Server error', 500); } break; case 'POST': $body = read_json_body(); $clientCode = trim($body['clientCode'] ?? ''); $coachID = trim($body['coachID'] ?? ''); if ($clientCode === '' || $coachID === '') { json_error('MISSING_FIELDS', 'clientCode and coachID are required', 400); } try { [$pdo, $tmpDb, $lockFp] = qdb_open(true); // Validate coach exists and caller is allowed to assign to them if ($callerRole === 'supervisor') { $chkCoach = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :sid"); $chkCoach->execute([':cid' => $coachID, ':sid' => $callerEntityID]); } else { $chkCoach = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid"); $chkCoach->execute([':cid' => $coachID]); } if (!$chkCoach->fetch()) { qdb_discard($tmpDb, $lockFp); json_error('NOT_FOUND', 'Coach not found or not authorized', 404); } $chk = $pdo->prepare("SELECT clientCode FROM client WHERE clientCode = :cc"); $chk->execute([':cc' => $clientCode]); if ($chk->fetch()) { qdb_discard($tmpDb, $lockFp); json_error('DUPLICATE', 'Client code already exists', 409); } $pdo->prepare("INSERT INTO client (clientCode, coachID) VALUES (:cc, :cid)") ->execute([':cc' => $clientCode, ':cid' => $coachID]); qdb_save($tmpDb, $lockFp); json_success(['clientCode' => $clientCode, 'coachID' => $coachID]); } catch (Throwable $e) { if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp); error_log($e->getMessage()); json_error('SERVER_ERROR', 'Server error', 500); } break; case 'DELETE': $body = read_json_body(); $clientCode = trim($body['clientCode'] ?? ''); if ($clientCode === '') { json_error('MISSING_FIELDS', 'clientCode is required', 400); } try { [$pdo, $tmpDb, $lockFp] = qdb_open(true); // Verify the client exists and the caller can see it (RBAC) [$clause, $params] = rbac_client_filter($tokenRec, 'cl'); $chk = $pdo->prepare( "SELECT clientCode FROM client cl WHERE cl.clientCode = :cc AND ($clause)" ); $chk->execute(array_merge([':cc' => $clientCode], $params)); if (!$chk->fetch()) { qdb_discard($tmpDb, $lockFp); json_error('NOT_FOUND', 'Client not found or not authorized', 404); } require_once __DIR__ . '/../lib/submissions.php'; $pdo->beginTransaction(); qdb_delete_client_response_data($pdo, [$clientCode]); $pdo->prepare('DELETE FROM client WHERE clientCode = :cc') ->execute([':cc' => $clientCode]); $pdo->commit(); qdb_save($tmpDb, $lockFp); json_success([]); } catch (Throwable $e) { if (isset($pdo) && $pdo->inTransaction()) $pdo->rollBack(); if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp); error_log($e->getMessage()); json_error('SERVER_ERROR', 'Server error', 500); } break; default: json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); }