CalendarModule mit GET /calendar?from=&to=&apiaryId= aggregiert Durchsichten, Aufgaben, Behandlungen, Fuetterungen, Ernten und Koeniginnen-Zuweisungen zu einem einheitlichen Ereignisfeed. Rein lesende Aggregation, kein neues Datenmodell.
126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
|
|
export type CalendarEventType =
|
|
| 'TASK'
|
|
| 'INSPECTION'
|
|
| 'TREATMENT'
|
|
| 'FEEDING'
|
|
| 'HARVEST'
|
|
| 'QUEEN_ASSIGNMENT';
|
|
|
|
export interface CalendarEvent {
|
|
id: string;
|
|
type: CalendarEventType;
|
|
date: Date;
|
|
title: string;
|
|
relatedType: string;
|
|
relatedId: string;
|
|
}
|
|
|
|
export interface CalendarQuery {
|
|
from: Date;
|
|
to: Date;
|
|
apiaryId?: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class CalendarService {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
async getEvents(query: CalendarQuery): Promise<CalendarEvent[]> {
|
|
const { from, to, apiaryId } = query;
|
|
const colonyFilter = apiaryId ? { colony: { apiaryId } } : {};
|
|
|
|
const [
|
|
tasks,
|
|
inspections,
|
|
treatments,
|
|
feedings,
|
|
harvests,
|
|
queenAssignments,
|
|
] = await Promise.all([
|
|
this.prisma.task.findMany({
|
|
where: { dueDate: { gte: from, lte: to } },
|
|
}),
|
|
this.prisma.inspection.findMany({
|
|
where: { date: { gte: from, lte: to }, ...colonyFilter },
|
|
include: { colony: true },
|
|
}),
|
|
this.prisma.treatment.findMany({
|
|
where: { date: { gte: from, lte: to }, ...colonyFilter },
|
|
include: { colony: true },
|
|
}),
|
|
this.prisma.feeding.findMany({
|
|
where: { date: { gte: from, lte: to }, ...colonyFilter },
|
|
include: { colony: true },
|
|
}),
|
|
this.prisma.harvest.findMany({
|
|
where: {
|
|
date: { gte: from, lte: to },
|
|
...(apiaryId ? { apiaryId } : {}),
|
|
},
|
|
}),
|
|
this.prisma.queenColonyHistory.findMany({
|
|
where: { startDate: { gte: from, lte: to }, ...colonyFilter },
|
|
include: { colony: true, queen: true },
|
|
}),
|
|
]);
|
|
|
|
const events: CalendarEvent[] = [
|
|
...tasks
|
|
.filter((task) => task.dueDate !== null)
|
|
.map((task) => ({
|
|
id: task.id,
|
|
type: 'TASK' as const,
|
|
date: task.dueDate!,
|
|
title: task.title,
|
|
relatedType: 'Task',
|
|
relatedId: task.id,
|
|
})),
|
|
...inspections.map((inspection) => ({
|
|
id: inspection.id,
|
|
type: 'INSPECTION' as const,
|
|
date: inspection.date,
|
|
title: `Durchsicht ${inspection.colony.code}`,
|
|
relatedType: 'Colony',
|
|
relatedId: inspection.colonyId,
|
|
})),
|
|
...treatments.map((treatment) => ({
|
|
id: treatment.id,
|
|
type: 'TREATMENT' as const,
|
|
date: treatment.date,
|
|
title: `Behandlung ${treatment.colony.code} · ${treatment.product}`,
|
|
relatedType: 'Colony',
|
|
relatedId: treatment.colonyId,
|
|
})),
|
|
...feedings.map((feeding) => ({
|
|
id: feeding.id,
|
|
type: 'FEEDING' as const,
|
|
date: feeding.date,
|
|
title: `Fütterung ${feeding.colony.code}`,
|
|
relatedType: 'Colony',
|
|
relatedId: feeding.colonyId,
|
|
})),
|
|
...harvests.map((harvest) => ({
|
|
id: harvest.id,
|
|
type: 'HARVEST' as const,
|
|
date: harvest.date,
|
|
title: 'Ernte',
|
|
relatedType: 'Apiary',
|
|
relatedId: harvest.apiaryId,
|
|
})),
|
|
...queenAssignments.map((assignment) => ({
|
|
id: assignment.id,
|
|
type: 'QUEEN_ASSIGNMENT' as const,
|
|
date: assignment.startDate,
|
|
title: `Königin ${assignment.queen.code} → ${assignment.colony.code}`,
|
|
relatedType: 'Queen',
|
|
relatedId: assignment.queenId,
|
|
})),
|
|
];
|
|
|
|
return events.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
}
|
|
}
|