fix(telefoni): show legacy call history when telefoni_opkald is empty
This commit is contained in:
parent
6a68aecafa
commit
c5478b7e29
10
RELEASE_NOTES_v2.3.7.md
Normal file
10
RELEASE_NOTES_v2.3.7.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Release Notes: v2.3.7
|
||||||
|
**Date:** 16. maj 2026
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- fallback to mission_call_state when telefoni_opkald is empty
|
||||||
|
- legacy rows shown read-only in telefoni UI
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
- app/modules/telefoni/backend/router.py
|
||||||
|
- app/modules/telefoni/templates/log.html
|
||||||
@ -735,8 +735,69 @@ async def list_calls(
|
|||||||
"""
|
"""
|
||||||
params.extend([limit, offset])
|
params.extend([limit, offset])
|
||||||
|
|
||||||
rows = execute_query(query, tuple(params))
|
rows = execute_query(query, tuple(params)) or []
|
||||||
return rows or []
|
if rows:
|
||||||
|
return rows
|
||||||
|
|
||||||
|
# Fallback: legacy mission call history (read-only rows) for environments
|
||||||
|
# where historical calls were stored before telefoni_opkald was populated.
|
||||||
|
if user_id is not None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
legacy_where = []
|
||||||
|
legacy_params = []
|
||||||
|
if date_from:
|
||||||
|
legacy_where.append("m.started_at >= %s")
|
||||||
|
legacy_params.append(date_from)
|
||||||
|
if date_to:
|
||||||
|
legacy_where.append("m.started_at <= %s")
|
||||||
|
legacy_params.append(date_to)
|
||||||
|
|
||||||
|
legacy_where_sql = ("WHERE " + " AND ".join(legacy_where)) if legacy_where else ""
|
||||||
|
|
||||||
|
legacy_query = f"""
|
||||||
|
SELECT
|
||||||
|
-ROW_NUMBER() OVER (ORDER BY m.started_at DESC, m.call_id) AS id,
|
||||||
|
m.call_id AS callid,
|
||||||
|
NULL::INTEGER AS bruger_id,
|
||||||
|
CASE
|
||||||
|
WHEN LOWER(COALESCE(m.state, '')) IN ('outbound', 'udgaaende') THEN 'outbound'
|
||||||
|
ELSE 'inbound'
|
||||||
|
END AS direction,
|
||||||
|
m.caller_number AS ekstern_nummer,
|
||||||
|
m.caller_number AS display_number,
|
||||||
|
NULL::VARCHAR AS intern_extension,
|
||||||
|
NULL::INTEGER AS kontakt_id,
|
||||||
|
NULL::INTEGER AS sag_id,
|
||||||
|
m.started_at,
|
||||||
|
m.ended_at,
|
||||||
|
CASE
|
||||||
|
WHEN m.started_at IS NOT NULL AND m.ended_at IS NOT NULL
|
||||||
|
THEN GREATEST(EXTRACT(EPOCH FROM (m.ended_at - m.started_at))::int, 0)
|
||||||
|
ELSE NULL
|
||||||
|
END AS duration_sec,
|
||||||
|
m.updated_at AS created_at,
|
||||||
|
NULL::VARCHAR AS username,
|
||||||
|
NULL::VARCHAR AS full_name,
|
||||||
|
COALESCE(NULLIF(TRIM(m.contact_name), ''), NULL) AS contact_name,
|
||||||
|
COALESCE(NULLIF(TRIM(m.company_name), ''), NULL) AS contact_company,
|
||||||
|
NULL::VARCHAR AS sag_titel,
|
||||||
|
'legacy_mission'::VARCHAR AS source
|
||||||
|
FROM mission_call_state m
|
||||||
|
{legacy_where_sql}
|
||||||
|
ORDER BY m.started_at DESC, m.call_id
|
||||||
|
LIMIT %s OFFSET %s
|
||||||
|
"""
|
||||||
|
legacy_params.extend([limit, offset])
|
||||||
|
|
||||||
|
try:
|
||||||
|
legacy_rows = execute_query(legacy_query, tuple(legacy_params)) or []
|
||||||
|
except Exception:
|
||||||
|
legacy_rows = []
|
||||||
|
|
||||||
|
if without_case:
|
||||||
|
return [r for r in legacy_rows if not r.get("sag_id")]
|
||||||
|
return legacy_rows
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/telefoni/calls/{call_id}")
|
@router.patch("/telefoni/calls/{call_id}")
|
||||||
|
|||||||
@ -851,6 +851,8 @@ async function loadCalls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody.innerHTML = rows.map(r => {
|
tbody.innerHTML = rows.map(r => {
|
||||||
|
const callId = Number(r.id);
|
||||||
|
const canMutateCall = Number.isInteger(callId) && callId > 0;
|
||||||
const started = r.started_at ? new Date(r.started_at) : null;
|
const started = r.started_at ? new Date(r.started_at) : null;
|
||||||
const dateTxt = started ? started.toLocaleString('da-DK') : '-';
|
const dateTxt = started ? started.toLocaleString('da-DK') : '-';
|
||||||
const userTxt = escapeHtml(r.full_name || r.username || '-');
|
const userTxt = escapeHtml(r.full_name || r.username || '-');
|
||||||
@ -866,30 +868,38 @@ async function loadCalls() {
|
|||||||
const contactHtml = r.kontakt_id
|
const contactHtml = r.kontakt_id
|
||||||
? `<div class="d-flex align-items-center gap-2 flex-wrap">
|
? `<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||||
<a href="/contacts/${r.kontakt_id}">${escapeHtml(r.contact_name || ('Kontakt #' + r.kontakt_id))}</a>
|
<a href="/contacts/${r.kontakt_id}">${escapeHtml(r.contact_name || ('Kontakt #' + r.kontakt_id))}</a>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="openLinkContactModal(${Number(r.id)})">Skift</button>
|
${canMutateCall
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="unlinkContact(${Number(r.id)})">Fjern</button>
|
? `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="openLinkContactModal(${callId})">Skift</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="unlinkContact(${callId})">Fjern</button>`
|
||||||
|
: '<span class="badge bg-light text-muted border">Historik</span>'}
|
||||||
</div>
|
</div>
|
||||||
${r.contact_company ? `<div class="text-muted small">${escapeHtml(r.contact_company)}</div>` : ''}`
|
${r.contact_company ? `<div class="text-muted small">${escapeHtml(r.contact_company)}</div>` : ''}`
|
||||||
: `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="openLinkContactModal(${Number(r.id)})" title="Vælg kontakt/firma">
|
: (canMutateCall
|
||||||
<i class="bi bi-three-dots"></i>
|
? `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="openLinkContactModal(${callId})" title="Vælg kontakt/firma">
|
||||||
</button>`;
|
<i class="bi bi-three-dots"></i>
|
||||||
|
</button>`
|
||||||
|
: '<span class="text-muted small">Historisk opkald</span>');
|
||||||
|
|
||||||
const numberForTitle = (r.display_number || r.ekstern_nummer || '').trim();
|
const numberForTitle = (r.display_number || r.ekstern_nummer || '').trim();
|
||||||
const createQs = new URLSearchParams();
|
const createQs = new URLSearchParams();
|
||||||
if (r.kontakt_id) createQs.set('contact_id', String(r.kontakt_id));
|
if (r.kontakt_id) createQs.set('contact_id', String(r.kontakt_id));
|
||||||
createQs.set('telefoni_opkald_id', String(r.id));
|
if (canMutateCall) createQs.set('telefoni_opkald_id', String(callId));
|
||||||
createQs.set('title', `Telefonsamtale – ${numberForTitle || 'ukendt nummer'}`);
|
createQs.set('title', `Telefonsamtale – ${numberForTitle || 'ukendt nummer'}`);
|
||||||
|
|
||||||
const sagHtml = r.sag_id
|
const sagHtml = r.sag_id
|
||||||
? `<div class="d-flex gap-2 align-items-center flex-wrap">
|
? `<div class="d-flex gap-2 align-items-center flex-wrap">
|
||||||
<a href="/sag/${r.sag_id}/v3">${escapeHtml(r.sag_titel || ('Sag #' + r.sag_id))}</a>
|
<a href="/sag/${r.sag_id}/v3">${escapeHtml(r.sag_titel || ('Sag #' + r.sag_id))}</a>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="linkExistingCase(${Number(r.id)})">Skift link</button>
|
${canMutateCall
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="unlinkCase(${Number(r.id)})">Fjern link</button>
|
? `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="linkExistingCase(${callId})">Skift link</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="unlinkCase(${callId})">Fjern link</button>`
|
||||||
|
: '<span class="badge bg-light text-muted border">Historik</span>'}
|
||||||
</div>`
|
</div>`
|
||||||
: `<div class="d-flex gap-2">
|
: (canMutateCall
|
||||||
<a class="btn btn-sm btn-outline-primary" href="/sag/new?${createQs.toString()}">Opret sag</a>
|
? `<div class="d-flex gap-2">
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="linkExistingCase(${Number(r.id)})">Link sag</button>
|
<a class="btn btn-sm btn-outline-primary" href="/sag/new?${createQs.toString()}">Opret sag</a>
|
||||||
</div>`;
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="linkExistingCase(${callId})">Link sag</button>
|
||||||
|
</div>`
|
||||||
|
: '<span class="text-muted small">Ingen sag</span>');
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user