bmc_hub/fremtidige planer/indgaaende-mail-ny-arkitektur.md
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

32 KiB

Indgående mail: fælles indbakke, sikker sagskobling og varig behandling

Status: Delvist under udvikling lokalt. Den samlede arkitektur er ikke implementeret. Dato: 4. oktober 2026.

Lokal delimplementering 29. oktober 2026

Autoriseret første pakke: enklere højrepanel og bedre afsendergenkendelse. Indgående svar på kendte sager/tråde skal behandles automatisk uden manuel afsendergodkendelse.

  • Højrepanelet samler Afsender og Sag; vurdering og tekniske handlinger er foldet sammen. De store, farvede handlingsknapper bevares øverst over mailen.
  • Nyt read-only sender-identity-endpoint viser alle eksakte kontakt-, mail- og domænematches til aktive kunder og leverandører. Flere mulige tilknytninger præsenteres til valg.
  • Operatøren kan søge eksisterende kunder/leverandører. Ny tilknytning fra afsenderpanelet ændrer kun den valgte mail. Ældre API-klienter beholder eksisterende automatisk domænelagring; det nye panel bruger remember_domain: false.
  • Automatisk routing af kendte svar bevares. En gemt sags-/trådrelation kan nu også overvinde klassifikation som spam/nyhedsbrev. Ukendte nyhedsbrevstråde får ikke denne undtagelse. Globale deaktiveringsindstillinger respekteres fortsat.
  • Testet lokalt med mockede databaser/providers: 27 Python-tests og 12 JavaScript-tests. JavaScript-syntaks og template-parsing kontrolleres separat. Ingen migration, ekstern mailafsendelse eller produktionsudrulning.
  • To direkte valg er tilføjet under de fire eksisterende store knapper: »Knyt til sag« åbner og fokuserer eksisterende sagsøgning; »Leverandørsag« vises for kendt leverandør og åbner et lille panel med titel fra mailen og eksplicit kundevalg. Eksisterende create-sag genbruges med leverandørsagstype og kontaktreference; der oprettes ikke domæneregel for leverandøren.
  • Safari-kontrol af nye knapper: åbning, fokus, kundesøgning, valg, aktivering af Opret og Annuller er verificeret. Ingen sag oprettet og ingen mail flyttet som del af kontrollen.
  • Et entydigt eksakt kontaktmatch tilknyttes nu automatisk i indbakken, uden at gemme domæneregel eller overskrive eksisterende tilknytning. Flere matches og domænematches alene kræver fortsat valg. Almindelige Globalconnect-mails får ikke længere teksten »Internetfaktura genkendt«.
  • Leverandørkontakter matches via vendor_contacts og contacts; læsende kontrol i lokal database bekræfter et entydigt kontaktmatch til Globalconnect for den rapporterede afsender.
  • Safari-genindlæsning af den berørte mail er udført; efterfølgende læsende databasekontrol bekræfter automatisk tilknytning til leverandør 6. Den afsluttende visuelle kontrol blev afbrudt af timeout i browserstyringen. Den samlede pipeline, konflikthåndtering, varig kø og historisk oprydning er fortsat planlagt.

Ordrebekræftelse og levering: lokal udvidelse

Under »Ordre og levering« findes nu to valg: Ordrebekræftelse og Levering / tracking. Begge åbner samme bestillingsvælger med søgning på vare, ekstern reference, kunde og sag. Der vises højst 15 af de senest opdaterede indkøbslinjer; annullerede indkøb og slettede sager udelades. Seneste liste er ikke et automatisk matchforslag.

Eksplicit bekræftelse gemmer sagstilknytning, valgt mailtype, eventuelt manuelt angivet trackingnummer og bestillingsreference i mailens eksisterende aktivitetslog i én transaktion. Gentaget identisk handling er idempotent; en anden eksisterende sag eller bestillingsbeslutning giver konflikt. Ingen indkøbsstatus, økonomisk eksport, ekstern notifikation eller workflow udføres af denne handling. Ingen migration kræves.

Valideret: 22 fokuserede Python-tests og 13 JavaScript-tests. Lokal API genstartet. Bestillingslisten er verificeret læsende mod containerens database (9 valgmuligheder). Safari-kontrol verificerer menu, begge paneltyper, trackingfelt, bestillingsvalg, aktivering af gemmeknap og annullering. Ingen rigtig mail blev knyttet til en bestilling som test; skriveforløbet er dækket med mockede transaktioner, inklusive fejl/rollback og konflikter.

Ensartet betjening

Alle mailhandlinger bruger nu samme store knapkomponent i ét grid; ordre- og leveringsvalg er direkte synlige. Afsender- og sagsøgning i højrepanelet er altid synlig. Alle oplysnings- og handlingssektioner i højrepanelet er permanent synlige med almindelige overskrifter. Ingen fold-ud-pile eller Vis/Skjul-knapper. Handlingsformularer åbnes fortsat ved deres respektive handlingsknap. »Kun arkivér« kan altid bruges uden kendt kunde, leverandør eller kontakt; de øvrige flows bevarer deres identitetskrav. Layoutet er kontrolleret visuelt i den indloggede Safari-session. 13 JavaScript-tests består.

Opret virksomhed og kontakt fra mail: lokal udvidelse

Afsenderpanelet har nu ét direkte valg: »Opret kunde / leverandør + kontakt«. Formularen foreslår navn, mail, direkte telefon, mobil, stilling, afdeling, virksomhedsnavn, CVR, adresse, postnummer, by og hjemmeside fra afsenderheaderen og den nyeste mails signatur. Citerede ældre mails afskæres før udtræk. Et valgfrit AI-valg kan supplere forslagene; alle felter er redigerbare og skal bekræftes før skrivning.

Eksisterende virksomheder foreslås ud fra eksakt navn, CVR, mail og sikkert domænematch. Eksisterende kontakter foreslås ved eksakt mailadresse. Valgte eksisterende poster overskrives ikke. Ny virksomhed, ny kontakt, relation og mailtilknytning gemmes i én transaktion med dubletkontrol, lås, audit og gentagelsessikker payload. Der oprettes ingen domæneregel automatisk.

Valideret lokalt: 40 relevante Python-tests og 13 JavaScript-tests består, inklusive gamle mailflows. Safari-kontrol på Nannas mail viser Leverandør, Globalconnect og den eksisterende kontakt Nanna Søndergaard Skov valgt automatisk. Gem blev ikke aktiveret under kontrollen, og der blev ikke oprettet testdata.

Kontrolleret lokal aktivering af automatisk mailbehandling

Den 29. oktober 2026 blev det planlagte mailjob aktiveret i den lokale Docker-konfiguration med fem minutters interval. EMAIL_REQUIRE_MANUAL_APPROVAL=true bevares: kun verificerede svar med en gemt sag-/trådrelation kan omgå den manuelle gate; nye og tvetydige mails kan ikke automatisk oprette eller vælge sag. Autokør-endpointet er samtidig aktiveret.

Første planlagte kørsel blev observeret i den kørende proces: 50 mails hentet og klassificeret på 3,7 sekunder uden fejl. 47 blev parkeret som awaiting_user_action; tre forblev new, fordi en almindelig reply-header tidligere blev behandlet som et routinghint uden et databasebevist forældrematch. Gaten er efterfølgende strammet: en reply-header alene giver ikke længere automatisk behandling. Kun gemt sag-/trådmatch eller eksplicit BMC-sagsmarkør kan omgå manuel kontrol. Ingen af de 50 blev markeret auto_processed eller knyttet til sag, så første levende succes afventer en ny mail med en kendt relation. Denne afgrænsning og automatisk kendt reply-routing er dækket af 41 relevante Python-tests.

Beslutning og afgrænsning

Retænk modulet som en arbejdsindbakke oven på en pålidelig mailpipeline. Bevar eksisterende mail-ID'er, Graph/IMAP/SMTP, sagsmail, reply/forward, import, regler og specialflows. Genbrug PostgreSQL, eksisterende scheduler, services og sagskomponenter; første version kræver hverken en ny broker eller en separat mailplatform.

Denne plan bygger på fem særskilte kodegennemgange: nuværende flow, sagskobling, UX, robusthed og datamodel/API. Fundene beskriver kodeverificerede risici, ikke dokumenterede produktionshændelser. Ingen mail er hentet eller sendt som del af gennemgangen. Ingen runtimekode eller database ændres af planen.

Nuværende flow og konkrete problemer

Scheduler → Graph/IMAP → gem mail/filer → transskription → klassifikation → godkendelsesgate → systemworkflows og konfigurerede workflows → legacyregler → sagsrelationer og lokal processed-status.

Fund Evidens Ændring i forslaget
Kun seneste N mails hentes; varig beskedcursor mangler app/services/email_service.py:411-412,487-507 Paginering, checkpoint og genoptagelig backlog
Providerfejl returnerer tom liste og kan ligne tom indbakke email_service.py:448-453,499-503; email_processor_service.py:81-83 Resultat med eksplicit syncfejl og sidst lykkede synkronisering
Gemte mails med efterfølgende fejl kommer ikke automatisk tilbage i behandling email_processor_service.py:85-114 Varig jobkø uafhængig af import
En vellykket workflow kan skjule en anden fejlet workflow email_processor_service.py:207-223 Resultater pr. obligatorisk handling; delvis succes er synlig
folder bruges både til kildemappe og lokal arbejdsstatus email_service.py:352-373; email_processor_service.py:217 Separate providerplaceringer og triagestatus
Første mail autoåbnes, og detail-GET kan markere den fælles læst app/emails/frontend/emails_v2.html:848-859; app/emails/backend/router.py:1030-1073 Nye GET-endpoints er uden sideeffekter; eksplicit kvittering
Aktive mails filtreres blandt seneste 150; tællere gælder kun den hentede liste emails_v2.html:748-758,814-840 Backendfiltre, pagination og globale køtal
Modstridende tråd- og sagsreferencer afgøres med første/seneste fund app/services/email_workflow_service.py:456-514,1170-1254 Indsaml alle kandidater og parkér konflikter
Sagskobling og journal udføres i flere separate commits email_workflow_service.py:616-652; app/emails/backend/router.py:1265-1420 Atomiske commands med lås og idempotens
Join-tabel og linked_case_id kan være uenige app/modules/sag/backend/router.py:5864-5877,5997-6002; app/emails/backend/router.py:1556-1604 Én autoritativ relation, gamle felter som kompatibilitetsprojektion
Standardvisningen mangler dele af V1's oprettelsesvalg og har faste sagstyper emails_v2.html:1320-1335,1801-1808; emails.html:2690-2706 Fælles sagsopretter med systemets sagstyper
Én gemt vedhæftning kan skjule, at andre mangler email_processor_service.py:142-151; email_service.py:1388-1430 Manifest og status pr. vedhæftning
Filer mangler relevant dedupe-constraint, og originalnavn indgår i storage-sti migrations/013_email_system.sql:99-115; email_service.py:1404-1425 Unik MIME-partidentitet og genereret sikker storagekey
Download bruger ikke lagrede DB-bytes, hvis diskfil mangler app/emails/backend/router.py:1160-1190 Samme downloadservice med verificeret disk-/DB-fallback
Graph-fejl udløser SMTP-fallback uden særskilt ukendt delivery email_service.py:1996 Outbox og afklaring før potentiel gensendelse

Den samlede arkitektur

flowchart TD
    P[Graph / IMAP / EML / MSG] --> S[MailboxSyncService]
    S --> I[MailIngestService: original, delivery, MIME, filer]
    I --> Q[PostgreSQL-jobkø]
    Q --> R[CaseMailRoutingService: kandidater og beviser]
    Q --> A[Analyse og eksisterende specialworkflows]
    R --> T[Triage: ansvar, handling, vurdering]
    T --> C[CaseMailCommandService]
    C --> J[Sagsrelation og journal i én transaktion]
    C --> O[Outbox for eksterne handlinger]
    J --> U[Fælles indbakke og sagens mailvisning]
    T --> U

Ansvarsfordeling

  1. MailboxSyncService: Provideradaptere, fuld paginering, synkroniseringscursor, rate limits og synlig forbindelsesstatus. Ingen AI eller sagsoprettelse. IMAP forbliver read-only med PEEK. Graph skal bruge stabil beskedidentitet; ændret mappe er en delivery-opdatering, ikke en ny mail.
  2. MailIngestService: Gem original og normaliseret mail, kildeidentitet, headers og vedhæftningsmanifest. Opret behandlingsjobs i samme DB-transaktion. Checkpoint flyttes først efter varig lagring. En ugyldig mail kan gemmes som fejlet import med originalen, så den ikke stopper resten af postkassen.
  3. MailJobRunner: PostgreSQL-kø med FOR UPDATE SKIP LOCKED, lease, forsøg, næste køretid og backoff. Ingen DB-lås under netværkskald. Udløbne leases kan overtages; effektens idempotens beskytter mod gentagelse. Manuel genbehandling genoptager manglende arbejde.
  4. CaseMailRoutingService: Ren kandidatberegning og begrundet beslutning; samme regler for indbakke, jobs og sagsvisning. Ingen writes under preview.
  5. MailTriageService: Arbejdsbehov, ansvarlig/gruppe, prioritet, vurderingsgrund, læsning og fælles kvittering. Behandling og visning ændrer ikke automatisk sagens status.
  6. CaseMailCommandService: Opret, knyt, flyt, tilføj relation og frakobl gennem fælles transaktionskode. Valider payload og adgang før writes. Lås mail/arbejdsenhed; sag, relation, journalhændelse, audit og videre jobs gemmes samlet.
  7. WorkflowRunner: Genbrug eksisterende regler og actions bag adaptere. Gem en versioneret eksekveringsplan og status pr. handling. Retry gentager ikke gennemførte handlinger. Nødvendige og valgfrie handlinger er eksplicitte.
  8. MailOutboxService: Varige sende-/notifikationsintentioner med stabile ID'er. Skeln mellem afvist, accepteret og ukendt afsendelsesresultat. Ingen automatisk SMTP-fallback efter et muligvis accepteret Graph-kald.

Graph bruger fuld bootstrap-paginering og derefter delta/checkpoint, hvor providerens kontrakt understøtter det; udløbet cursor giver afstemt resync, ikke tavs nulstilling. IMAP bruger UIDVALIDITY og UID pr. mappe. Skift af postkasse/provider opretter en ny kildeidentitet; credentialsrotation alene gør ikke. Scheduler kan fortsat udløse arbejdet, men varige leases er autoriteten ved flere processer.

Kobling af mails til sager

Kandidater og prioritering

Alle stærke signaler indsamles, før noget kobles. Prioriteringen hjælper med entydige tilfælde; den må ikke skjule en konflikt.

  1. Tidligere eksplicit manuel beslutning eller trådsplit med versionshistorik.
  2. Gyldigt, serverregistreret scanner-token eller nyt tilfældigt reply-token knyttet til sag og tråd. Token i citeret/videresendt tekst er ikke alene autorisation.
  3. Eksakt kendt parent fra In-Reply-To; dernæst nærmeste kendte References, nyeste først.
  4. Mailbox-afgrænset provider-conversation med entydig kobling.
  5. Eksisterende BMCid/SAG-tag med deltager-, kunde- og konfliktkontrol; legacy Message-ID-markør som kompatibilitetsfallback.
  6. Eksakt kontakt, domæne, emnelighed og AI giver forslag til eksisterende sag. Et kundedomæne alene identificerer ikke en sag.

Modstridende sag/kunde, flere stærke kandidater eller slettet sag giver Kræver vurdering. Systemet opretter ikke en ekstra sag som fallback efter en konflikt. Nye leverandører og CC-deltagere kan legitimt svare; ukendt afsender betyder derfor ikke automatisk afvisning. Kendt modstridende kunde kræver stillingtagen.

Sikkert reply på lukket sag vises som Ny mail på lukket sag, uden automatisk genåbning. Brugeren kan beholde relationen, genåbne eller oprette en relateret sag. Eventuel automatisk genåbning kræver en særskilt, synlig indstilling.

Forward starter normalt en ny tråd med reference til kildetråden. Citerede headers/BMCid giver forslag. Eksisterende videresendelse af seneste mail eller hele tråden bevares. Et bestemt antal mailtråde må aldrig udledes af, at de ligger på samme sag.

Automatisk oprettelse for kendte kunder bevares som særskilt indstilling, fortsat deaktiveret som standard. Entydig kundekobling, policy, spam-/loopkontrol og ingen matchkonflikt er forudsætninger. Scannerens tidsheuristik nedgraderes til forslag; egentlige validerede dokumenttokens bevares til automatisk kobling.

Relationer og journal

En sag kan have mange tråde. En mail kan have én primær sagstilknytning og flere supplerende relationer. Konkursflowets eksakte CVR-match til flere kundesager bevares; hver relation har eget journal-event og adgangskontrol. Der må ikke følge deltagerinformation eller mails fra én kundesag automatisk med til en anden.

Indgående mail giver én identificerbar journalhændelse pr. mail og sag med ny tekst, link til original/tråd og filstatus. Genbehandling giver ikke flere kommentarer. Bevar eksisterende kommentarer; første version kan projicere nye mail-events gennem sagens eksisterende kommentarrenderer med en unik source_email_id/eventnøgle.

Flyt primær tilknytning, tilføj supplerende relation, frakobl og fortryd er forskellige commands. Begrundelse og audit gemmes. Et manuelt afvist match må ikke genopstå ved retry. Ved flytning beholdes en historisk journalreference med rettelsesmarkering; originalmailen slettes ikke.

UX: arbejdsindbakken

Desktop har arbejdslisten til venstre, mail/tråd i midten og kunde/sag/handlinger til højre. Mobil bruger liste, tråd og handlingspanel som separate visninger.

Backendberegnede køer: Kræver handling, Ikke tildelt, Mine, Min gruppe, Nye på sager, Afventer, Fejl og Arkiv. Ulæst er et separat filter. Rækker viser kunde/afsender, emne, seneste tekst, antal ulæste, alder, ansvarlig/gruppe, prioritet og primær sag. Køer omfatter også ældre mails; filtre og tællere gælder hele det autoriserede datasæt.

Seneste nye tekst vises først, med foldbar signatur/citering og adgang til original og headers. Ingen automatisk kvittering ved indlæsning. Vis dokumenter med navn, størrelse og hentnings-/sikkerhedsstatus.

Primære handlinger: Knyt til sag, Opret sag, Leverandørfaktura, Bogholderi, Arkivér. Foreslåede sager viser matchbegrundelse, kunde, status og seneste aktivitet. Kundevalg opretter ikke automatisk en permanent domæneregel; “Husk dette domæne” er særskilt.

Sagsopretteren genbruger titel, ny tekst, kunde, kontakt, systemets konfigurerede sagstyper, prioritet, ansvarlig, gruppe, startdato og deadline. Historiske sagstyper bevares på eksisterende sager. Før oprettelse vises hvilke mails/filer, der følger med.

Composer skelner tydeligt mellem Svar og Intern kommentar. Svar viser To/Cc og reply-all før afsendelse. Plusmenu til tildeling med begrundelse, tidsregistrering, løsning, vedhæft og afventer genbruger sagens funktioner. Intern kommentar kan ikke implicit blive kundemail. Tid og løsning kræver relevant sag og eksisterende valideringsregler.

Tråde uden sag kan have køansvarlig. Sagstilknyttet arbejdsbehov arver som standard sagens ansvar; en lokal køoverdragelse ændrer ikke sagens ansvar uden en særskilt handling. AI-hast, sentiment og kunderisiko er adskilte rådgivende signaler. Ingen automatisk ændring af prioritet, SLA eller ansvar i første version, jf. kundestemning-ai.md.

Adskilte tilstande

Dimension Eksempler Betydning
Personligt set bruger + seen_at Hvad den enkelte har åbnet
Fælles kvittering bruger + acknowledged_at Ansvarlig/gruppen har taget imod beskeden
Arbejde kræver handling / afventer / afsluttet / arkiveret Henvendelsens behandling, ikke sagens lukning
Teknisk behandling queued / running / retry / failed / completed Pipeline og nødvendige handlinger
Kobling ukoblet / foreslået / tilknyttet / konflikt Begrundet relation
Prioritet eksisterende kontrollerede prioriteter Menneskeligt triagevalg, adskilt fra alder og AI

Ny indgående reply skaber nyt arbejdsbehov og ulæstindikator, selv om tidligere mails er afsluttet. Både indbakke og V4/V5 læser samme tilstand. En reply gør ikke automatisk hele sagen åben eller løst.

Datamodel: additive ændringer

Undersøg faktisk installationsschema før migration. Bevar eksisterende email_messages, email_attachments, email_analysis, email_activity_log, workflows og sag_emails med deres ID'er. Historiske migrationer genkøres ikke.

Tabel/udvidelse Rolle og centrale constraints
mail_sources, mail_sync_states Stabil kildeidentitet, provider, postkasse; cursor pr. kilde/mappe, sidste succes/fejl og lease. Referér eksisterende krypteret konfiguration, kopiér ikke secrets.
email_deliveries email_id, kilde, provider-ID, folder, UIDVALIDITY/UID eller Graph immutable ID. Unik stabil provideridentitet pr. kilde; samme mail kan findes flere steder.
email_messages ekstra felter Faktisk RFC Message-ID, raw hash/storage-reference, normaliserede headerdata, retning, parseversion og ny tekst. Gamle message_id og globale UNIQUE bevares som legacyidentitet. Nye kollisioner får separat intern legacyværdi, mens faktisk RFC-ID lagres korrekt.
email_threads, medlemskab og provideraliases Stabil intern tråd-ID, mailbox-afgrænsede aliases og parentrelationer. En besked har én aktuel normal tråd; flyt/split auditeres. Sagsrelation bestemmer ikke tråd-ID.
mail_work_items Arbejde pr. tråd og primær sag/ukoblet kø; status, ansvarlig/gruppe, prioritet, seneste indgående besked og versionsnummer. Særskilte kundearbejder ved legitime fler-sagsflows.
email_user_state og kvittering Unik (email_id,user_id) personlig set-state. Fælles kvittering bevarer ansvarlig-/gruppereglen og hvem/hvornår.
sag_emails metadata Primær/supplerende rolle, aktiv/inaktiv, kilde, beslutnings-ID og audit. Højst én aktiv primær relation pr. mail; flere supplerende tilladt. linked_case_id er projektion af primær relation.
email_routing_decisions Kandidater, beviser, algoritmeversion, konflikt, valgt handling, aktør og fravalg. Manuel beslutning kan kun erstattes eksplicit.
email_notes og kopieringsreference Interne triagenoter før sagoprettelse; unik source-note-reference ved senere journaloverførsel, så noter ikke kopieres igen.
mail_jobs, action-runs Unik job/effect-nøgle, payloadversion, lease, forsøg, next_attempt_at, fejl og permanent parkering. Unik workflowhandling pr. mail/planversion/step/mål.
mail_outbox Varig intention, stabil sendnøgle, modtagere, case/thread, attempts, provider-ID og accepted/unknown/rejected. Simuleret afsendelse er ikke faktisk sent.
Vedhæftningsmanifest + casefilkilde MIME part-ID, originalnavn, sikker storagekey, SHA-256, forventet/modtaget størrelse, CID/disposition, status/fejl. Unik part pr. mail; unik casefilreference pr. sag/source-attachment.
Mail-journalhændelse Unik (email_id,sag_id,event_kind); kommentar/event-ID og audit. Undgå dedupe på bodytekst.

Dedupe sker først på sikker deliveryidentitet. RFC Message-ID normaliseres separat fra legacyfeltet og sammenholdes med indhold/proveniens. Samme RFC-ID med forskelligt indhold bliver en synlig kollision, ikke overwrite eller tavs udeladelse. Manglende ID får syntetisk intern ID baseret på kilde og varig deliveryidentitet. Indholdshash alene må ikke samle forskellige leverancer fra kunder eller fjerne en legitim ny henvendelse.

Indekser: jobstatus/next_attempt_at, kilde/provider-ID, normaliseret RFC-ID, parentreferences, thread/seneste aktivitet, aktive sagrelationer, triagestatus/ansvarlig/gruppe/prioritet og ulæststate. Brug TIMESTAMPTZ for nye tidsfelter. Normaliserede identiteter og faste statuser er kolonner; analyse/evidens kan være JSONB.

API og kompatibilitet

Nye routes under /api/v1/mail/...; eksisterende /api/v1/emails, sagsmailroutes og V1/V2 bevares via adapters. Legacy detail-GET's læsebivirkning bevares under overgang; ny UI anvender kun rene GET og eksplicitte commands. Fjernelse af bivirkningen fra gammel API kræver en senere eksplicit beslutning.

Endpointforslag Kontrakt
GET /mail/work-items Serverfiltre, stabil cursor med ID-tiebreaker, pagestørrelse og næste cursor
GET /mail/queue-counts Samme autoriserede kødefinitioner som liste; count-scope beskrives
GET /mail/threads/{id} Tråd, deltagerliste, paginerede beskeder og relationsstatus; ingen writes
GET /mail/messages/{id} Original/ny tekst, headers, filmanifest og audit; ingen writes
POST /mail/messages/{id}/seen Personlig set-state; ændrer ikke fælles arbejdsstatus
POST /mail/messages/{id}/acknowledge Fælles kvittering med eksisterende ansvarlig-/gruppetilladelse
POST /mail/messages/{id}/routing-preview Kandidater og begrundelser uden writes
POST /mail/messages/{id}/case-link og /case-create Atomisk handling; rolle/sag/kunde/begrundelse, expected_version og Idempotency-Key
POST /mail/messages/{id}/case-unlink Eksplicit frakobling og fravalg, ikke sletning af mail
POST /mail/threads/{id}/split Valgte beskeder, måltråd og begrundelse; ingen implicit sagsflytning
PATCH /mail/work-items/{id} Tildeling, prioritet, afventer/afslut med versionskontrol
POST /mail/work-items/{id}/comment Intern kommentar; sag kræves for sagsjournal, ellers intern triagenote
POST /mail/threads/{id}/reply Validerede modtagere, kildebesked og outbox-ID; 202 betyder queued, ikke sent
POST /mail/jobs/{id}/retry Genoptag manglende trin, aldrig automatisk genafsend ukendt delivery
GET /mail/sources/{id}/sync-status Sidste succes, lag, backlog og maskeret fejl

Idempotency-Key gemmes med aktør, operation og payloadhash; samme key/payload returnerer samme resultat, ændret payload giver 409. Versionskonflikt giver 409 med opdateret ressource. Læsning, tilknytning, tildeling, arkivering, filer og afsendelse bruger fælles ressourcetilladelser; autentifikation alene giver ikke adgang til alle kundemails. Fejl returnerer stabil kode, dansk handlingsbesked og correlation-ID uden secrets.

Robusthed og mailpolitik

  • Dubletter/samtidighed: delivery-upsert og constraints; import, job og klik kan gentages uden nye sager, kommentarer, tidslinjer eller filer. Soft-deleted mails genimporteres ikke automatisk som nye.
  • Replies/threading: bevar fulde rå headers; nærmeste kendte parent vægtes. Ukendt parent kan afklares, når en ældre besked importeres. Det må ikke overskrive en manuel beslutning.
  • Forwards/MIME: top-level afsender/headers adskilles fra vedhæftet message/rfc822 og citeret historik. Charset og multipart/alternative behandles korrekt; tekstfiler må ikke blive body. Inline CID-billeder har separat rolle.
  • Filer: grænser før fuld indlæsning for mailstørrelse, antal dele, MIME-dybde og samlet størrelse; begrænset udpakning/OCR/transskription. Sikre genererede storagekeys uden klientfilnavne. Bevar originale navne som metadata. Kontroller faktisk indhold, MIME og checksum; karantæne ved uafklaret sikkerhed. En manglende del er synlig. Invoice-/dokumentflows må ikke afsluttes med ufuldstændige nødvendige filer. Download genbruger DB-bytes, når diskfil mangler, og giver en tydelig fejl, hvis begge kilder mangler.
  • Loops/autosvar: registrér Auto-Submitted, Precedence, List-ID, delivery-status, interne senderidentiteter og egne correlationheaders. Gem beskederne, men undlad automatisk svar/ny sag på egne notifikationer, bounces og autosvar. Manuel behandling forbliver mulig. Ratebegræns autosvar pr. kilde/tråd/modtager.
  • Spam: behold klassifikation/regler og separat karantæne. Spam/nyhedsbrev er reversible dispositioner; intet slettes alene på AI-resultat. Modstridende stærkt sagsmatch vises til vurdering, uden auto-response.
  • Fejl: providerfejl, parsefejl, analysefejl, sagskonflikt og ekstern delivery har forskellige fejlkoder. AI-fejl stopper ikke import eller deterministic routing. Retry med backoff/Retry-After og begrænsede forsøg; derefter fejl-kø med sikker manuel genoptagelse.
  • Eksterne effekter: lokal transaktion giver ikke exactly-once SMTP. Timeout efter send giver unknown, som afklares via provider eller manuelt. Fallback/retry må kun sende igen, når tidligere forsøg sikkert ikke er accepteret. Bevar eksisterende test/dry-run-policy og registrér simulation særskilt.
  • Indhold: genbrug HTML-sanitering og blokér aktive elementer/fjern tracking som standard. Rå original kræver autoriseret download. AI-input behandles som maildata, ikke som instruktioner til værktøjer/actions.

Eksisterende funktioner, der skal bevares

Graph og IMAP/SMTP; krypteret konfiguration og miljøfallback; manuelt force-run; .eml/.msg; read-only hentning; klassifikation og manuel godkendelse; workflowpreview, regler, bulk, arkiv, genbehandling og aktivitetslog; kunde-/leverandørvalg og domæneregler; reply/reply-all, send og videresend seneste/hel tråd; flere tråde pr. sag; scannertokens, BMCid og historiske routingmarkører; vedhæftningsrecovery og sagsfiler; lydtransskription; leverandørfaktura/ALSO/tracking/tid/konkursflows. Ældre tticket-actions bevares bag adapter og flyttes ikke stiltiende til SAG.

Begrundede ændringer: ingen automatisk læsekvittering i ny UI; ingen tavs vinder ved matchkonflikt; scanner-tid alene er forslag; ingen processed-status ved nødvendig delvis fejl; ingen blind gensendelse efter uklar accept. Alle ændringer leveres bag flags med dokumenteret gammel/ny adfærd.

Migration og levering

  1. Baseline og kontrakttests: kortlæg installeret schema, aktive workflows, eksisterende API-responses og permissions. Fixtures skal omfatte support, økonomi, konkurs, scanner, forward og lyd. Ingen eksterne testwrites.
  2. Additiv lagring og sync: nye tabeller/felter, varig importcursor, tydelig syncstatus og manifest. Backfill i små genoptagelige batches. Bevar gamle tabeller og ID'er.
  3. Jobkø og atomiske commands: fælles service bag eksisterende routes; versionerede actionruns og idempotens. Aktiver én write-path pr. flow, så gammelt job og nyt job ikke udfører samme handling.
  4. Routing i shadow mode: beregn og sammenlign beslutninger uden sagswrites, journal, notifikation eller filkopiering. Historiske modstridende relationer markeres til vurdering; de samles eller slettes ikke automatisk.
  5. Ny indbakke: ny route/flag, fælles komponenter med V4/V5, eksplicit set/kvittering, serverkøer og sagsreview. V1/V2 findes stadig.
  6. Kontrolleret aktivering: først sikre replies/entydige matches, derefter specialflows ét ad gangen. Overvåg backlog, cursorlag, retries, konflikter, delvise filer og ukendt afsendelse.

Historisk linked_case_id bliver primær, hvis den er gyldig og konsistent; øvrige relationer bevares som legacy_unclassified, indtil rollen kan dokumenteres. Konflikter får review. Global is_read kan blive historisk fælles kvittering med ukendt aktør; opfind ikke personlige læseevents. Processed bliver ikke automatisk bevis for besvaret mail eller lukket sag. Kildemappe kan være ukendt, indtil providerafstemning; gæt den ikke ud fra processed-status. Tilføj håndhævede constraints efter backfill-validering, ikke ved at slette konflikter.

Rollback slukker nye UI/routingflags og standser nye workers, før gamle jobs genaktiveres. Allerede gennemførte effekter bevares og skal være dedupe-synlige for gamle adapters. Ingen down-migration sletter nye mails, relationer eller audit. Produktionsudrulning er en separat opgave.

Kontrol og accept

Område Nødvendige kontroller
Import Mere end fetchlimit; alle sider; crash før/efter checkpoint; IMAP UID-reset; Graph mappeflyt; timeout/429/tokenfejl; ændring af kildekonfiguration
Identitet Samme delivery to gange; RFC-ID med/uden brackets; manglende ID; samme RFC-ID forskelligt indhold; identiske mails på flere postkasser; soft-delete
Sagskobling Konflikt token/tag/parent; delt domæne; ukendt CC/leverandør; flere tråde pr. sag; lukket/slettet sag; forward med citeret BMCid; legacyrouting; unlink + retry
Atomaritet Samtidigt klik/job; crash mellem sag/link/journal; gentagen payload; ændret payload samme key; uniqueconstraints; konkurs til flere kunder
Filer Delvist download; null/zero bytes; samme navn forskelligt indhold; path traversal; inline CID; nested EML; store/deep MIME; recovery og kopi uden dubletter
Workflows En succes + en nødvendig fejl; genoptag kun manglende trin; ændret workflowversion; AI utilgængelig; faktura/ALSO/tracking/tid/konkurs/scanner/lyd bevares
Send/loops Accepteret send efterfulgt af timeout; ingen blind fallback; bounce/autosvar/egen mail; rategrænse; dry-run sender intet
UX/adgang Indlæsning ændrer intet læst; gammel aktiv mail efter 150 nyere; globale køtal; system-sagstyper; ansvar/hastegrad før opret; personlig læsning vs. fælles kvittering; ingen krydskunde-læk
Kompatibilitet Gamle responses/paths/statusprojektioner, V1/V2 og V4/V5; eksisterende filer, kommentarer, relationer og API-consumers

Browserkontrol på lokalt testdatasæt og mobilbredde; integrationskontrol med fake providers og rollback-transaktioner. Dokumentér separat hvad der er kode-/testverificeret, browserverificeret og endnu ikke verificeret i drift.

Første implementeringspakke efter accept

Afgræns første pakke til providerstatus/paginering, deliveryidentitet, varig jobkø, vedhæftningsmanifest og atomisk opret/kobl-command med kontrakttests. Derefter entydig routing og review. Den nye UX kommer, når tilstande og kommandoer er stabile. Implementér ikke hele omlægningen i én ændring.