// Plant service — DB work + AuditEvent for plantings. Called by /api/v1 routes // and (later) the MCP server, so the logic lives once. import { db } from "@/lib/db"; import { ApiError, type AuthContext } from "@/lib/api/authenticate"; import type { PlantLogInput } from "@/lib/api/schemas"; export function listPlants(_ctx: AuthContext, opts?: { q?: string }) { return db.plant.findMany({ where: { active: true, ...(opts?.q ? { commonName: { contains: opts.q, mode: "insensitive" } } : {}), }, orderBy: { commonName: "asc" }, include: { location: { select: { id: true, name: true } }, plantType: { select: { id: true, commonName: true } }, }, }); } export async function getPlant(_ctx: AuthContext, id: string) { const plant = await db.plant.findUnique({ where: { id }, include: { location: { select: { id: true, name: true } }, plantType: { select: { id: true, commonName: true } }, logs: { orderBy: { date: "desc" }, take: 50 }, }, }); if (!plant || !plant.active) throw new ApiError(404, "Plant not found"); return plant; } export async function addPlantLog(ctx: AuthContext, plantId: string, input: PlantLogInput) { const plant = await db.plant.findUnique({ where: { id: plantId }, select: { active: true } }); if (!plant || !plant.active) throw new ApiError(404, "Plant not found"); const log = await db.plantLog.create({ data: { plantId, type: input.type, date: input.date ? new Date(input.date) : new Date(), notes: input.notes?.trim() || null, quantity: input.quantity?.trim() || null, actorId: ctx.userId, }, }); await db.auditEvent.create({ data: { action: "plant.log.add", entityId: plantId, actorId: ctx.userId, payload: { type: input.type, via: ctx.via }, }, }); return log; }