From 2e6846285deadd9c1cc27fd71e7baee9a55d19ae Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Wed, 9 Sep 2026 10:50:46 +0200 Subject: [PATCH] fix: include Wirkstoff, Charge, and Wartezeit in the annual treatment report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The colony annual PDF report's treatment section only ever printed date/product/amount - activeIngredient, batchNumber, and withdrawalPeriodDays (added earlier this session specifically for Wartezeit compliance) existed on the Treatment record but never made it into the one document a beekeeper would actually hand to a Veterinäramt inspector or keep as their Bestandsbuch-style compliance record. Silently missing the withdrawal period from the printed record undermines the whole point of tracking it. Also adds the first tests for annual-reports.ts (previously untested). Co-Authored-By: Claude Sonnet 5 --- .../src/export/annual-reports.spec.ts | 74 +++++++++++++++++++ ApiCore-Backend/src/export/annual-reports.ts | 21 +++++- 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 ApiCore-Backend/src/export/annual-reports.spec.ts diff --git a/ApiCore-Backend/src/export/annual-reports.spec.ts b/ApiCore-Backend/src/export/annual-reports.spec.ts new file mode 100644 index 0000000..29c9da8 --- /dev/null +++ b/ApiCore-Backend/src/export/annual-reports.spec.ts @@ -0,0 +1,74 @@ +import { NotFoundException } from '@nestjs/common'; +import { buildColonyAnnualReport } from './annual-reports'; +import { PrismaService } from '../prisma/prisma.service'; + +describe('buildColonyAnnualReport', () => { + let prisma: { + colony: { findUnique: jest.Mock }; + inspection: { findMany: jest.Mock }; + treatment: { findMany: jest.Mock }; + varroaMeasurement: { findMany: jest.Mock }; + swarmControlCheck: { findMany: jest.Mock }; + harvestColony: { findMany: jest.Mock }; + }; + + const colony = { + id: 'colony-1', + code: 'V-001', + colonyType: 'PRODUCTION_COLONY', + status: 'ACTIVE', + apiary: { id: 'apiary-1', name: 'Heimstand' }, + }; + + beforeEach(() => { + prisma = { + colony: { findUnique: jest.fn().mockResolvedValue(colony) }, + inspection: { findMany: jest.fn().mockResolvedValue([]) }, + treatment: { findMany: jest.fn().mockResolvedValue([]) }, + varroaMeasurement: { findMany: jest.fn().mockResolvedValue([]) }, + swarmControlCheck: { findMany: jest.fn().mockResolvedValue([]) }, + harvestColony: { findMany: jest.fn().mockResolvedValue([]) }, + }; + }); + + it('throws NotFoundException for an unknown colony', async () => { + prisma.colony.findUnique.mockResolvedValue(null); + await expect( + buildColonyAnnualReport( + prisma as unknown as PrismaService, + 'missing', + 2026, + ), + ).rejects.toBeInstanceOf(NotFoundException); + }); + + it('carries activeIngredient, batchNumber and withdrawalPeriodDays through for each treatment', async () => { + prisma.treatment.findMany.mockResolvedValue([ + { + date: new Date('2026-05-01'), + product: 'Ameisensäure 60%', + amount: '15 ml', + activeIngredient: 'Ameisensäure', + batchNumber: 'CH-2026-01', + withdrawalPeriodDays: 0, + }, + ]); + + const report = await buildColonyAnnualReport( + prisma as unknown as PrismaService, + 'colony-1', + 2026, + ); + + expect(report.treatments).toEqual([ + { + date: new Date('2026-05-01'), + product: 'Ameisensäure 60%', + amount: '15 ml', + activeIngredient: 'Ameisensäure', + batchNumber: 'CH-2026-01', + withdrawalPeriodDays: 0, + }, + ]); + }); +}); diff --git a/ApiCore-Backend/src/export/annual-reports.ts b/ApiCore-Backend/src/export/annual-reports.ts index 7a27384..a4141b4 100644 --- a/ApiCore-Backend/src/export/annual-reports.ts +++ b/ApiCore-Backend/src/export/annual-reports.ts @@ -37,7 +37,14 @@ export interface ColonyAnnualReport { queenSeen: boolean | null; broodAmount: string | null; }[]; - treatments: { date: Date; product: string; amount: string | null }[]; + treatments: { + date: Date; + product: string; + amount: string | null; + activeIngredient: string | null; + batchNumber: string | null; + withdrawalPeriodDays: number | null; + }[]; varroaMeasurements: { date: Date; method: string; @@ -141,6 +148,9 @@ export async function buildColonyAnnualReport( date: entry.date, product: entry.product, amount: entry.amount, + activeIngredient: entry.activeIngredient, + batchNumber: entry.batchNumber, + withdrawalPeriodDays: entry.withdrawalPeriodDays, })), varroaMeasurements: varroaMeasurements.map((entry) => ({ date: entry.date, @@ -262,7 +272,14 @@ export function renderColonyAnnualReportPdf( } for (const entry of report.treatments) { doc.text( - `- ${formatDate(entry.date)}: ${entry.product} (${entry.amount ?? '-'})`, + `- ${formatDate(entry.date)}: ${entry.product} (${entry.amount ?? '-'})` + + (entry.activeIngredient + ? `, Wirkstoff: ${entry.activeIngredient}` + : '') + + (entry.batchNumber ? `, Charge: ${entry.batchNumber}` : '') + + (entry.withdrawalPeriodDays !== null + ? `, Wartezeit: ${entry.withdrawalPeriodDays} Tage` + : ''), ); } doc.moveDown();