bmc_hub/app/services/email_purchase_link.py
Christian 1fb5b4a191 Add comprehensive tests for email and drift functionalities
- Introduced tests for drift operations UI and backend to ensure proper handling of ignore rules and UI elements.
- Added tests for email identity API to validate email linking and identity retrieval without updates.
- Implemented tests for email purchase link service to verify correct handling of purchase mail links and conflict scenarios.
- Created tests for email reply automation to ensure proper routing and processing of replies based on classification.
- Developed tests for email sender creation to validate suggestions and handling of existing contacts and companies.
- Added tests for email sender identity to ensure correct matching of contacts and domains.
- Implemented tests for work desk functionalities to validate task management, meeting registrations, and timer functionalities.
2026-11-03 01:16:12 +01:00

53 lines
3.4 KiB
Python

"""Explicit purchase-mail linking, without changing purchase fulfilment or running workflows."""
import json
from fastapi import HTTPException
from app.core.database import atomic_transaction, execute_query_single, execute_update
def link_purchase_mail(email_id, purchase_id, kind, tracking_number=None, user_id=None):
if kind not in {'order_confirmation', 'freight_note'}:
raise HTTPException(400, 'Ukendt indkøbsmailtype')
tracking = str(tracking_number or '').strip() or None
if tracking and (kind != 'freight_note' or len(tracking) > 100):
raise HTTPException(400, 'Trackingnummer må højst være 100 tegn og bruges kun ved levering')
with atomic_transaction():
mail = execute_query_single(
'SELECT id, linked_case_id FROM email_messages WHERE id = %s AND deleted_at IS NULL FOR UPDATE',
(email_id,))
if not mail:
raise HTTPException(404, 'Mailen blev ikke fundet')
purchase = execute_query_single("""
SELECT p.id, p.sag_id, p.description, s.titel AS case_title
FROM sag_salgsvarer p JOIN sag_sager s ON s.id = p.sag_id
WHERE p.id = %s AND p.type = 'purchase' AND p.status <> 'cancelled'
AND s.deleted_at IS NULL FOR UPDATE OF p, s
""", (purchase_id,))
if not purchase:
raise HTTPException(404, 'Bestillingen findes ikke eller er annulleret')
if mail.get('linked_case_id') and mail['linked_case_id'] != purchase['sag_id']:
raise HTTPException(409, 'Mailen er allerede knyttet til en anden sag. Ret sagstilknytningen først.')
metadata = {'purchase_id': purchase_id, 'sag_id': purchase['sag_id'], 'kind': kind,
'tracking_number': tracking}
previous = execute_query_single("""SELECT metadata FROM email_activity_log
WHERE email_id = %s AND event_type = 'purchase_mail_link'
ORDER BY id DESC LIMIT 1""", (email_id,))
if previous:
if previous['metadata'] != metadata:
raise HTTPException(409, 'Mailen er allerede behandlet som indkøbsmail med andre oplysninger.')
return {'success': True, 'sag_id': purchase['sag_id'], 'idempotent': True,
'message': 'Mailen er allerede knyttet til bestillingen'}
execute_update('''INSERT INTO sag_emails (sag_id, email_id) VALUES (%s, %s)
ON CONFLICT (sag_id, email_id) DO NOTHING''', (purchase['sag_id'], email_id))
execute_update("""UPDATE email_messages SET linked_case_id = %s,
classification = %s, confidence_score = 1.0,
extracted_tracking_number = COALESCE(%s, extracted_tracking_number),
status = 'processed', folder = 'Processed', processed_at = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP WHERE id = %s""",
(purchase['sag_id'], kind, tracking, email_id))
execute_update("""INSERT INTO email_activity_log
(email_id, event_type, event_category, description, metadata, user_id, created_by)
VALUES (%s, 'purchase_mail_link', 'user', %s, %s::jsonb, %s, 'mailindbakke')""",
(email_id, ('Ordrebekræftelse' if kind == 'order_confirmation' else 'Levering / tracking')
+ ': ' + purchase['description'], json.dumps(metadata), user_id))
return {'success': True, 'sag_id': purchase['sag_id'], 'message': 'Mailen er knyttet til bestillingen og sagen'}