feat: Etikettendruck prueft auch gedrehte Anordnung fuer bessere Blattausnutzung
Backend Image / build-and-push (push) Successful in 2m25s
Frontend Image / build-and-push (push) Successful in 2m5s

Im automatischen Modus (Spalten/Zeilen leer) wird jetzt zusaetzlich
berechnet, wie viele Etiketten pro Blatt passen, wenn man sie um 90
Grad dreht (Breite/Hoehe vertauscht). Passt die gedrehte Anordnung mehr
drauf, wird sie verwendet - die Etiketten selbst bleiben inhaltlich
unveraendert (nur auf dem Blatt gedreht), jedes einzelne Etikett bleibt
beim Abziehen normal lesbar.

Bei explizit gesetzten Spalten/Zeilen (z.B. fuer ein vorgedrucktes
Avery-Raster) greift die Drehung nicht - ein reales Blatt hat feste,
nicht drehbare Felder.

Rotation erfolgt via pdfkit-Rotation um die Mitte des Feldes: ein
Etikett, exakt 90 Grad um seinen eigenen Mittelpunkt gedreht, ergibt
immer eine vertauschte Bounding-Box an derselben Stelle - unabhaengig
von der Drehrichtungskonvention. 2 neue Tests belegen ueber die
Seitenzahl, dass die Drehung greift, wenn sie mehr Etiketten
unterbringt, und dass ein explizites Raster unangetastet bleibt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-03 08:14:51 +02:00
1 parent 1e53758753
commit 390ec32f50
2 files changed
+169 -21

No files matched your search

@@ -343,6 +343,54 @@ describe('LabelTemplatesService', () => {
expect(countPages(pdf)).toBe(2);
});
it('rotates labels 90 degrees when that fits more per sheet than the upright orientation', async () => {
// widthMm:90, heightMm:35, marginTopMm/Left:5, gapX/Y:2 (the default
// fixture): upright fits 2 columns x 7 rows = 14 per page, but rotated
// (35mm-wide x 90mm-tall slots) fits 5 columns x 3 rows = 15 per page.
prisma.labelTemplate.findUnique.mockResolvedValue(template);
prisma.bottlingBatch.findUnique.mockResolvedValue({
id: 'batch-1',
jarSize: 500,
jarCount: 15,
lotNumber: 'L-001',
date: new Date('2026-06-01T00:00:00.000Z'),
bestBeforeDate: new Date('2028-06-01T00:00:00.000Z'),
honeyBatch: { batchNumber: 'HB-001', honeyType: 'Bluetenhonig' },
product: { name: 'Glas 500g' },
});
const pdf = await service.renderLabelsPdf('template-1', 'batch-1');
// Only fits on a single page if the rotated 15-per-page layout was
// actually used - upright would need a second page for the 15th copy.
expect(countPages(pdf)).toBe(1);
});
it('does not rotate when columns/rows are explicitly set, even if rotating would fit more', async () => {
prisma.labelTemplate.findUnique.mockResolvedValue({
...template,
columns: 2,
rows: 7,
});
prisma.bottlingBatch.findUnique.mockResolvedValue({
id: 'batch-1',
jarSize: 500,
jarCount: 15,
lotNumber: 'L-001',
date: new Date('2026-06-01T00:00:00.000Z'),
bestBeforeDate: new Date('2028-06-01T00:00:00.000Z'),
honeyBatch: { batchNumber: 'HB-001', honeyType: 'Bluetenhonig' },
product: { name: 'Glas 500g' },
});
const pdf = await service.renderLabelsPdf('template-1', 'batch-1');
// An explicit grid describes a real pre-printed sheet - it must be
// honoured as-is (14 per page), needing 2 pages for 15 copies, even
// though the auto-fit rotation logic would otherwise fit all 15 on one.
expect(countPages(pdf)).toBe(2);
});
it('skips already-used slots on the first sheet via startPosition, but not later sheets', async () => {
prisma.labelTemplate.findUnique.mockResolvedValue({
...template,