fix(worker): fix Bandel ticket sync and reactivate the integration

The status PATCH needs statusName alongside statusId or the API 400s,
so every write-back had been silently failing. The board's flat
assigneeName is null on every live ticket; the real assignee list is
ticketAssignees[]. Import now also picks up In Bearbeitung tickets,
not just Offen ones.

Both issues are live-verified against the real API, so the feature
flag flips back on.
This commit is contained in:
mika kuns
2026-09-02 09:59:06 +02:00
parent f640580463
commit 30d0611b8e
10 changed files with 185 additions and 64 deletions
+14 -4
View File
@@ -275,11 +275,16 @@ refresh token uses, one instance per file, DI markers `OnlineRefreshTokenStore`/
distinguish the two singletons). The token never touches `worker.config.json` or the DB.
- **Import** (`TicketImportService`, hub `ImportTickets`) — per-list, manual only (no polling): pulls
the linked project's board (`ListConfigEntity.TicketProjectId`), keeps tickets that are `StatusId
== 1` (Offen) *and* assigned to the PAT owner (case-insensitive name match), skips any `TicketRef`
the linked project's board (`ListConfigEntity.TicketProjectId`), keeps tickets whose `StatusId` is
`1` (Offen) or `2` (InBearbeitung) *and* that are assigned to the PAT owner, skips any `TicketRef`
already present on the list, and creates one `Idle` task per remainder via `TaskRepository.AddAsync`
(so it gets a `Number` like every other task). Already-imported tickets are never updated — a
re-import would overwrite task notes.
⚠️ **The assignee lives in `ticketAssignees[]`, never in the flat `assigneeName`** — that field is
null on every ticket the live API returns (checked across all 63 in Abteilung Entwicklung,
2026-09-01), so the original filter imported nothing, ever. `personInChargeName` is the wrong
source too: it holds the login on some rows and the display name ("Mika Kuns") on others, while
`ticketAssignees[].assigneeName` matches `/pat/me`'s `userName` format exactly.
- **Write-back** (`TicketStatusSync`) hangs off `TaskStateService.NotifyAsync` — the one place every
status transition passes through. Mapping: `Running`/`WaitingForReview` → ticket status `2
InBearbeitung`, `Done` → `3 Fertig`, everything else writes nothing (`1 Offen` is never written
@@ -290,8 +295,13 @@ distinguish the two singletons). The token never touches `worker.config.json` or
- `BandelTicketClient` wraps the four REST calls (`GET /api/ticketsystem/pat/me`, `GET
/api/Navigation/sidebar`, `GET /api/Board/project/{id}`, `PATCH /api/Ticket/{id}/status`); every
response is unwrapped from `BandelEnvelope<T>` and a non-2xx/`success:false` throws
`TicketApiException` with a readable message. ⚠️ The endpoint shapes were taken from the
`Bandel.APIs` source, never confirmed against a live call — see `docs/open.md`.
`TicketApiException` with a readable message. All four are **live-verified (2026-09-01)** against
`http://192.168.45.161`.
⚠️ `PATCH /api/Ticket/{id}/status` needs **both** `statusId` and `statusName` — omitting the name
is a 400 (`"The StatusName field is required."`) and no status is written. The name is *not*
validated against the id (`statusId` wins, any non-empty string passes), but it must be there;
`TicketStatusMap.NameOf` supplies the canonical name.
⚠️ The base URL must be the **IP** — `bandelapis.fb-tuning.local` still refuses the connection.
## Notes