feat(export): Stockkarte - a colony's complete record as PDF

New report colony-record-card (button "Stockkarte (PDF)" on the colony
page): master data, current queen and queen history, every inspection as
a table row (strength, eggs, open/capped brood, queen seen, queen cells,
gentleness, swarm drive, feed, measures, notes), treatments with batch
number and withdrawal period, feedings, varroa counts and harvests.
Landscape A4, table header repeated on each page. Whole history by
default, one calendar year with ?year=.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 06:27:55 +02:00
1 parent 5cf4e87ca3
commit f263ec79f2
5 files changed
+586 -1

No files matched your search

+27
View File
@@ -157,6 +157,33 @@ describe('Export (e2e)', () => {
);
});
it('renders the record card (Stockkarte) for the whole history and for one year', async () => {
for (const query of [
`colonyId=${colonyId}`,
`colonyId=${colonyId}&year=2026`,
]) {
const response = await request(app.getHttpServer())
.get(`/api/v1/export/colony-record-card.pdf?${query}`)
.set('Authorization', `Bearer ${beekeeperToken}`)
.expect(200);
expect(response.headers['content-type']).toContain('application/pdf');
expect((response.body as Buffer).subarray(0, 4).toString('latin1')).toBe(
'%PDF',
);
}
});
it('rejects the record card without a colonyId and for an unknown colony', async () => {
await request(app.getHttpServer())
.get('/api/v1/export/colony-record-card.pdf')
.set('Authorization', `Bearer ${beekeeperToken}`)
.expect(400);
await request(app.getHttpServer())
.get('/api/v1/export/colony-record-card.pdf?colonyId=does-not-exist')
.set('Authorization', `Bearer ${beekeeperToken}`)
.expect(404);
});
it('rejects the apiary annual report without an apiaryId', () => {
return request(app.getHttpServer())
.get('/api/v1/export/apiary-annual-report.pdf')