feat(backend): Lagerbehälter-Umlagerung für Honigchargen (Modul 32)

Neues HoneyBatchRelocation-Modell dokumentiert jede Umlagerung einer
Honigcharge zwischen Lagerbehältern (von/nach, Zeitpunkt, Benutzer,
Notiz). HoneyBatchesService.relocate() aktualisiert
HoneyBatch.storageContainerId transaktional zusammen mit dem
Historieneintrag; wirft NotFoundException bei unbekanntem Zielbehälter.

Neue Endpunkte: POST /honey-batches/:id/relocate,
GET /honey-batches/:id/relocations. honeyBatchInclude liefert jetzt
zusätzlich den aktuell zugeordneten storageContainer mit.

Unit- und E2E-Tests ergänzt (Mehrfachumlagerung, Historienreihenfolge,
404 bei unbekanntem Behälter). Migration auf Dev- und Test-Datenbank
angewendet.
This commit is contained in:
Nick Szaunig committed 2026-08-24 16:55:15 +02:00
1 parent fa13543578
commit 59a320ae36
7 files changed
+328 -44

No files matched your search

@@ -5,9 +5,11 @@ import { WebhooksService } from '../webhooks/webhooks.service';
import { HoneyBatchStatus } from '../../generated/prisma';
import { CreateHoneyBatchDto } from './dto/create-honey-batch.dto';
import { CreateHoneyMeasurementDto } from './dto/create-honey-measurement.dto';
import { RelocateHoneyBatchDto } from './dto/relocate-honey-batch.dto';
const honeyBatchInclude = {
measurements: true,
storageContainer: true,
extraction: {
include: {
harvests: {
@@ -90,6 +92,50 @@ export class HoneyBatchesService {
return this.findById(id);
}
async relocate(id: string, dto: RelocateHoneyBatchDto, actingUserId: string) {
const honeyBatch = await this.findById(id);
const targetContainer = await this.prisma.storageContainer.findUnique({
where: { id: dto.storageContainerId },
});
if (!targetContainer) {
throw new NotFoundException('Storage container not found');
}
const relocation = await this.prisma.$transaction(async (tx) => {
const created = await tx.honeyBatchRelocation.create({
data: {
honeyBatchId: id,
fromStorageContainerId: honeyBatch.storageContainerId,
toStorageContainerId: dto.storageContainerId,
userId: actingUserId,
notes: dto.notes,
},
});
await tx.honeyBatch.update({
where: { id },
data: { storageContainerId: dto.storageContainerId },
});
return created;
});
await this.auditLog.record({
entityType: 'HoneyBatchRelocation',
entityId: relocation.id,
action: 'CREATE',
userId: actingUserId,
afterData: relocation,
});
return this.findById(id);
}
async findRelocations(id: string) {
await this.findById(id);
return this.prisma.honeyBatchRelocation.findMany({
where: { honeyBatchId: id },
include: { fromStorageContainer: true, toStorageContainer: true },
orderBy: { relocatedAt: 'desc' },
});
}
async traceability(id: string) {
const honeyBatch = await this.findById(id);
return {