-- Yealink/PBX call IDs are reused after a phone or PBX lifecycle. -- Keep historical calls and require uniqueness only while a call is active. DROP INDEX IF EXISTS ux_telefoni_opkald_callid; CREATE UNIQUE INDEX IF NOT EXISTS ux_telefoni_opkald_active_callid ON telefoni_opkald(callid) WHERE ended_at IS NULL; -- Recover calls that were previously merged into an older row with the same -- reused call ID. System events are written from the successful callback and -- retain the callback metadata required for a read-safe reconstruction. INSERT INTO telefoni_opkald ( callid, bruger_id, direction, ekstern_nummer, intern_extension, started_at, ended_at, duration_sec, raw_payload, lifecycle_status, termination_source, last_event_at ) SELECT established.correlation_id, established.user_id, COALESCE(NULLIF(established.details->'payload'->>'direction', ''), 'inbound'), NULLIF(established.details->>'external_number', ''), NULLIF(established.details->>'extension', ''), established.occurred_at AT TIME ZONE 'UTC', terminated.occurred_at AT TIME ZONE 'UTC', CASE WHEN NULLIF(terminated.details->>'duration_sec', '') ~ '^[0-9]+$' THEN (terminated.details->>'duration_sec')::INTEGER WHEN terminated.occurred_at IS NOT NULL THEN GREATEST(EXTRACT(EPOCH FROM (terminated.occurred_at - established.occurred_at))::INTEGER, 0) ELSE NULL END, COALESCE(established.details->'payload', '{}'::jsonb), CASE WHEN terminated.id IS NULL THEN 'active' ELSE 'completed' END, CASE WHEN terminated.id IS NULL THEN NULL ELSE 'system_log_recovery' END, COALESCE(terminated.occurred_at, established.occurred_at) FROM system_event_log established LEFT JOIN LATERAL ( SELECT term.id, term.occurred_at, term.details FROM system_event_log term WHERE term.source = 'voip' AND term.event_type = 'voip.terminated' AND term.correlation_id = established.correlation_id AND term.occurred_at >= established.occurred_at ORDER BY term.occurred_at ASC, term.id ASC LIMIT 1 ) terminated ON TRUE WHERE established.source = 'voip' AND established.event_type = 'voip.established' AND NULLIF(TRIM(established.correlation_id), '') IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM telefoni_opkald current_call WHERE current_call.callid = established.correlation_id AND current_call.started_at BETWEEN (established.occurred_at AT TIME ZONE 'UTC') - INTERVAL '5 minutes' AND (established.occurred_at AT TIME ZONE 'UTC') + INTERVAL '5 minutes' ); CREATE INDEX IF NOT EXISTS idx_telefoni_opkald_callid_started ON telefoni_opkald(callid, started_at DESC);