import logging from datetime import datetime from typing import Any, Optional from app.core.database import execute_query, execute_query_single logger = logging.getLogger(__name__) class TelefoniService: @staticmethod def find_user_by_extension(extension: Optional[str]) -> Optional[int]: if not extension: return None row = execute_query_single( "SELECT user_id FROM users WHERE telefoni_aktiv = TRUE AND telefoni_extension = %s LIMIT 1", (extension,), ) return int(row["user_id"]) if row and row.get("user_id") is not None else None @staticmethod def find_contact_by_phone_suffix(suffix8: Optional[str]) -> Optional[dict]: if not suffix8: return None query = """ SELECT c.id, c.first_name, c.last_name, ( SELECT cu.name FROM contact_companies cc JOIN customers cu ON cu.id = cc.customer_id WHERE cc.contact_id = c.id ORDER BY cc.is_primary DESC NULLS LAST, cc.id ASC LIMIT 1 ) AS company FROM contacts c WHERE RIGHT(regexp_replace(COALESCE(c.phone, ''), '\\D', '', 'g'), 8) = %s OR RIGHT(regexp_replace(COALESCE(c.mobile, ''), '\\D', '', 'g'), 8) = %s ORDER BY c.id ASC LIMIT 1 """ row = execute_query_single(query, (suffix8, suffix8)) if not row: return None return { "id": row["id"], "name": f"{(row.get('first_name') or '').strip()} {(row.get('last_name') or '').strip()}".strip(), "company": row.get("company"), } @staticmethod def upsert_call( *, callid: str, user_id: Optional[int], direction: str, ekstern_nummer: Optional[str], intern_extension: Optional[str], kontakt_id: Optional[int], raw_payload: Any, started_at: datetime, ) -> dict: query = """ INSERT INTO telefoni_opkald (callid, bruger_id, direction, ekstern_nummer, intern_extension, kontakt_id, started_at, raw_payload) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb) ON CONFLICT (callid) DO UPDATE SET raw_payload = EXCLUDED.raw_payload, direction = EXCLUDED.direction, intern_extension = COALESCE(telefoni_opkald.intern_extension, EXCLUDED.intern_extension), ekstern_nummer = COALESCE(telefoni_opkald.ekstern_nummer, EXCLUDED.ekstern_nummer), bruger_id = COALESCE(telefoni_opkald.bruger_id, EXCLUDED.bruger_id), kontakt_id = COALESCE(telefoni_opkald.kontakt_id, EXCLUDED.kontakt_id), started_at = LEAST(telefoni_opkald.started_at, EXCLUDED.started_at) RETURNING * """ rows = execute_query( query, ( callid, user_id, direction, ekstern_nummer, intern_extension, kontakt_id, started_at, raw_payload, ), ) return rows[0] if rows else {} @staticmethod def terminate_call(callid: str, duration_sec: Optional[int]) -> bool: if not callid: return False rows = execute_query( """ UPDATE telefoni_opkald SET ended_at = NOW(), duration_sec = %s WHERE callid = %s RETURNING id """, (duration_sec, callid), ) return bool(rows) @staticmethod def get_contact_details(contact_id: int) -> dict: """ Get extended contact details including: - Latest 3 open cases - Last call date """ if not contact_id: return {"recent_cases": [], "last_call": None} # Get the 3 newest open cases for this contact cases_query = """ SELECT s.id, s.titel, s.created_at FROM sag_sager s INNER JOIN sag_kontakter sk ON s.id = sk.sag_id WHERE sk.contact_id = %s AND s.status = 'åben' AND s.deleted_at IS NULL AND sk.deleted_at IS NULL ORDER BY s.created_at DESC LIMIT 3 """ cases = execute_query(cases_query, (contact_id,)) or [] # Get the most recent call for this contact last_call_query = """ SELECT t.started_at, t.bruger_id, t.duration_sec, u.full_name, u.username FROM telefoni_opkald t LEFT JOIN users u ON t.bruger_id = u.user_id WHERE t.kontakt_id = %s AND t.ended_at IS NOT NULL ORDER BY t.started_at DESC LIMIT 1 """ last_call_row = execute_query_single(last_call_query, (contact_id,)) last_call_data = None if last_call_row: last_call_data = { "started_at": last_call_row.get("started_at"), "bruger_navn": last_call_row.get("full_name") or last_call_row.get("username"), "duration_sec": last_call_row.get("duration_sec"), } return { "recent_cases": [ { "id": case["id"], "titel": case["titel"], "created_at": case["created_at"], } for case in cases ], "last_call": last_call_data, }