From 0ed450451d08c0bdd0480f085c6f78ec62bb2d10 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 16 May 2026 10:28:05 +0200 Subject: [PATCH] fix(contacts): stabilize contacts pagination and company enrichment --- RELEASE_NOTES_v2.3.1.md | 19 ++++++++++ app/contacts/backend/router_simple.py | 51 +++++++++++++++++++-------- 2 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 RELEASE_NOTES_v2.3.1.md diff --git a/RELEASE_NOTES_v2.3.1.md b/RELEASE_NOTES_v2.3.1.md new file mode 100644 index 0000000..fe12b14 --- /dev/null +++ b/RELEASE_NOTES_v2.3.1.md @@ -0,0 +1,19 @@ +# v2.3.1 — 16. maj 2026 + +## Fix: contacts pagination and company enrichment + +- **Hotfix:** Contacts showing too few rows (contacts pagination bug) +- **Fix:** File `app/contacts/backend/router_simple.py` to stabilize pagination and company enrichment + +## Contacts list + +- Fixed bug where contacts list showed too few rows (pagination issue) +- Stabilized company enrichment data for contacts + +## File changed + +- `app/contacts/backend/router_simple.py` + +## Affected versions + +- v2.3.1 diff --git a/app/contacts/backend/router_simple.py b/app/contacts/backend/router_simple.py index 5d1ecfe..674ab22 100644 --- a/app/contacts/backend/router_simple.py +++ b/app/contacts/backend/router_simple.py @@ -119,32 +119,55 @@ async def get_contacts( if is_active is not None: where_clauses.append("c.is_active = %s") params.append(is_active) + + if customer_id is not None: + where_clauses.append( + "EXISTS (SELECT 1 FROM contact_companies cc WHERE cc.contact_id = c.id AND cc.customer_id = %s)" + ) + params.append(customer_id) where_sql = "WHERE " + " AND ".join(where_clauses) if where_clauses else "" - # Count total (needs alias c for consistency) - count_query = f"SELECT COUNT(*) as count FROM contacts c {where_sql}" + # Count total (distinct id for consistency with optional filters/joins) + count_query = f"SELECT COUNT(DISTINCT c.id) as count FROM contacts c {where_sql}" count_result = execute_query(count_query, tuple(params)) total = count_result[0]['count'] if count_result else 0 - # Get contacts with company info - query = f""" + # Step 1: Fetch contacts only (stable pagination) + contacts_query = f""" SELECT c.id, c.first_name, c.last_name, c.email, c.phone, c.mobile, - c.title, c.department, c.user_company, c.is_active, c.created_at, c.updated_at, - COUNT(DISTINCT cc.customer_id) as company_count, - ARRAY_AGG(DISTINCT cu.name ORDER BY cu.name) FILTER (WHERE cu.name IS NOT NULL) as company_names + c.title, c.department, c.user_company, c.is_active, c.created_at, c.updated_at FROM contacts c - LEFT JOIN contact_companies cc ON c.id = cc.contact_id - LEFT JOIN customers cu ON cc.customer_id = cu.id {where_sql} - GROUP BY c.id, c.first_name, c.last_name, c.email, c.phone, c.mobile, - c.title, c.department, c.user_company, c.is_active, c.created_at, c.updated_at - ORDER BY company_count DESC, c.last_name, c.first_name, c.id + ORDER BY c.last_name, c.first_name, c.id LIMIT %s OFFSET %s """ - params.extend([limit, offset]) - contacts = execute_query(query, tuple(params)) + contacts_params = list(params) + contacts_params.extend([limit, offset]) + contacts = execute_query(contacts_query, tuple(contacts_params)) or [] + + # Step 2: Enrich page contacts with aggregated company info + if contacts: + contact_ids = [row["id"] for row in contacts] + placeholders = ",".join(["%s"] * len(contact_ids)) + companies_query = f""" + SELECT + cc.contact_id, + COUNT(DISTINCT cc.customer_id) AS company_count, + ARRAY_AGG(DISTINCT cu.name ORDER BY cu.name) FILTER (WHERE cu.name IS NOT NULL) AS company_names + FROM contact_companies cc + LEFT JOIN customers cu ON cc.customer_id = cu.id + WHERE cc.contact_id IN ({placeholders}) + GROUP BY cc.contact_id + """ + company_rows = execute_query(companies_query, tuple(contact_ids)) or [] + company_map = {row["contact_id"]: row for row in company_rows} + + for contact in contacts: + info = company_map.get(contact["id"]) + contact["company_count"] = int(info["company_count"]) if info and info.get("company_count") is not None else 0 + contact["company_names"] = info.get("company_names") if info and info.get("company_names") else [] return { "total": total,