feat: T4 wave 3 - notifications per user, suggestions API, varroa bands, split/queen events

- Schema III: per-user notifications (broadcast fan-out migration,
  category, link, requiredPermissions filter), notification preferences
  and settings endpoints, calendar withdrawal switch, occupiedFrames
- Suggestions API: list/summary with permission visibility and paging,
  accept/dismiss/reopen incl. bulk, SuggestionView on all responses
- Varroa traffic light by season and method, young colonies, treatment
  effect window, sample-based percentage; shared withdrawal-window helper
  on Berlin calendar days; GET /varroa-measurements/thresholds
- ColonySplit and QueenAssigned domain events with calendar and webhook
  handlers
- Outbox claims compare against now() rounded to timestamp(3); fixes the
  flaky e2e claims of rows written with SQL now()

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-14 14:25:24 +02:00
1 parent d99a697739
commit f468dc5f75
87 files changed
+6022 -510

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
import { INestApplicationContext } from '@nestjs/common';
import type { Suggestion } from '../../generated/prisma';
import { PrismaService } from '../../src/prisma/prisma.service';
import { SuggestionsService } from '../../src/suggestions/suggestions.service';
import type { ProposeInput } from '../../src/suggestions/suggestion.types';
/**
* Proposes a suggestion in its own transaction, as a rule handler would.
* Throws when nothing was proposed (anchor or intent already taken), so a
* test never works with a suggestion that does not exist.
*/
export async function proposeForTest(
app: INestApplicationContext,
input: ProposeInput,
): Promise<Suggestion> {
const prisma = app.get(PrismaService);
const service = app.get(SuggestionsService);
const created = await prisma.$transaction((tx) =>
service.proposeIn(tx, input),
);
if (created === null) {
throw new Error(`proposeForTest: nothing proposed for ${input.anchor}`);
}
return created;
}