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.
86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { AuditLogService } from '../audit/audit-log.service';
|
|
import { Feeding } from '../../generated/prisma';
|
|
import { CreateFeedingDto } from './dto/create-feeding.dto';
|
|
import { UpdateFeedingDto } from './dto/update-feeding.dto';
|
|
|
|
export interface FeedingSummaryEntry {
|
|
unit: string;
|
|
totalAmount: number;
|
|
}
|
|
|
|
@Injectable()
|
|
export class FeedingsService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly auditLog: AuditLogService,
|
|
) {}
|
|
|
|
async findAll(): Promise<Feeding[]> {
|
|
return this.prisma.feeding.findMany({ orderBy: { date: 'desc' } });
|
|
}
|
|
|
|
async findById(id: string): Promise<Feeding> {
|
|
const feeding = await this.prisma.feeding.findUnique({ where: { id } });
|
|
if (!feeding) {
|
|
throw new NotFoundException('Feeding not found');
|
|
}
|
|
return feeding;
|
|
}
|
|
|
|
async findByColony(colonyId: string): Promise<Feeding[]> {
|
|
return this.prisma.feeding.findMany({
|
|
where: { colonyId },
|
|
orderBy: { date: 'asc' },
|
|
});
|
|
}
|
|
|
|
async summaryByColony(colonyId: string): Promise<FeedingSummaryEntry[]> {
|
|
const grouped = await this.prisma.feeding.groupBy({
|
|
by: ['unit'],
|
|
where: { colonyId },
|
|
_sum: { amount: true },
|
|
});
|
|
return grouped.map((entry) => ({
|
|
unit: entry.unit,
|
|
totalAmount: entry._sum.amount ?? 0,
|
|
}));
|
|
}
|
|
|
|
async create(dto: CreateFeedingDto, actingUserId: string): Promise<Feeding> {
|
|
const feeding = await this.prisma.feeding.create({
|
|
data: { ...dto, date: new Date(dto.date), userId: actingUserId },
|
|
});
|
|
await this.auditLog.record({
|
|
entityType: 'Feeding',
|
|
entityId: feeding.id,
|
|
action: 'CREATE',
|
|
userId: actingUserId,
|
|
afterData: feeding,
|
|
});
|
|
return feeding;
|
|
}
|
|
|
|
async update(
|
|
id: string,
|
|
dto: UpdateFeedingDto,
|
|
actingUserId: string,
|
|
): Promise<Feeding> {
|
|
const before = await this.findById(id);
|
|
const feeding = await this.prisma.feeding.update({
|
|
where: { id },
|
|
data: { ...dto, date: dto.date ? new Date(dto.date) : undefined },
|
|
});
|
|
await this.auditLog.record({
|
|
entityType: 'Feeding',
|
|
entityId: id,
|
|
action: 'UPDATE',
|
|
userId: actingUserId,
|
|
beforeData: before,
|
|
afterData: feeding,
|
|
});
|
|
return feeding;
|
|
}
|
|
}
|