import { BadRequestException, Injectable, NotFoundException, } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { AuditLogService } from '../audit/audit-log.service'; import { WebhooksService } from '../webhooks/webhooks.service'; import { AutomationService } from '../automation/automation.service'; import { HoneyBatchStatus } from '../../generated/prisma'; import { CreateHoneyBatchDto } from './dto/create-honey-batch.dto'; import { UpdateHoneyBatchDto } from './dto/update-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: { include: { harvest: { include: { colonies: { include: { colony: true } } } }, }, }, }, }, } as const; export interface HoneyBatchFilter { status?: HoneyBatchStatus; } @Injectable() export class HoneyBatchesService { constructor( private readonly prisma: PrismaService, private readonly auditLog: AuditLogService, private readonly webhooks: WebhooksService, private readonly automation: AutomationService, ) {} async findAll(filter: HoneyBatchFilter = {}) { return this.prisma.honeyBatch.findMany({ where: { status: filter.status }, include: honeyBatchInclude, orderBy: { createdAt: 'desc' }, }); } async findById(id: string) { const honeyBatch = await this.prisma.honeyBatch.findUnique({ where: { id }, include: honeyBatchInclude, }); if (!honeyBatch) { throw new NotFoundException('Honey batch not found'); } return honeyBatch; } async create(dto: CreateHoneyBatchDto, actingUserId: string) { const batchNumber = dto.batchNumber ?? (await this.automation.generateNumber('HONEY_BATCH_NUMBER_FORMAT')); if (!batchNumber) { throw new BadRequestException( 'Chargennummer erforderlich (oder Automatisierung "Chargennummer-Format" aktivieren)', ); } const honeyBatch = await this.prisma.honeyBatch.create({ data: { ...dto, batchNumber }, }); await this.auditLog.record({ entityType: 'HoneyBatch', entityId: honeyBatch.id, action: 'CREATE', userId: actingUserId, afterData: honeyBatch, }); const created = await this.findById(honeyBatch.id); await this.webhooks.dispatch('batch.created', created); return created; } async update(id: string, dto: UpdateHoneyBatchDto, actingUserId: string) { const before = await this.findById(id); await this.prisma.honeyBatch.update({ where: { id }, data: { ...dto }, }); const updated = await this.findById(id); await this.auditLog.record({ entityType: 'HoneyBatch', entityId: id, action: 'UPDATE', userId: actingUserId, beforeData: before, afterData: updated, }); return updated; } async addMeasurement( id: string, dto: CreateHoneyMeasurementDto, actingUserId: string, ) { await this.findById(id); const measurement = await this.prisma.honeyMeasurement.create({ data: { ...dto, measuredAt: new Date(dto.measuredAt), honeyBatchId: id, userId: actingUserId, }, }); await this.auditLog.record({ entityType: 'HoneyMeasurement', entityId: measurement.id, action: 'CREATE', userId: actingUserId, afterData: measurement, }); 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 { honeyBatch: { id: honeyBatch.id, batchNumber: honeyBatch.batchNumber, honeyType: honeyBatch.honeyType, amount: honeyBatch.amount, status: honeyBatch.status, }, extraction: { id: honeyBatch.extraction.id, date: honeyBatch.extraction.date, room: honeyBatch.extraction.room, }, harvests: honeyBatch.extraction.harvests.map((entry) => ({ id: entry.harvest.id, date: entry.harvest.date, apiaryId: entry.harvest.apiaryId, colonies: entry.harvest.colonies.map((hc) => ({ id: hc.colony.id, code: hc.colony.code, })), })), }; } }