2026-01-29 23:07:33 +01:00
|
|
|
import logging
|
2026-02-01 11:58:44 +01:00
|
|
|
from fastapi import APIRouter, HTTPException, Query, Request
|
2026-01-29 23:07:33 +01:00
|
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from app.core.database import execute_query
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
2026-02-01 00:38:10 +01:00
|
|
|
# Setup template directory
|
2026-02-01 11:58:44 +01:00
|
|
|
templates = Jinja2Templates(directory="app")
|
2026-02-01 00:38:10 +01:00
|
|
|
|
|
|
|
|
@router.get("/sag", response_class=HTMLResponse)
|
|
|
|
|
async def sager_liste(
|
2026-02-01 11:58:44 +01:00
|
|
|
request: Request,
|
2026-02-01 00:38:10 +01:00
|
|
|
status: str = Query(None),
|
|
|
|
|
tag: str = Query(None),
|
|
|
|
|
customer_id: int = Query(None),
|
2026-02-06 10:47:14 +01:00
|
|
|
include_deferred: bool = Query(False),
|
2026-02-01 00:38:10 +01:00
|
|
|
):
|
2026-01-29 23:07:33 +01:00
|
|
|
"""Display list of all cases."""
|
2026-02-01 00:38:10 +01:00
|
|
|
try:
|
2026-02-01 11:58:44 +01:00
|
|
|
query = """
|
|
|
|
|
SELECT s.*,
|
|
|
|
|
c.name as customer_name,
|
|
|
|
|
CONCAT(COALESCE(cont.first_name, ''), ' ', COALESCE(cont.last_name, '')) as kontakt_navn
|
|
|
|
|
FROM sag_sager s
|
|
|
|
|
LEFT JOIN customers c ON s.customer_id = c.id
|
|
|
|
|
LEFT JOIN LATERAL (
|
|
|
|
|
SELECT cc.contact_id
|
|
|
|
|
FROM contact_companies cc
|
|
|
|
|
WHERE cc.customer_id = c.id
|
|
|
|
|
ORDER BY cc.is_primary DESC NULLS LAST, cc.id ASC
|
|
|
|
|
LIMIT 1
|
|
|
|
|
) cc_first ON true
|
|
|
|
|
LEFT JOIN contacts cont ON cc_first.contact_id = cont.id
|
2026-02-06 10:47:14 +01:00
|
|
|
LEFT JOIN sag_sager ds ON ds.id = s.deferred_until_case_id
|
2026-02-01 11:58:44 +01:00
|
|
|
WHERE s.deleted_at IS NULL
|
|
|
|
|
"""
|
2026-02-01 00:38:10 +01:00
|
|
|
params = []
|
2026-02-06 10:47:14 +01:00
|
|
|
|
|
|
|
|
if not include_deferred:
|
2026-02-09 15:30:07 +01:00
|
|
|
query += " AND ("
|
|
|
|
|
query += "s.deferred_until IS NULL"
|
|
|
|
|
query += " OR s.deferred_until <= NOW()"
|
|
|
|
|
query += " OR (s.deferred_until_case_id IS NOT NULL AND s.deferred_until_status IS NOT NULL AND ds.status = s.deferred_until_status)"
|
|
|
|
|
query += ")"
|
2026-02-01 00:38:10 +01:00
|
|
|
|
|
|
|
|
if status:
|
2026-02-01 11:58:44 +01:00
|
|
|
query += " AND s.status = %s"
|
2026-02-01 00:38:10 +01:00
|
|
|
params.append(status)
|
|
|
|
|
if customer_id:
|
2026-02-01 11:58:44 +01:00
|
|
|
query += " AND s.customer_id = %s"
|
2026-02-01 00:38:10 +01:00
|
|
|
params.append(customer_id)
|
|
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
query += " ORDER BY s.created_at DESC"
|
2026-02-01 00:38:10 +01:00
|
|
|
sager = execute_query(query, tuple(params))
|
|
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
# Fetch relations for all cases
|
|
|
|
|
relations_query = """
|
|
|
|
|
SELECT
|
|
|
|
|
sr.kilde_sag_id,
|
|
|
|
|
sr.målsag_id,
|
|
|
|
|
sr.relationstype,
|
|
|
|
|
sr.id as relation_id
|
|
|
|
|
FROM sag_relationer sr
|
|
|
|
|
WHERE sr.deleted_at IS NULL
|
|
|
|
|
"""
|
|
|
|
|
all_relations = execute_query(relations_query, ())
|
|
|
|
|
child_ids = set()
|
|
|
|
|
|
|
|
|
|
# Build relations map: {sag_id: [list of related sag_ids]}
|
|
|
|
|
relations_map = {}
|
|
|
|
|
for rel in all_relations or []:
|
|
|
|
|
if rel.get('målsag_id') is not None:
|
|
|
|
|
child_ids.add(rel['målsag_id'])
|
|
|
|
|
# Add forward relation
|
|
|
|
|
if rel['kilde_sag_id'] not in relations_map:
|
|
|
|
|
relations_map[rel['kilde_sag_id']] = []
|
|
|
|
|
relations_map[rel['kilde_sag_id']].append({
|
|
|
|
|
'target_id': rel['målsag_id'],
|
|
|
|
|
'type': rel['relationstype'],
|
|
|
|
|
'direction': 'forward'
|
|
|
|
|
})
|
|
|
|
|
# Add backward relation
|
|
|
|
|
if rel['målsag_id'] not in relations_map:
|
|
|
|
|
relations_map[rel['målsag_id']] = []
|
|
|
|
|
relations_map[rel['målsag_id']].append({
|
|
|
|
|
'target_id': rel['kilde_sag_id'],
|
|
|
|
|
'type': rel['relationstype'],
|
|
|
|
|
'direction': 'backward'
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-01 00:38:10 +01:00
|
|
|
# Filter by tag if provided
|
|
|
|
|
if tag and sager:
|
|
|
|
|
sag_ids = [s['id'] for s in sager]
|
|
|
|
|
tag_query = "SELECT sag_id FROM sag_tags WHERE tag_navn = %s AND deleted_at IS NULL"
|
|
|
|
|
tagged = execute_query(tag_query, (tag,))
|
|
|
|
|
tagged_ids = set(t['sag_id'] for t in tagged)
|
|
|
|
|
sager = [s for s in sager if s['id'] in tagged_ids]
|
|
|
|
|
|
|
|
|
|
# Fetch all distinct statuses and tags for filters
|
|
|
|
|
statuses = execute_query("SELECT DISTINCT status FROM sag_sager WHERE deleted_at IS NULL ORDER BY status", ())
|
|
|
|
|
all_tags = execute_query("SELECT DISTINCT tag_navn FROM sag_tags WHERE deleted_at IS NULL ORDER BY tag_navn", ())
|
2026-02-06 10:47:14 +01:00
|
|
|
|
|
|
|
|
toggle_include_deferred_url = str(
|
|
|
|
|
request.url.include_query_params(include_deferred="0" if include_deferred else "1")
|
|
|
|
|
)
|
2026-02-01 00:38:10 +01:00
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
return templates.TemplateResponse("modules/sag/templates/index.html", {
|
2026-02-01 00:38:10 +01:00
|
|
|
"request": request,
|
|
|
|
|
"sager": sager,
|
2026-02-01 11:58:44 +01:00
|
|
|
"relations_map": relations_map,
|
|
|
|
|
"child_ids": list(child_ids),
|
2026-02-01 00:38:10 +01:00
|
|
|
"statuses": [s['status'] for s in statuses],
|
|
|
|
|
"all_tags": [t['tag_navn'] for t in all_tags],
|
|
|
|
|
"current_status": status,
|
|
|
|
|
"current_tag": tag,
|
2026-02-06 10:47:14 +01:00
|
|
|
"include_deferred": include_deferred,
|
|
|
|
|
"toggle_include_deferred_url": toggle_include_deferred_url,
|
2026-02-01 00:38:10 +01:00
|
|
|
})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("❌ Error displaying case list: %s", e)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to load case list")
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
@router.get("/sag/new", response_class=HTMLResponse)
|
|
|
|
|
async def opret_sag_side(request: Request):
|
|
|
|
|
"""Show create case form."""
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/create.html", {"request": request})
|
|
|
|
|
|
|
|
|
|
@router.get("/sag/varekob-salg", response_class=HTMLResponse)
|
|
|
|
|
async def sag_varekob_salg(request: Request):
|
|
|
|
|
"""Display orders overview for all purchases and sales."""
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/varekob_salg.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-01 00:38:10 +01:00
|
|
|
@router.get("/sag/{sag_id}", response_class=HTMLResponse)
|
2026-02-01 11:58:44 +01:00
|
|
|
async def sag_detaljer(request: Request, sag_id: int):
|
2026-01-29 23:07:33 +01:00
|
|
|
"""Display case details."""
|
2026-02-01 00:38:10 +01:00
|
|
|
try:
|
|
|
|
|
# Fetch main case
|
|
|
|
|
sag_query = "SELECT * FROM sag_sager WHERE id = %s AND deleted_at IS NULL"
|
|
|
|
|
sag_result = execute_query(sag_query, (sag_id,))
|
|
|
|
|
|
|
|
|
|
if not sag_result:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Case not found")
|
|
|
|
|
|
|
|
|
|
sag = sag_result[0]
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
# Fetch tags (Support both Legacy sag_tags and New entity_tags)
|
|
|
|
|
# First try the new system (entity_tags) which the valid frontend uses
|
|
|
|
|
tags_query = """
|
|
|
|
|
SELECT t.name as tag_navn
|
|
|
|
|
FROM tags t
|
|
|
|
|
JOIN entity_tags et ON t.id = et.tag_id
|
|
|
|
|
WHERE et.entity_type = 'case' AND et.entity_id = %s
|
|
|
|
|
"""
|
2026-02-01 00:38:10 +01:00
|
|
|
tags = execute_query(tags_query, (sag_id,))
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
|
|
|
|
|
# If empty, try legacy table fallback
|
|
|
|
|
if not tags:
|
|
|
|
|
tags_query_legacy = "SELECT * FROM sag_tags WHERE sag_id = %s AND deleted_at IS NULL ORDER BY created_at DESC"
|
|
|
|
|
tags = execute_query(tags_query_legacy, (sag_id,))
|
2026-02-01 00:38:10 +01:00
|
|
|
|
|
|
|
|
# Fetch relations
|
|
|
|
|
relationer_query = """
|
|
|
|
|
SELECT sr.*,
|
|
|
|
|
ss_kilde.titel as kilde_titel,
|
|
|
|
|
ss_mål.titel as mål_titel
|
|
|
|
|
FROM sag_relationer sr
|
|
|
|
|
JOIN sag_sager ss_kilde ON sr.kilde_sag_id = ss_kilde.id
|
|
|
|
|
JOIN sag_sager ss_mål ON sr.målsag_id = ss_mål.id
|
|
|
|
|
WHERE (sr.kilde_sag_id = %s OR sr.målsag_id = %s)
|
|
|
|
|
AND sr.deleted_at IS NULL
|
|
|
|
|
ORDER BY sr.created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
relationer = execute_query(relationer_query, (sag_id, sag_id))
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
# --- Relation Tree Construction ---
|
|
|
|
|
relation_tree = []
|
|
|
|
|
try:
|
2026-02-15 11:12:58 +01:00
|
|
|
from app.modules.sag.services.relation_service import RelationService
|
|
|
|
|
relation_tree = RelationService.get_relation_tree(sag_id)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error building relation tree: {e}")
|
|
|
|
|
relation_tree = []
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error building relation tree: {e}")
|
|
|
|
|
relation_tree = []
|
|
|
|
|
|
2026-02-01 00:38:10 +01:00
|
|
|
# Fetch customer info if customer_id exists
|
|
|
|
|
customer = None
|
2026-02-01 11:58:44 +01:00
|
|
|
hovedkontakt = None
|
2026-02-01 00:38:10 +01:00
|
|
|
if sag.get('customer_id'):
|
|
|
|
|
customer_query = "SELECT * FROM customers WHERE id = %s"
|
|
|
|
|
customer_result = execute_query(customer_query, (sag['customer_id'],))
|
|
|
|
|
if customer_result:
|
|
|
|
|
customer = customer_result[0]
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
|
2026-02-06 10:47:14 +01:00
|
|
|
# Fetch hovedkontakt (primary contact) for case via sag_kontakter
|
|
|
|
|
kontakt_query = """
|
|
|
|
|
SELECT c.*
|
|
|
|
|
FROM contacts c
|
|
|
|
|
JOIN sag_kontakter sk ON c.id = sk.contact_id
|
|
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL AND sk.is_primary = TRUE
|
|
|
|
|
LIMIT 1
|
|
|
|
|
"""
|
|
|
|
|
kontakt_result = execute_query(kontakt_query, (sag_id,))
|
|
|
|
|
if kontakt_result:
|
|
|
|
|
hovedkontakt = kontakt_result[0]
|
|
|
|
|
else:
|
|
|
|
|
fallback_query = """
|
|
|
|
|
SELECT c.*
|
|
|
|
|
FROM contacts c
|
|
|
|
|
JOIN sag_kontakter sk ON c.id = sk.contact_id
|
|
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
|
|
|
|
|
ORDER BY sk.created_at ASC
|
|
|
|
|
LIMIT 1
|
|
|
|
|
"""
|
|
|
|
|
fallback_result = execute_query(fallback_query, (sag_id,))
|
|
|
|
|
if fallback_result:
|
|
|
|
|
hovedkontakt = fallback_result[0]
|
|
|
|
|
|
|
|
|
|
# Fetch prepaid cards for customer
|
|
|
|
|
# Cast remaining_hours to float to avoid Jinja formatting issues with Decimal
|
|
|
|
|
# DEBUG: Logging customer ID
|
|
|
|
|
prepaid_cards = []
|
|
|
|
|
if sag.get('customer_id'):
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
cid = sag.get('customer_id')
|
|
|
|
|
logger.info(f"🔎 Looking up prepaid cards for Sag {sag_id}, Customer ID: {cid} (Type: {type(cid)})")
|
|
|
|
|
|
|
|
|
|
pc_query = """
|
2026-02-08 01:45:00 +01:00
|
|
|
SELECT id, card_number, CAST(remaining_hours AS FLOAT) as remaining_hours, expires_at
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
FROM tticket_prepaid_cards
|
2026-02-08 01:45:00 +01:00
|
|
|
WHERE customer_id = %s
|
|
|
|
|
AND status = 'active'
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
AND remaining_hours > 0
|
|
|
|
|
ORDER BY created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
prepaid_cards = execute_query(pc_query, (cid,))
|
|
|
|
|
logger.info(f"💳 Found {len(prepaid_cards)} prepaid cards for customer {cid}")
|
|
|
|
|
|
2026-02-08 01:45:00 +01:00
|
|
|
# Fetch fixed-price agreements for customer
|
|
|
|
|
fixed_price_agreements = []
|
|
|
|
|
if sag.get('customer_id'):
|
|
|
|
|
cid = sag.get('customer_id')
|
|
|
|
|
logger.info(f"🔎 Looking up fixed-price agreements for Sag {sag_id}, Customer ID: {cid}")
|
|
|
|
|
|
|
|
|
|
fpa_query = """
|
|
|
|
|
SELECT
|
|
|
|
|
a.id,
|
|
|
|
|
a.agreement_number,
|
|
|
|
|
a.monthly_hours,
|
|
|
|
|
COALESCE(bp.remaining_hours, a.monthly_hours) as remaining_hours_this_month
|
|
|
|
|
FROM customer_fixed_price_agreements a
|
|
|
|
|
LEFT JOIN fixed_price_billing_periods bp ON (
|
|
|
|
|
a.id = bp.agreement_id
|
|
|
|
|
AND bp.period_start <= CURRENT_DATE
|
|
|
|
|
AND bp.period_end >= CURRENT_DATE
|
|
|
|
|
)
|
|
|
|
|
WHERE a.customer_id = %s
|
|
|
|
|
AND a.status = 'active'
|
|
|
|
|
AND (a.end_date IS NULL OR a.end_date >= CURRENT_DATE)
|
|
|
|
|
ORDER BY a.created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
fixed_price_agreements = execute_query(fpa_query, (cid,))
|
|
|
|
|
logger.info(f"📋 Found {len(fixed_price_agreements)} fixed-price agreements for customer {cid}")
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
# Fetch Nextcloud Instance for this customer
|
|
|
|
|
nextcloud_instance = None
|
|
|
|
|
if customer:
|
|
|
|
|
nc_query = "SELECT * FROM nextcloud_instances WHERE customer_id = %s AND deleted_at IS NULL"
|
|
|
|
|
nc_result = execute_query(nc_query, (customer['id'],))
|
|
|
|
|
if nc_result:
|
|
|
|
|
nextcloud_instance = nc_result[0]
|
|
|
|
|
|
|
|
|
|
# Fetch linked contacts
|
|
|
|
|
contacts_query = """
|
2026-02-06 10:47:14 +01:00
|
|
|
SELECT
|
|
|
|
|
sk.*,
|
|
|
|
|
c.first_name || ' ' || c.last_name as contact_name,
|
|
|
|
|
c.email as contact_email,
|
|
|
|
|
c.phone,
|
|
|
|
|
c.mobile,
|
|
|
|
|
c.title,
|
|
|
|
|
company.customer_name
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
FROM sag_kontakter sk
|
|
|
|
|
JOIN contacts c ON sk.contact_id = c.id
|
2026-02-06 10:47:14 +01:00
|
|
|
LEFT JOIN LATERAL (
|
|
|
|
|
SELECT cu.name AS customer_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, cu.name
|
|
|
|
|
LIMIT 1
|
|
|
|
|
) company ON TRUE
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
|
|
|
|
|
"""
|
|
|
|
|
contacts = execute_query(contacts_query, (sag_id,))
|
|
|
|
|
|
|
|
|
|
# Fetch linked customers
|
|
|
|
|
customers_query = """
|
|
|
|
|
SELECT sk.*, c.name as customer_name, c.email as customer_email
|
|
|
|
|
FROM sag_kunder sk
|
|
|
|
|
JOIN customers c ON sk.customer_id = c.id
|
|
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
|
|
|
|
|
"""
|
|
|
|
|
customers = execute_query(customers_query, (sag_id,))
|
2026-02-01 00:38:10 +01:00
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
# Fetch comments
|
|
|
|
|
comments_query = "SELECT * FROM sag_kommentarer WHERE sag_id = %s AND deleted_at IS NULL ORDER BY created_at ASC"
|
|
|
|
|
comments = execute_query(comments_query, (sag_id,))
|
|
|
|
|
|
|
|
|
|
# Fetch Solution
|
|
|
|
|
solution_query = "SELECT * FROM sag_solutions WHERE sag_id = %s"
|
|
|
|
|
solution_res = execute_query(solution_query, (sag_id,))
|
|
|
|
|
solution = solution_res[0] if solution_res else None
|
|
|
|
|
|
|
|
|
|
# Fetch Time Entries
|
|
|
|
|
time_query = "SELECT * FROM tmodule_times WHERE sag_id = %s ORDER BY worked_date DESC"
|
|
|
|
|
time_entries = execute_query(time_query, (sag_id,))
|
|
|
|
|
|
2026-02-14 02:26:29 +01:00
|
|
|
# Fetch linked telephony call history
|
|
|
|
|
call_history_query = """
|
|
|
|
|
SELECT
|
|
|
|
|
t.id,
|
|
|
|
|
t.callid,
|
|
|
|
|
t.direction,
|
|
|
|
|
t.ekstern_nummer,
|
|
|
|
|
t.started_at,
|
|
|
|
|
t.ended_at,
|
|
|
|
|
t.duration_sec,
|
|
|
|
|
u.username,
|
|
|
|
|
u.full_name,
|
|
|
|
|
CONCAT(COALESCE(c.first_name, ''), ' ', COALESCE(c.last_name, '')) AS contact_name
|
|
|
|
|
FROM telefoni_opkald t
|
|
|
|
|
LEFT JOIN users u ON u.user_id = t.bruger_id
|
|
|
|
|
LEFT JOIN contacts c ON c.id = t.kontakt_id
|
|
|
|
|
WHERE t.sag_id = %s
|
|
|
|
|
ORDER BY t.started_at DESC
|
|
|
|
|
LIMIT 200
|
|
|
|
|
"""
|
|
|
|
|
call_history = execute_query(call_history_query, (sag_id,))
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
# Check for nextcloud integration (case-insensitive, insensitive to whitespace)
|
|
|
|
|
logger.info(f"Checking tags for Nextcloud on case {sag_id}: {tags}")
|
|
|
|
|
is_nextcloud = any(t['tag_navn'] and t['tag_navn'].strip().lower() == 'nextcloud' for t in tags)
|
|
|
|
|
logger.info(f"is_nextcloud result: {is_nextcloud}")
|
|
|
|
|
|
2026-02-06 10:47:14 +01:00
|
|
|
related_case_options = []
|
|
|
|
|
try:
|
|
|
|
|
related_ids = set()
|
|
|
|
|
for rel in relationer or []:
|
|
|
|
|
related_ids.add(rel["kilde_sag_id"])
|
|
|
|
|
related_ids.add(rel["målsag_id"])
|
|
|
|
|
related_ids.discard(sag_id)
|
|
|
|
|
if related_ids:
|
|
|
|
|
placeholders = ",".join(["%s"] * len(related_ids))
|
|
|
|
|
related_query = f"SELECT id, titel, status FROM sag_sager WHERE id IN ({placeholders}) AND deleted_at IS NULL"
|
|
|
|
|
related_case_options = execute_query(related_query, tuple(related_ids))
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("❌ Error building related case options: %s", e)
|
|
|
|
|
related_case_options = []
|
|
|
|
|
|
2026-02-15 11:12:58 +01:00
|
|
|
pipeline_stages = []
|
|
|
|
|
try:
|
|
|
|
|
pipeline_stages = execute_query(
|
|
|
|
|
"SELECT id, name, color, sort_order FROM pipeline_stages ORDER BY sort_order ASC, id ASC",
|
|
|
|
|
(),
|
|
|
|
|
)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.warning("⚠️ Could not load pipeline stages: %s", e)
|
|
|
|
|
pipeline_stages = []
|
|
|
|
|
|
2026-02-06 10:47:14 +01:00
|
|
|
statuses = execute_query("SELECT DISTINCT status FROM sag_sager WHERE deleted_at IS NULL ORDER BY status", ())
|
|
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
return templates.TemplateResponse("modules/sag/templates/detail.html", {
|
2026-02-01 00:38:10 +01:00
|
|
|
"request": request,
|
2026-02-01 11:58:44 +01:00
|
|
|
"case": sag,
|
2026-02-01 00:38:10 +01:00
|
|
|
"customer": customer,
|
2026-02-01 11:58:44 +01:00
|
|
|
"hovedkontakt": hovedkontakt,
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
"contacts": contacts,
|
|
|
|
|
"customers": customers,
|
|
|
|
|
"prepaid_cards": prepaid_cards,
|
2026-02-08 01:45:00 +01:00
|
|
|
"fixed_price_agreements": fixed_price_agreements,
|
2026-02-01 00:38:10 +01:00
|
|
|
"tags": tags,
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
|
2026-02-01 00:38:10 +01:00
|
|
|
"relationer": relationer,
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
"relation_tree": relation_tree,
|
|
|
|
|
"comments": comments,
|
|
|
|
|
"solution": solution,
|
|
|
|
|
"time_entries": time_entries,
|
2026-02-14 02:26:29 +01:00
|
|
|
"call_history": call_history,
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
"is_nextcloud": is_nextcloud,
|
|
|
|
|
"nextcloud_instance": nextcloud_instance,
|
2026-02-06 10:47:14 +01:00
|
|
|
"related_case_options": related_case_options,
|
2026-02-15 11:12:58 +01:00
|
|
|
"pipeline_stages": pipeline_stages,
|
2026-02-06 10:47:14 +01:00
|
|
|
"status_options": [s["status"] for s in statuses],
|
2026-02-01 00:38:10 +01:00
|
|
|
})
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("❌ Error displaying case details: %s", e)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to load case details")
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/sag/{sag_id}/edit", response_class=HTMLResponse)
|
|
|
|
|
async def sag_rediger(request: Request, sag_id: int):
|
|
|
|
|
"""Display edit case form."""
|
|
|
|
|
try:
|
|
|
|
|
sag_query = "SELECT * FROM sag_sager WHERE id = %s AND deleted_at IS NULL"
|
|
|
|
|
sag_result = execute_query(sag_query, (sag_id,))
|
|
|
|
|
|
|
|
|
|
if not sag_result:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Case not found")
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/edit.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"case": sag_result[0],
|
|
|
|
|
})
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("❌ Error loading edit case page: %s", e)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to load edit case page")
|