exec("CREATE TABLE IF NOT EXISTS jobcard_status_history (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, jobcard_id BIGINT UNSIGNED NOT NULL, from_status VARCHAR(60) NOT NULL, to_status VARCHAR(60) NOT NULL, changed_by BIGINT UNSIGNED NULL, changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (jobcard_id) REFERENCES jobcards(id) ON DELETE CASCADE, FOREIGN KEY (changed_by) REFERENCES users(id) ON DELETE SET NULL, INDEX status_history_jobcard_idx (jobcard_id, changed_at)) ENGINE=InnoDB"); $pdo->exec("CREATE TABLE IF NOT EXISTS jobcard_sequences (sequence_year SMALLINT UNSIGNED PRIMARY KEY, next_sequence INT UNSIGNED NOT NULL) ENGINE=InnoDB"); $index = $pdo->query("SHOW INDEX FROM sla_agreements WHERE Key_name = 'sla_client_unique'")->fetch(); if (!$index) { $duplicates = (int)$pdo->query('SELECT COUNT(*) FROM (SELECT client_id FROM sla_agreements GROUP BY client_id HAVING COUNT(*) > 1) duplicate_clients')->fetchColumn(); if ($duplicates > 0) throw new RuntimeException('Multiple SLA agreements exist for one or more clients; resolve duplicates before rerunning the installer.'); $pdo->exec('ALTER TABLE sla_agreements ADD UNIQUE KEY sla_client_unique (client_id)'); } } try { $schemaPath = __DIR__ . '/../database/schema.sql'; if (!is_readable($schemaPath)) throw new RuntimeException('database/schema.sql is missing or unreadable'); $schema = file_get_contents($schemaPath); if ($schema === false || trim($schema) === '') throw new RuntimeException('database/schema.sql could not be read'); db()->exec($schema); apply_existing_database_upgrades(db()); ensure_initial_administrator(); fwrite(STDOUT, "Database schema installed and initial Administrator verified.\n"); } catch (Throwable $exception) { fwrite(STDERR, "Installation failed: {$exception->getMessage()}\n"); exit(1); }