feat: T6 wave 4 – notice PDFs, compliance notifications, readiness core
- Authority notice PDFs: pure model plus renderer, TSK count in A4 landscape, FEHLT for missing particulars, stored totals with correction note; GET /authority-notices/:id/pdf with compliance:read. - Compliance notifications in category COMPLIANCE: herd change and TSK reference day, at most one unread per topic and user (advisory lock); daily rule TskReferenceDayReached with Berlin day and catch-up; handlers in API and worker. - Readiness core: live evaluation of seven pure rules, fingerprinted acknowledgements that reopen when revoked or stale, subject masking, read-only GET paths; READINESS_RULES injectable; kept out of the worker. - e2e hygiene: no unscoped deletes, seeded settings rows saved/restored. - ARCHITECTURE.md §12.8–12.10, §10 handler rows, Annahmen 78–85. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
edc193f128
commit
b912d29166
54 files changed
+5300
-14
No files matched your search
@@ -9,6 +9,13 @@ export const PDF_MARGIN = 36;
|
||||
export interface PdfColumn {
|
||||
header: string;
|
||||
width: number;
|
||||
/** Numbers read better right-aligned; the default is left. */
|
||||
align?: 'left' | 'right';
|
||||
}
|
||||
|
||||
export interface DrawTableOptions {
|
||||
/** Draws the last row in bold, e.g. a totals row. */
|
||||
boldLastRow?: boolean;
|
||||
}
|
||||
|
||||
export function formatDay(date: Date): string {
|
||||
@@ -55,11 +62,12 @@ export function drawTable(
|
||||
doc: PDFKit.PDFDocument,
|
||||
columns: PdfColumn[],
|
||||
rows: string[][],
|
||||
options: DrawTableOptions = {},
|
||||
): void {
|
||||
const left = PDF_MARGIN;
|
||||
const bottomLimit = doc.page.height - PDF_MARGIN - 20;
|
||||
|
||||
const drawRow = (cells: string[], bold: boolean) => {
|
||||
const drawRow = (cells: string[], isHeader: boolean, bold = isHeader) => {
|
||||
doc.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(8);
|
||||
const heights = cells.map((cell, index) =>
|
||||
doc.heightOfString(cell, { width: columns[index].width - 4 }),
|
||||
@@ -67,18 +75,21 @@ export function drawTable(
|
||||
const rowHeight = Math.max(...heights, 10) + 4;
|
||||
if (doc.y + rowHeight > bottomLimit) {
|
||||
doc.addPage();
|
||||
if (!bold) {
|
||||
if (!isHeader) {
|
||||
drawRow(
|
||||
columns.map((c) => c.header),
|
||||
true,
|
||||
);
|
||||
doc.font('Helvetica').fontSize(8);
|
||||
doc.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(8);
|
||||
}
|
||||
}
|
||||
const top = doc.y;
|
||||
let x = left;
|
||||
cells.forEach((cell, index) => {
|
||||
doc.text(cell, x + 2, top + 2, { width: columns[index].width - 4 });
|
||||
doc.text(cell, x + 2, top + 2, {
|
||||
width: columns[index].width - 4,
|
||||
align: columns[index].align ?? 'left',
|
||||
});
|
||||
x += columns[index].width;
|
||||
});
|
||||
doc
|
||||
@@ -102,6 +113,12 @@ export function drawTable(
|
||||
doc.moveDown(0.5);
|
||||
return;
|
||||
}
|
||||
for (const row of rows) drawRow(row, false);
|
||||
rows.forEach((row, index) =>
|
||||
drawRow(
|
||||
row,
|
||||
false,
|
||||
options.boldLastRow === true && index === rows.length - 1,
|
||||
),
|
||||
);
|
||||
doc.moveDown(0.5);
|
||||
}
|
||||
Reference in new issue
Block a user