--- quick_id: 260811-j04 slug: doe-tender-links-point-to-the-api-instea date: 2026-08-11 status: complete relates_to: tender-radar closes_todo: 2026-08-05-ausschreibungsportal-falsche-url commits: - ecf7872 fix(tenders): link DOE notices to the notice page, not the API --- # Summary: DÖE-Ausschreibungen verlinken auf die Bekanntmachungsseite ## Entscheidung des Users Von den beiden möglichen Zielen — Bekanntmachungsseite auf oeffentlichevergabe.de oder Link auf das ausschreibende Portal selbst (`tender.documents[0].url`) — hat der User am 2026-08-11 die **Bekanntmachungsseite** gewählt. ## Was gemacht wurde `doe-opendata.adapter.ts` baut `sourceUrl` jetzt über die neue Funktion `buildDoeNoticeUrl()` aus der Bekanntmachungsnummer, statt `ocdsDocument.uri` durchzureichen. Die Nummer liegt ohnehin schon als `sourceNoticeId` im Datensatz, es braucht also keinen zweiten Abruf. Dazu die Migration `20260811120000_doe_notice_url_backfill`, die die bereits importierten Zeilen umschreibt — in `Tender` und in `TenderSource`, weil die Detailansicht die Quell-Links zusammengeführter Ausschreibungen einzeln auflistet. ## Was vorher geprüft wurde - **Ist `ocdsDocument.uri` anderswo als Kennung in Gebrauch?** Nein — die einzige Verwendung im gesamten Code war genau diese eine Zeile. - **Ist die Nummer in der API-URL dieselbe wie `releases[0].id`?** Ja, live gegen den Feed geprüft. Der eine auffällige Datensatz in der Datenbank (`sourceNoticeId` 25671684, URL auf 25671680) stammt nicht aus einer Feld-Verwechslung, sondern aus der Dedup-Zusammenführung zweier echter, gleichnamiger Bekanntmachungen ("Bauleistungen"). Nach dem Backfill folgt die URL der Kennung der Zeile und ist damit in sich stimmig. - **Funktioniert die Zielseite für beide Kennungsformen?** Ja, im Browser verifiziert: numerisch (25673764 → "Feuerwehr-Gerätehaus Miehlen Fliesenarbeiten") und als UUID (7085ba12-… → "Holzfassade"). - **Reicht ein HTTP-Statuscheck?** Nein, und das ist die Falle an dieser Stelle: die Zielseite ist eine Single-Page-App und antwortet auf JEDE Kennung mit 200 und identischen 1309 Bytes, auch auf `NONSENSE123`. Nur der gerenderte Inhalt beweist etwas. - **Wie viele Zeilen betrifft der Nachlauf?** Read-only auf alpha gezählt: 2846 DÖE-Zeilen betroffen, 0 fallen durch die Kennungsprüfung. ## Verifikation - `pnpm --filter @tessera/api test` — 575 Tests grün (41 Dateien) - `npx tsc --noEmit` — sauber - Neue Tests: der Adapter setzt für jeden Datensatz die Bekanntmachungs-URL und nie mehr `/api/notices/`; `buildDoeNoticeUrl()` escapet die Kennung; das Migrations-SQL fasst beide Tabellen an, ist idempotent und rührt andere Portale nicht an. ## Offen Der Nachlauf läuft erst beim nächsten Deploy an, weil Migrationen beim Containerstart ausgeführt werden. Danach im Browser gegenprüfen, dass ein Treffer aus der Liste auf der Bekanntmachung landet.