feat(backend): Voelker-Modul (Modul 3)

ColoniesModule: CRUD + Move/Split/Merge/Dissolve/History, entsprechend
Lastenheft §4.4 (Lebenszyklus eines Volkes):
- move: Standortwechsel, protokolliert in ColonyApiaryHistory.
- split: erzeugt Ableger als neues Volk, Abstammung ueber
  ColonyLineage(SPLIT, von Mutter- zu Ablegervolk).
- merge: vereinigtes Volk bleibt historisch erhalten (status MERGED,
  deactivatedAt gesetzt statt geloescht), ColonyLineage(MERGE) je
  vereinigtem Volk zum Zielvolk.
- dissolve: Volk wird deaktiviert (status DISSOLVED), Dokumentation
  bleibt erhalten.
- history: aggregiert Standort-Historie, Koeniginnen-Historie und
  Abstammungslinien (als Quelle und als Ergebnis).

Move/Split/Merge laufen transaktional (prisma.$transaction). Rollen wie
bei Apiaries: Lesen offen, Schreiben auf ADMIN/BEEKEEPER beschraenkt.

9 Unit-Tests + 6 e2e-Tests (inkl. vollstaendigem Move->Split->Merge->
Dissolve-Flow und Rollenpruefung), Lint, Typecheck und Build gruen.
This commit is contained in:
Nick Szaunig committed 2026-08-21 09:06:51 +02:00
1 parent d59153a697
commit d3f321f1b5
12 files changed
+781 -1

No files matched your search

@@ -0,0 +1,52 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEnum, IsInt, IsOptional, IsString } from 'class-validator';
import { ColonyType } from '../../../generated/prisma';
export class CreateColonyDto {
@ApiProperty()
@IsString()
code!: string;
@ApiProperty()
@IsString()
apiaryId!: string;
@ApiProperty({ enum: ColonyType })
@IsEnum(ColonyType)
colonyType!: ColonyType;
@ApiPropertyOptional()
@IsOptional()
@IsString()
hiveType?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
originText?: string;
@ApiPropertyOptional()
@IsOptional()
@IsInt()
strength?: number;
@ApiPropertyOptional()
@IsOptional()
@IsString()
broodStatus?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
feedStatus?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
healthStatus?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
notes?: string;
}
@@ -0,0 +1,15 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { ArrayMinSize, IsArray, IsOptional, IsString } from 'class-validator';
export class MergeColonyDto {
@ApiProperty({ type: [String] })
@IsArray()
@ArrayMinSize(1)
@IsString({ each: true })
mergedColonyIds!: string[];
@ApiPropertyOptional()
@IsOptional()
@IsString()
notes?: string;
}
@@ -0,0 +1,13 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsOptional, IsString } from 'class-validator';
export class MoveColonyDto {
@ApiProperty()
@IsString()
apiaryId!: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
notes?: string;
}
@@ -0,0 +1,18 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEnum, IsOptional, IsString } from 'class-validator';
import { ColonyType } from '../../../generated/prisma';
export class SplitColonyDto {
@ApiProperty()
@IsString()
code!: string;
@ApiProperty({ enum: ColonyType })
@IsEnum(ColonyType)
colonyType!: ColonyType;
@ApiPropertyOptional()
@IsOptional()
@IsString()
notes?: string;
}
@@ -0,0 +1,24 @@
import { ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger';
import { IsEnum, IsOptional } from 'class-validator';
import { ColonyStatus } from '../../../generated/prisma';
import { CreateColonyDto } from './create-colony.dto';
class UpdatableColonyFieldsDto extends PartialType(
PickType(CreateColonyDto, [
'hiveType',
'originText',
'colonyType',
'strength',
'broodStatus',
'feedStatus',
'healthStatus',
'notes',
] as const),
) {}
export class UpdateColonyDto extends UpdatableColonyFieldsDto {
@ApiPropertyOptional({ enum: ColonyStatus })
@IsOptional()
@IsEnum(ColonyStatus)
status?: ColonyStatus;
}