From 30f39af361d470a470722dd3d8c4dfd6c7b2b3dd Mon Sep 17 00:00:00 2001 From: s4luorth Date: Wed, 30 Sep 2026 11:50:37 +0200 Subject: [PATCH] C4-Drucktemplate anlegen + Textmengencheck ohne Limit - format_templates: kuvert_c4_v1 (+ A5/DIN-lang) werden per Nachruest-Schritt auch auf bestehenden Installationen angelegt (nur wenn Key fehlt); neue Templates tragen default_format (auto-Zuordnung im manuellen Druck). - orders /preview: check:true umgeht Kennung + Kontingent (Textmengencheck), liefert aber die SVGs. Co-Authored-By: Claude Opus 4.8 --- bootstrap/bootstrap.mjs | 17 +++++++++++++---- .../dist/index.js | 12 +++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/bootstrap/bootstrap.mjs b/bootstrap/bootstrap.mjs index 7f4f32e..f3b7df7 100644 --- a/bootstrap/bootstrap.mjs +++ b/bootstrap/bootstrap.mjs @@ -996,9 +996,9 @@ async function applySeed() { // Upsert gesetzt, damit falsche Altwerte beim Bootstrap korrigiert werden. const formatTemplates = [ { key: 'a4_single', name: 'A4 Brief', type: 'brief', width: 8.27, height: 11.69, xpos: 0.0, ypos: -0.3, scale: 1.0, rotation: 0 }, - { key: 'a5_v1', name: 'A5 Hochformat', type: 'brief', width: 5.83, height: 8.27, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, - { key: 'a5l_v1', name: 'A5 Querformat', type: 'brief', width: 8.27, height: 5.83, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, - { key: 'dl_v1', name: 'DIN lang (komplett beschrieben)', type: 'brief', width: 8.27, height: 4.13, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, + { key: 'a5_v1', name: 'A5 Hochformat', type: 'brief', default_format: 'a5p', width: 5.83, height: 8.27, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, + { key: 'a5l_v1', name: 'A5 Querformat', type: 'brief', default_format: 'a5l', width: 8.27, height: 5.83, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, + { key: 'dl_v1', name: 'DIN lang (komplett beschrieben)', type: 'brief', default_format: 'dinlang_karte', width: 8.27, height: 4.13, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, { key: 'a6_v1', name: 'A6 Hochformat – Variante 1', type: 'brief', width: 4.1, height: 5.8, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, { key: 'a6_v2', name: 'A6 Hochformat – Variante 2', type: 'brief', width: 4.1, height: 5.8, xpos: 4.0, ypos: -0.3, scale: 1.0, rotation: 90 }, { key: 'a6l_v1', name: 'A6 Querformat – Variante 1', type: 'brief', width: 5.8, height: 4.1, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, @@ -1010,7 +1010,7 @@ async function applySeed() { { key: 'kuvert_b_v1', name: 'Kuvert C6 – Variante 1', type: 'umschlag', width: 4.49, height: 6.38, xpos: 4.2, ypos: -0.2, scale: 1.0, rotation: 90 }, { key: 'kuvert_b_v2', name: 'Kuvert C6 – Variante 2', type: 'umschlag', width: 4.33, height: 6.38, xpos: 4.0, ypos: -0.2, scale: 1.0, rotation: 90 }, // C4 quer (324x229 mm = 12.76x9.02 Zoll). Querformat, maschinenseitig quer eingelegt (rotation 0). - { key: 'kuvert_c4_v1', name: 'Kuvert C4 quer', type: 'umschlag', width: 12.76, height: 9.02, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, + { key: 'kuvert_c4_v1', name: 'Kuvert C4 quer', type: 'umschlag', default_format: 'c4', width: 12.76, height: 9.02, xpos: -0.3, ypos: -0.3, scale: 1.0, rotation: 0 }, ]; // Nur bei LEERER Collection anlegen (Erstinstallation). Der Betreiber pflegt die // Templates danach selbst (löschen/umbenennen/eigene) – Bootstrap fügt sie NICHT @@ -1023,6 +1023,15 @@ async function applySeed() { } else { log('format_templates vorhanden → Seed übersprungen (vom Betreiber gepflegt)'); } + // Nachzügler: neu hinzugekommene Formate (A5, DIN lang, C4) auch auf bestehenden + // Installationen anlegen – aber NUR, wenn der Key fehlt (bestehende unangetastet). + // So ist u. a. der C4-Kuvertdruck ohne Handarbeit möglich. + for (const key of ['a5_v1', 'a5l_v1', 'dl_v1', 'kuvert_c4_v1']) { + const t = formatTemplates.find((x) => x.key === key); + if (!t) continue; + const da = await api('GET', `/items/format_templates?filter[key][_eq]=${encodeURIComponent(key)}&limit=1&fields=id`); + if (!da || !da.length) { await api('POST', '/items/format_templates', t); log('seed + format_templates.' + key + ' (nachgerüstet)'); } + } // Rendering-Formate (zentral fürs Backend). Werte = bisherige Defaults. const renderFormats = [ diff --git a/extensions/directus-extension-skrift-orders/dist/index.js b/extensions/directus-extension-skrift-orders/dist/index.js index a038048..0d0f67a 100644 --- a/extensions/directus-extension-skrift-orders/dist/index.js +++ b/extensions/directus-extension-skrift-orders/dist/index.js @@ -696,8 +696,10 @@ const handler = (router, { services, getSchema, logger }) => { const nurPruefen = body.validate === true; // Angemeldete Admin/App-Nutzer (Produktions-Modul) umgehen Kennung + Kontingent. const istIntern = !!(req.accountability && (req.accountability.admin || req.accountability.app)); + // Textmengencheck (app.skrift.de/textmenge): unbegrenzt, ohne Kontingent-Verbrauch. + const istCheck = body.check === true; const kennung = norm(body.email) || String(body.client_id || '').slice(0, 64); - if (!kennung && !nurPruefen && !istIntern) return res.status(400).json({ error: 'E-Mail oder Kennung erforderlich.' }); + if (!kennung && !nurPruefen && !istIntern && !istCheck) return res.status(400).json({ error: 'E-Mail oder Kennung erforderlich.' }); // Ohne Brieftext nur erlaubt, wenn Kuverts gewünscht sind (dann nur Kuvert-Vorschau). const willKuvertVorschau = !!(body.envelope_labeling && body.envelope_labeling !== 'keine'); if ((!body.text || !String(body.text).trim()) && !willKuvertVorschau) @@ -708,11 +710,11 @@ const handler = (router, { services, getSchema, logger }) => { const svc = (c) => new ItemsService(c, { schema, accountability: null }); const heute = new Date().toISOString().slice(0, 10); - const vorhanden = (nurPruefen || istIntern) ? null : (await svc('preview_usage').readByQuery({ + const vorhanden = (nurPruefen || istIntern || istCheck) ? null : (await svc('preview_usage').readByQuery({ filter: { key: { _eq: kennung }, day: { _eq: heute } }, limit: 1, fields: ['id', 'count'], }))?.[0]; const verbraucht = vorhanden?.count ?? 0; - if (!nurPruefen && !istIntern && verbraucht >= PREVIEW_LIMIT) { + if (!nurPruefen && !istIntern && !istCheck && verbraucht >= PREVIEW_LIMIT) { return res.status(429).json({ error: `Vorschau-Kontingent für heute aufgebraucht (${PREVIEW_LIMIT}).`, remaining: 0, limit: PREVIEW_LIMIT, @@ -868,8 +870,8 @@ const handler = (router, { services, getSchema, logger }) => { } } - // Verbrauch erst nach Erfolg hochzählen – intern (Admin) zählt nicht. - if (!istIntern && !nurPruefen && kennung) { + // Verbrauch erst nach Erfolg hochzählen – intern (Admin) und Textmengencheck zählen nicht. + if (!istIntern && !nurPruefen && !istCheck && kennung) { if (vorhanden) await svc('preview_usage').updateOne(vorhanden.id, { count: verbraucht + 1 }); else await svc('preview_usage').createOne({ key: kennung, day: heute, count: 1 }); }