fix: stabilize call->case prefill and migration status routing

This commit is contained in:
Christian 2026-03-26 00:32:54 +01:00
parent 9f563941e6
commit 5b24c5d978
6 changed files with 106 additions and 8 deletions

View File

@ -107,6 +107,21 @@ class MissionService:
FROM contacts c
WHERE RIGHT(regexp_replace(COALESCE(c.phone, ''), '\\D', '', 'g'), 8) = RIGHT(regexp_replace(%s, '\\D', '', 'g'), 8)
OR RIGHT(regexp_replace(COALESCE(c.mobile, ''), '\\D', '', 'g'), 8) = RIGHT(regexp_replace(%s, '\\D', '', 'g'), 8)
ORDER BY (
SELECT COUNT(*)
FROM sag_kontakter sk
JOIN sag_sager s ON s.id = sk.sag_id
WHERE sk.contact_id = c.id
AND sk.deleted_at IS NULL
AND s.deleted_at IS NULL
AND LOWER(COALESCE(s.status, '')) <> 'lukket'
) DESC,
(
SELECT MAX(t.started_at)
FROM telefoni_opkald t
WHERE t.kontakt_id = c.id
) DESC NULLS LAST,
c.id ASC
LIMIT 1
"""
row = execute_query_single(query, (caller_number, caller_number))

View File

@ -29,6 +29,14 @@ class TelefoniService:
c.id,
c.first_name,
c.last_name,
(
SELECT cu.id
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_id,
(
SELECT cu.name
FROM contact_companies cc
@ -36,11 +44,25 @@ class TelefoniService:
WHERE cc.contact_id = c.id
ORDER BY cc.is_primary DESC NULLS LAST, cc.id ASC
LIMIT 1
) AS company
) AS company,
(
SELECT COUNT(*)
FROM sag_kontakter sk
JOIN sag_sager s ON s.id = sk.sag_id
WHERE sk.contact_id = c.id
AND sk.deleted_at IS NULL
AND s.deleted_at IS NULL
AND LOWER(COALESCE(s.status, '')) <> 'lukket'
) AS open_case_count,
(
SELECT MAX(t.started_at)
FROM telefoni_opkald t
WHERE t.kontakt_id = c.id
) AS last_call_at
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
ORDER BY open_case_count DESC, last_call_at DESC NULLS LAST, c.id ASC
LIMIT 1
"""
row = execute_query_single(query, (suffix8, suffix8))
@ -49,6 +71,7 @@ class TelefoniService:
return {
"id": row["id"],
"name": f"{(row.get('first_name') or '').strip()} {(row.get('last_name') or '').strip()}".strip(),
"company_id": row.get("company_id"),
"company": row.get("company"),
}

View File

@ -259,6 +259,14 @@ async def get_setting_categories():
return [row['category'] for row in result] if result else []
@router.get("/settings/migrations/status", tags=["Settings"])
async def get_migration_statuses_api():
"""Expose migration status via API router (served under /api/v1)."""
from app.settings.backend.views import migration_statuses
return migration_statuses()
@router.post("/settings/sync-from-env", tags=["Settings"])
async def sync_settings_from_env():
"""Sync settings from .env file into database (only updates empty values)"""

View File

@ -281,12 +281,6 @@ def migration_statuses():
release_db_connection(conn)
@router.get("/api/v1/settings/migrations/status", tags=["Frontend"])
def migration_statuses_api_v1_alias():
"""API-prefixed alias for environments/routes expecting /api/v1 prefix."""
return migration_statuses()
@router.post("/settings/migrations/execute", tags=["Frontend"])
def execute_migration(payload: MigrationExecution):
"""Execute a migration SQL file"""

View File

@ -0,0 +1,57 @@
-- Migration 151: Reconcile opportunity comment attachments schema with migration 019 expectations
-- Some environments already had pipeline_opportunity_comment_attachments with legacy columns,
-- so migration 019 (CREATE TABLE IF NOT EXISTS) did not add these fields.
ALTER TABLE IF EXISTS pipeline_opportunity_comment_attachments
ADD COLUMN IF NOT EXISTS content_type VARCHAR(100),
ADD COLUMN IF NOT EXISTS stored_name TEXT,
ADD COLUMN IF NOT EXISTS uploaded_by_user_id INTEGER;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'pipeline_opportunity_comment_attachments'
AND column_name = 'file_path'
) THEN
UPDATE pipeline_opportunity_comment_attachments
SET stored_name = COALESCE(stored_name, file_path)
WHERE stored_name IS NULL;
END IF;
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'pipeline_opportunity_comment_attachments'
AND column_name = 'file_type'
) THEN
UPDATE pipeline_opportunity_comment_attachments
SET content_type = COALESCE(content_type, file_type)
WHERE content_type IS NULL;
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'pipeline_opportunity_comment_attachments_uploaded_by_fkey'
) THEN
ALTER TABLE pipeline_opportunity_comment_attachments
ADD CONSTRAINT pipeline_opportunity_comment_attachments_uploaded_by_fkey
FOREIGN KEY (uploaded_by_user_id)
REFERENCES users(user_id)
ON DELETE SET NULL;
END IF;
END
$$;
-- Keep existing rows valid while allowing future inserts to set explicit stored_name.
UPDATE pipeline_opportunity_comment_attachments
SET stored_name = COALESCE(stored_name, filename)
WHERE stored_name IS NULL;

View File

@ -163,6 +163,7 @@
if (action === 'create-case') {
const qs = new URLSearchParams();
if (contact?.id) qs.set('contact_id', String(contact.id));
if (contact?.company_id) qs.set('customer_id', String(contact.company_id));
qs.set('title', `Telefonsamtale ${number}`);
qs.set('telefoni_opkald_id', String(callId));
window.location.href = `/sag/new?${qs.toString()}`;