Bisher liessen sich diese acht Objekttypen nur anlegen, nie nachträglich korrigieren – Aufgaben hatten zwar schon einen PATCH-Endpunkt, aber keine Bearbeiten-UI, die übrigen sieben hatten weder Backend- noch Frontend-Unterstützung. Jetzt bekommt jeder Typ einen PATCH :id Endpunkt (UpdateXDto per PartialType(PickType(...)) nach demselben Muster wie UpdateColonyDto/UpdateApiaryDto, mit Audit-Log-Eintrag inkl. Vorher-/Nachher-Stand) sowie eine Bearbeiten-UI: - Aufgaben: Dialog auf der Aufgabenliste - Durchsichten/Behandlungen/Fütterungen/Varroa-Messungen: Dialog pro Listeneintrag auf der Volk-Detailseite - Ernten: Dialog auf der eigenen Ernte-Detailseite - Schleuderungen: Dialog direkt in der Rückverfolgbarkeits-Karte der Honigcharge (keine eigene Detailseite nötig); Erntezeilen dort verlinken jetzt zur Ernte-Detailseite statt sie zu duplizieren - Abfüllungen: Dialog auf der Abfüllungs-Detailseite, bewusst nur Datum/MHD/Losnummer editierbar – Glasgröße/-anzahl bleiben unveränderlich, weil sie bereits unumkehrbar die Honigcharge-Menge reduziert haben Relationsstiftende Fremdschlüssel (colonyId, apiaryId/colonyIds, harvestIds, honeyBatchId) bleiben überall bewusst nicht editierbar, konsistent mit dem bestehenden apiaryId-Ausschluss bei UpdateColonyDto.
89 lines
2.4 KiB
TypeScript
89 lines
2.4 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { AuditLogService } from '../audit/audit-log.service';
|
|
import { CreateExtractionDto } from './dto/create-extraction.dto';
|
|
import { UpdateExtractionDto } from './dto/update-extraction.dto';
|
|
|
|
const extractionInclude = {
|
|
harvests: {
|
|
include: {
|
|
harvest: { include: { colonies: { include: { colony: true } } } },
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
@Injectable()
|
|
export class ExtractionsService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly auditLog: AuditLogService,
|
|
) {}
|
|
|
|
async findAll() {
|
|
return this.prisma.extraction.findMany({
|
|
include: extractionInclude,
|
|
orderBy: { date: 'desc' },
|
|
});
|
|
}
|
|
|
|
async findById(id: string) {
|
|
const extraction = await this.prisma.extraction.findUnique({
|
|
where: { id },
|
|
include: extractionInclude,
|
|
});
|
|
if (!extraction) {
|
|
throw new NotFoundException('Extraction not found');
|
|
}
|
|
return extraction;
|
|
}
|
|
|
|
async create(dto: CreateExtractionDto, actingUserId: string) {
|
|
const extraction = await this.prisma.$transaction(async (tx) => {
|
|
const created = await tx.extraction.create({
|
|
data: {
|
|
date: new Date(dto.date),
|
|
amount: dto.amount,
|
|
room: dto.room,
|
|
waterContent: dto.waterContent,
|
|
temperature: dto.temperature,
|
|
notes: dto.notes,
|
|
userId: actingUserId,
|
|
},
|
|
});
|
|
await tx.extractionHarvest.createMany({
|
|
data: dto.harvestIds.map((harvestId) => ({
|
|
extractionId: created.id,
|
|
harvestId,
|
|
})),
|
|
});
|
|
return created;
|
|
});
|
|
await this.auditLog.record({
|
|
entityType: 'Extraction',
|
|
entityId: extraction.id,
|
|
action: 'CREATE',
|
|
userId: actingUserId,
|
|
afterData: extraction,
|
|
});
|
|
return this.findById(extraction.id);
|
|
}
|
|
|
|
async update(id: string, dto: UpdateExtractionDto, actingUserId: string) {
|
|
const before = await this.findById(id);
|
|
await this.prisma.extraction.update({
|
|
where: { id },
|
|
data: { ...dto, date: dto.date ? new Date(dto.date) : undefined },
|
|
});
|
|
const extraction = await this.findById(id);
|
|
await this.auditLog.record({
|
|
entityType: 'Extraction',
|
|
entityId: id,
|
|
action: 'UPDATE',
|
|
userId: actingUserId,
|
|
beforeData: before,
|
|
afterData: extraction,
|
|
});
|
|
return extraction;
|
|
}
|
|
}
|