#!/usr/bin/env php connect_error) { die("Database connection failed: " . $conn->connect_error . "\n"); } echo "Adding closed_at column to tickets table...\n"; // Add the column if it doesn't exist $result = $conn->query("SHOW COLUMNS FROM tickets LIKE 'closed_at'"); if ($result->num_rows > 0) { echo "Column 'closed_at' already exists, skipping ALTER TABLE.\n"; } else { $sql = "ALTER TABLE tickets ADD COLUMN closed_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at"; if ($conn->query($sql)) { echo "Column added successfully.\n"; } else { die("Failed to add column: " . $conn->error . "\n"); } // Add index for stats queries $conn->query("CREATE INDEX idx_tickets_closed_at ON tickets (closed_at)"); echo "Index created.\n"; } // Backfill: For existing closed tickets, use the audit log to find when they were closed echo "\nBackfilling closed_at from audit log...\n"; $sql = "UPDATE tickets t JOIN ( SELECT entity_id as ticket_id, MIN(created_at) as first_closed FROM audit_log WHERE entity_type = 'ticket' AND action_type = 'update' AND details LIKE '%\"status\":\"Closed\"%' GROUP BY entity_id ) al ON t.ticket_id = al.ticket_id SET t.closed_at = al.first_closed WHERE t.status = 'Closed' AND t.closed_at IS NULL"; $result = $conn->query($sql); $backfilled = $conn->affected_rows; echo "Backfilled $backfilled tickets from audit log.\n"; // For any remaining closed tickets without audit log entries, use updated_at as fallback $sql = "UPDATE tickets SET closed_at = updated_at WHERE status = 'Closed' AND closed_at IS NULL"; $conn->query($sql); $fallback = $conn->affected_rows; if ($fallback > 0) { echo "Used updated_at as fallback for $fallback tickets without audit log entries.\n"; } echo "\nMigration complete!\n"; $conn->close();