fix: Etiketten werden beim Drucken abgeschnitten, wenn Spalten/Zeilen zu gross gewaehlt sind

Eine explizit im Etiketten-Designer eingetragene Spalten-/Zeilenzahl
wurde beim PDF-Rendern ungeprueft uebernommen. Passt sie nicht auf ein
A4-Blatt (z.B. 90mm breite Etiketten x 4 Spalten bei 5mm Rand), ragen
die hinteren Spalten/Zeilen ueber den Blattrand hinaus - der Drucker
schneidet sie dann einfach ab, statt sie zu drucken.

- Backend (label-templates.service.ts): Spalten/Zeilen werden jetzt auf
  das tatsaechlich auf A4 passende Maximum begrenzt, bevor gerendert
  wird - betrifft auch bereits gespeicherte Vorlagen mit zu grossen
  Werten.
- Frontend (label-designer.tsx): zeigt die maximal passende Spalten-/
  Zeilenzahl direkt im Seiten-Layout-Panel an und warnt inline, wenn
  der eingetragene Wert das Blatt sprengen wuerde.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-02 14:53:15 +02:00
1 parent 19d5e5d65c
commit 8b8616ce03
3 files changed
+74 -12

No files matched your search

@@ -247,6 +247,33 @@ describe('LabelTemplatesService', () => {
expect(countPages(pdf)).toBe(3);
});
it('clamps an explicit columns/rows override that would run labels off the A4 page', async () => {
// widthMm:90, marginLeftMm:5, gapXMm:2 only fit 2 columns on A4 - an
// explicit 99 would place columns 3+ past the page's right edge,
// where a printer just clips them instead of printing anything.
prisma.labelTemplate.findUnique.mockResolvedValue({
...template,
columns: 99,
rows: 1,
});
prisma.bottlingBatch.findUnique.mockResolvedValue({
id: 'batch-1',
jarSize: 500,
jarCount: 3,
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');
// Clamped to the real max of 2 columns x 1 row = 2 per page, so 3
// copies still need 2 pages - same as the explicit columns:2 test.
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,
@@ -237,15 +237,21 @@ export class LabelTemplatesService {
layout;
const usableWidthMm = A4_WIDTH_MM - 2 * marginLeftMm;
const usableHeightMm = A4_HEIGHT_MM - 2 * marginTopMm;
const columns =
layout.columns ??
Math.max(1, Math.floor((usableWidthMm + gapXMm) / (widthMm + gapXMm)));
const rows =
layout.rows ??
Math.max(
1,
Math.floor((usableHeightMm + gapYMm) / (heightMm + gapYMm)),
);
const maxColumns = Math.max(
1,
Math.floor((usableWidthMm + gapXMm) / (widthMm + gapXMm)),
);
const maxRows = Math.max(
1,
Math.floor((usableHeightMm + gapYMm) / (heightMm + gapYMm)),
);
// An explicit columns/rows value from the sheet-layout designer is
// clamped to what actually fits the A4 page - without this, a value
// typed in by the user (or already saved from before this fix) would
// place labels past the page's right/bottom edge, where printers just
// clip them instead of printing anything.
const columns = Math.min(layout.columns ?? maxColumns, maxColumns);
const rows = Math.min(layout.rows ?? maxRows, maxRows);
const perPage = columns * rows;
// startPosition only skips ahead on the first sheet (partially-used
// physical sheet, e.g. a few labels already peeled off) - every sheet