$todayStart; } /** * @return array{scheduledDate: string, programCycleNumber: int, programSessionNumber: int, programSessionID: ?string, updatedAt: int}|null */ function qdb_get_client_session_schedule(PDO $pdo, string $clientCode): ?array { $stmt = $pdo->prepare( 'SELECT scheduledDate, programCycleNumber, programSessionNumber, programSessionID, updatedAt FROM client_session_schedule WHERE clientCode = :cc' ); $stmt->execute([':cc' => $clientCode]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if (!$row) { return null; } return [ 'scheduledDate' => (string)$row['scheduledDate'], 'programCycleNumber' => (int)$row['programCycleNumber'], 'programSessionNumber' => (int)$row['programSessionNumber'], 'programSessionID' => $row['programSessionID'] !== null && $row['programSessionID'] !== '' ? (string)$row['programSessionID'] : null, 'updatedAt' => (int)$row['updatedAt'], ]; } function qdb_upsert_client_session_schedule( PDO $pdo, string $clientCode, string $scheduledDate, int $programCycleNumber, int $programSessionNumber, ?string $programSessionID = null ): void { if (!qdb_scheduled_date_format_valid($scheduledDate)) { json_error('INVALID_FIELD', 'scheduledDate must be DD-MM-YYYY', 400); } if (!qdb_scheduled_date_is_after_today($scheduledDate)) { json_error('INVALID_FIELD', 'scheduledDate must be in the future', 400); } if ($programCycleNumber < 1 || $programSessionNumber < 1) { json_error('INVALID_FIELD', 'program cycle and session must be positive', 400); } $pdo->prepare( 'INSERT INTO client_session_schedule ( clientCode, scheduledDate, programCycleNumber, programSessionNumber, programSessionID, updatedAt ) VALUES ( :cc, :sd, :pcn, :psn, :psid, :uat ) ON CONFLICT(clientCode) DO UPDATE SET scheduledDate = excluded.scheduledDate, programCycleNumber = excluded.programCycleNumber, programSessionNumber = excluded.programSessionNumber, programSessionID = excluded.programSessionID, updatedAt = excluded.updatedAt' )->execute([ ':cc' => $clientCode, ':sd' => $scheduledDate, ':pcn' => $programCycleNumber, ':psn' => $programSessionNumber, ':psid' => $programSessionID, ':uat' => time(), ]); } /** * RBAC-scoped schedule rows for the website Schedule view. * * @return list> */ function qdb_list_session_schedules_scoped(PDO $pdo, array $tokenRec): array { [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); $sql = " SELECT css.clientCode, css.scheduledDate, css.programCycleNumber, css.programSessionNumber, css.programSessionID, css.updatedAt, cl.coachID, co.username AS coachUsername, sv.username AS supervisorUsername FROM client_session_schedule css INNER JOIN client cl ON cl.clientCode = css.clientCode LEFT JOIN coach co ON co.coachID = cl.coachID LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID WHERE ($rbacClause) ORDER BY css.scheduledDate ASC, co.username ASC, css.clientCode ASC "; $stmt = $pdo->prepare($sql); $stmt->execute($rbacParams); $rows = []; foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { $rows[] = [ 'clientCode' => $row['clientCode'], 'scheduledDate' => $row['scheduledDate'], 'programCycleNumber' => (int)$row['programCycleNumber'], 'programSessionNumber' => (int)$row['programSessionNumber'], 'programSessionID' => $row['programSessionID'], 'coachID' => $row['coachID'], 'coachUsername' => $row['coachUsername'] ?? $row['coachID'], 'supervisorUsername' => $row['supervisorUsername'] ?? '', 'updatedAt' => (int)$row['updatedAt'], ]; } return $rows; }