Kuvert-Absender (Feld direkt sichtbar), Textmengen-/Session-Aenderungen

- neuauftrag: Absenderfeld fuers Kuvert IMMER sichtbar (nicht mehr an 'Kuverts
  generieren' gebunden), Payloads (Vorschau + Anlage) reichen envelope_sender durch.
- orders: manual-order speichert envelope_sender, Preview-Proxy reicht sender durch.
- bootstrap: Feld envelope_sender.
- (enthaelt weiterhin die Session-Aenderungen: signature-order, mehrseitige Vorschau,
  'In manuellen Druck uebernehmen', gesendet-Nummern, Generierungs-Fehleranzeige,
  seitenumbruch-Hinweis.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
s4luorthandClaude Opus 4.8 committed 2026-09-01 15:23:06 +02:00
1 parent 46adf2a2f5
commit 46b5155c27
4 files changed
+179 -33

No files matched your search

@@ -78,14 +78,14 @@ const NeuAuftrag = defineComponent({
// Mehrseitiges Schriftstück (Auto-Fluss über mehrere A4-Seiten).
mehrseitig: false, seitenInfo: '',
// Kuvert-Adresse je Empfänger: '5zeilen' (5 Zeilen) oder 'individuell' (einzeilig).
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
importOpen: false, importText: '',
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '', lbDoc: null,
});
// Kuverts nur generieren, wenn angehakt (Beschriftung aus der Empfängeradresse).
const kuvertPayload = () => (neu.kuverts
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '' }
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '' });
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '', envelope_sender: (neu.envelopeSender || '').trim() }
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '', envelope_sender: '' });
async function laden() {
try {
@@ -109,6 +109,35 @@ const NeuAuftrag = defineComponent({
try { verbotenRe.value = wl ? new RegExp(`[^${wl}\\n\\r\\t]`, 'g') : null; } catch { verbotenRe.value = null; }
neu.product = produkte.value[0]?.key || 'briefe';
neu.format = neuFormate()[0]?.key || '';
// Übernahme aus dem Produktions-Board (localStorage): Text/Format/Schrift
// und alle Empfänger vorbefüllen, danach den Schlüssel wieder entfernen.
try {
const raw = localStorage.getItem('skrift_neuauftrag_uebernahme');
if (raw) {
localStorage.removeItem('skrift_neuauftrag_uebernahme');
const d = JSON.parse(raw);
if (d && typeof d === 'object') {
if (d.product) neu.product = d.product;
if (d.format) neu.format = d.format;
if (d.font) neu.font = d.font;
if (typeof d.text === 'string') neu.text = d.text;
if (typeof d.multi_page === 'boolean') neu.mehrseitig = d.multi_page;
if (typeof d.realistic === 'boolean') neu.realistic = d.realistic;
if (d.fontSizeLetter) neu.fontSizeLetter = d.fontSizeLetter;
if (d.fontSizeEnvelope) neu.fontSizeEnvelope = d.fontSizeEnvelope;
if (typeof d.kuverts === 'boolean') neu.kuverts = d.kuverts;
if (d.adressModus) neu.adressModus = d.adressModus;
if (Array.isArray(d.rows) && d.rows.length) {
neu.rows = d.rows.map((r) => ({
free_text: String(r.free_text || ''),
placeholders: (r.placeholders && typeof r.placeholders === 'object') ? { ...r.placeholders } : {},
}));
}
neu.previewSession = null;
neu.msg = 'Aus Auftrag übernommen – anpassen, dann Vorschau/Anlegen.';
}
}
} catch { /* localStorage nicht verfügbar → ignorieren */ }
} catch (e) {
neu.err = e?.response?.data?.errors?.[0]?.message || e?.message || 'Laden fehlgeschlagen.';
} finally { geladen.value = true; }
@@ -237,7 +266,7 @@ const NeuAuftrag = defineComponent({
const kv = kuvertPayload();
const res = await api.post('/skrift-orders/preview', {
text: neu.text, font: neu.font, format: neu.format, realistic: neu.realistic,
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
multi_page: neu.mehrseitig,
...briefSigPayload(),
@@ -278,6 +307,33 @@ const NeuAuftrag = defineComponent({
neu.err = e?.response?.data?.error || e?.message || 'Seitenzählung fehlgeschlagen.';
}
}
// Das Backend generiert die Dokumente asynchron (fire-and-forget). Fehler
// landen als status_history-Eintrag 'fehler' am Auftrag, Erfolg als 'in_queue'.
// Wir pollen kurz und zeigen ein Problem DIREKT hier im Dashboard – statt dass
// es still verschluckt wird und „nie Dokumente" entstehen.
async function ueberwacheGenerierung(orderId, nr) {
for (let i = 0; i < 20; i += 1) {
await new Promise((r) => setTimeout(r, 1500));
let sh = [];
try {
sh = (await api.get('/items/status_history', { params: {
'filter[order][_eq]': orderId, sort: '-date_created', limit: 4, fields: 'status,note',
} })).data?.data || [];
} catch { continue; }
const fehler = sh.find((s) => s.status === 'fehler');
if (fehler) {
neu.err = `Generierung fehlgeschlagen: ${fehler.note || 'unbekannter Grund'}`;
neu.msg = '';
return;
}
if (sh.some((s) => s.status === 'in_queue')) {
neu.msg = `✓ Auftrag ${nr} angelegt & generiert – liegt im Produktions-Board unter „Neu". Felder bleiben stehen.`;
return;
}
}
// Kein eindeutiges Signal (langsame Generierung oder Datei-Produkt ohne Kuvert).
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden noch generiert. Status im Produktions-Board prüfen.`;
}
async function absenden() {
// Empfänger sind optional – ohne Empfänger genau ein Schriftstück (Blankozeile).
const ent = entries();
@@ -291,16 +347,19 @@ const NeuAuftrag = defineComponent({
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
multi_page: neu.mehrseitig,
...briefSigPayload(),
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
shipping_type: 'sammel',
customer_email: neu.customer_email.trim() || undefined,
text_template: neu.text, preview_session: neu.previewSession || undefined,
entries: payloadEntries,
});
const nr = res.data?.order_number || '';
const oid = res.data?.id;
// Felder bewusst NICHT zurücksetzen – der letzte Auftrag bleibt stehen,
// damit er sich als Vorlage für den nächsten leicht anpassen lässt.
neu.msg = `✓ Auftrag ${nr} angelegt – liegt im Produktions-Board unter „Neu". Felder bleiben stehen; mit „Formular leeren" zurücksetzen.`;
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden generiert …`;
// Generierung im Hintergrund überwachen; ein Backend-Fehler erscheint dann hier.
if (oid) ueberwacheGenerierung(oid, nr);
} catch (e) { neu.err = e?.response?.data?.error || e?.message || 'Anlegen fehlgeschlagen.'; neu.msg = ''; }
}
function zuruecksetzen() {
@@ -308,7 +367,7 @@ const NeuAuftrag = defineComponent({
product: produkte.value[0]?.key || 'briefe', format: neuFormate()[0]?.key || '', font: 'tilda',
fontSizeLetter: 100, fontSizeEnvelope: 100,
sigSvg: '', sigLabel: '', sigX: 0, sigY: 0, sigScale: 100, mehrseitig: false, seitenInfo: '',
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
importOpen: false, importText: '',
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '',
});
@@ -449,6 +508,17 @@ const NeuAuftrag = defineComponent({
h('input', { type: 'checkbox', checked: neu.kuverts, onChange: (e) => { neu.kuverts = e.target.checked; neu.previewSession = null; }, style: 'width:16px;height:16px;' }),
'Kuverts generieren',
]),
h('div', { style: 'margin-top:14px;' }, [
label('Absender aufs Kuvert (oben links, max. 5 Zeilen, optional)'),
h('textarea', {
value: neu.envelopeSender, rows: 5,
placeholder: 'Max Mustermann\nMusterstraße 1\n12345 Musterstadt',
style: WFELD + 'width:100%;font-family:var(--family-monospace,monospace);',
onInput: (e) => { neu.envelopeSender = saeubere(String(e.target.value)).split('\n').slice(0, 5).join('\n'); neu.previewSession = null; },
}),
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
'Ist ein Absender gesetzt, rutscht der Empfänger auf dem Kuvert automatisch nach unten rechts. Wirkt nur, wenn „Kuverts generieren" aktiv ist.'),
]),
neu.importOpen ? h('div', { style: 'margin-top:12px;' }, [
subdued('Aus Excel kopieren und einfügen – eine Zeile pro Empfänger, Spalten mit Tab/Semikolon/Komma. '
+ 'Reihenfolge: erst individuelle Platzhalter, dann ' + (neu.adressModus === 'individuell' ? 'die eine Adresszeile.' : 'Zeile 1–5.'), 'margin-bottom:6px;'),
@@ -478,6 +548,8 @@ const NeuAuftrag = defineComponent({
neu.previewSession = null;
requestAnimationFrame(() => { try { ta.selectionStart = ta.selectionEnd = s + 1; } catch {} });
} }),
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
'Tipp: [[seitenumbruch]] erzwingt an dieser Stelle eine neue Seite (mehrseitiger Brief). [[signatur1]] setzt die Unterschrift.'),
// ── Unterschrift auf den Brief ─────────────────────────────────────
h('div', { style: 'margin-top:16px;border-top:1px solid rgba(30,80,165,.15);padding-top:14px;' }, [
label('Unterschrift auf den Brief (optional) · Platzhalter [[signatur1]]'),