Punkt 3+4: Empfaenger-Fallbacks, "nur Kuvert"-Kuvertieren-Feld

Punkt 3:
- Board: kundeName faellt bei API-Auftraegen auf api_client.name/company zurueck (kein "-" mehr)
- Uebersicht: mehrseitige Briefe ("Seite 1/2/3") werden nicht als Empfaenger angezeigt
- empfKurz/entryName: totes company-Feld entfernt (gibt es auf order_entries nicht)

Punkt 4: neues Feld orders.kuvertieren (bool, default true) = "Blatt ins Kuvert legen".
- Board "Weiteres": getrennte Schalter "Kuvert schreiben" (needs_envelope) und
  "Blatt einlegen (kuvertieren)" (Blatt rein / Nur Kuvert)
- Ablauf-Schritt "Kuvertieren" nur noetig wenn Kuvert UND Blatt eingelegt wird
- kuvertieren in Order-Abfragen (Board + Uebersicht) ergaenzt

ACHTUNG Deploy: Bootstrap VOR Directus-Neustart laufen lassen, sonst 403 (neues Feld).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
s4luorth
2026-10-01 13:21:24 +02:00
parent a043a624d4
commit a1a2a615cc
3 changed files with 18 additions and 9 deletions

View File

@@ -292,6 +292,7 @@ const collections = [
{ field: 'production_status_changed_at', type: 'timestamp', meta: { interface: 'datetime', note: 'Zeitpunkt der letzten Produktionsstatus-Änderung (für „status_since" der Public-API).' }, schema: { is_nullable: true } },
dropdown('shipping_type', ['einzeln', 'sammel'], { default: 'sammel' }),
f('needs_envelope', 'boolean', { interface: 'boolean', default: false }),
f('kuvertieren', 'boolean', { interface: 'boolean', default: true, note: 'Blatt wird ins Kuvert gelegt (kuvertiert). Leer/false = nur Kuvert beschriften, kein Blatt einlegen.' }),
dropdown('envelope_labeling', ['empfaenger', 'freitext', 'keine']),
f('envelope_text', 'text', { interface: 'input-multiline', note: 'Individueller Kuverttext (bei envelope_labeling = freitext).' }),
f('envelope_sender', 'text', { interface: 'input-multiline', note: 'Absender fürs Kuvert (oben links, 5 Zeilen); manuelle Aufträge. Empfänger rutscht dann unten rechts.' }),

View File

@@ -195,7 +195,7 @@ function extras(e) {
}
function kundeName(o) {
const c = o.customer || {};
return [c.first_name, c.last_name].filter(Boolean).join(' ') || c.email || '—';
return [c.first_name, c.last_name].filter(Boolean).join(' ') || c.company || c.email || o.api_client?.name || '—';
}
const Board = defineComponent({
@@ -263,7 +263,7 @@ const Board = defineComponent({
// Basis-Felder ohne die evtl. noch nicht gebootstrapten Felder – diese
// werden separat versucht, damit ein fehlendes Feld nicht das ganze Board sperrt.
const ORDER_FELDER = 'id,order_number,order_name,status,payment_status,production_status,needs_envelope,envelope_format,'
+ 'envelope_labeling,shipping_type,shipping_day,keep_contacts,entries_count,count_inland,count_ausland,brief_gedruckt,kuvert_gedruckt,frankiert,versanddatum,'
+ 'envelope_labeling,shipping_type,shipping_day,keep_contacts,entries_count,count_inland,count_ausland,brief_gedruckt,kuvert_gedruckt,frankiert,kuvertieren,versanddatum,'
+ 'date_updated,date_created,format.key,format.name,product.name,product.type,product.input_mode,'
+ 'source,api_client.id,api_client.name,motif_mode,motif.name,shipping_address,'
+ 'source_file,internal_note,approval_status,approval_note,linked_order.id,linked_order.order_number,'
@@ -1392,7 +1392,8 @@ const Board = defineComponent({
h('div', { style: 'display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px;' }, [
h('div', {}, [feldLabel('Versandart'), segToggle(o.shipping_type === 'einzeln' ? 'einzeln' : 'sammel', [{ key: 'einzeln', label: 'Einzelversand' }, { key: 'sammel', label: 'Sammelversand' }], (k) => setOrderFeld(o, { shipping_type: k }))]),
h('div', {}, [feldLabel('Frankierung'), segToggle(((typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel')) ? 'ja' : 'nein', [{ key: 'ja', label: 'Frankieren' }, { key: 'nein', label: 'Nicht' }], (k) => setOrderFeld(o, { frankiert: k === 'ja' }))]),
h('div', {}, [feldLabel('Kuvertierung'), segToggle(o.needs_envelope ? 'ja' : 'nein', [{ key: 'ja', label: 'Mit Kuvert' }, { key: 'nein', label: 'Ohne' }], (k) => setOrderFeld(o, { needs_envelope: k === 'ja' }))]),
h('div', {}, [feldLabel('Kuvert schreiben'), segToggle(o.needs_envelope ? 'ja' : 'nein', [{ key: 'ja', label: 'Mit Kuvert' }, { key: 'nein', label: 'Ohne' }], (k) => setOrderFeld(o, { needs_envelope: k === 'ja' }))]),
h('div', {}, [feldLabel('Blatt einlegen (kuvertieren)'), segToggle((o.kuvertieren !== false) ? 'ja' : 'nein', [{ key: 'ja', label: 'Blatt rein' }, { key: 'nein', label: 'Nur Kuvert' }], (k) => setOrderFeld(o, { kuvertieren: k === 'ja' }))]),
h('div', {}, [feldLabel('Brief gedruckt'), segToggle(o.brief_gedruckt ? 'ja' : 'nein', [{ key: 'ja', label: 'Ja' }, { key: 'nein', label: 'Nein' }], (k) => setOrderFeld(o, { brief_gedruckt: k === 'ja' }))]),
h('div', {}, [feldLabel('Kuvert gedruckt'), segToggle(o.kuvert_gedruckt ? 'ja' : 'nein', [{ key: 'ja', label: 'Ja' }, { key: 'nein', label: 'Nein' }], (k) => setOrderFeld(o, { kuvert_gedruckt: k === 'ja' }))]),
]),

View File

@@ -66,15 +66,20 @@ const Board = {
const aktiveMaschinen = computed(() => machines.value.filter((m) => m.active !== false));
function kundeName(o) { return [o.customer?.company, [o.customer?.first_name, o.customer?.last_name].filter(Boolean).join(' ')].filter(Boolean).join(' · '); }
// „Seite 1" o. Ä. = Seiten eines mehrseitigen Briefs, kein Empfänger.
const istSeitenmarker = (z) => /^seite\s*\d+$/i.test(String(z || '').trim());
function freitextName(ft) {
const z = ft ? String(ft).split('\n').map((s) => s.trim()).filter(Boolean)[0] : '';
return istSeitenmarker(z) ? '' : z;
}
function empfKurz(e) {
if (!e) return '';
const name = e.company || [e.first_name, e.last_name].filter(Boolean).join(' ')
|| (e.free_text ? String(e.free_text).split('\n').map((z) => z.trim()).filter(Boolean)[0] : '');
const name = [e.first_name, e.last_name].filter(Boolean).join(' ') || freitextName(e.free_text);
return [name, e.city].filter(Boolean).join(', ');
}
function entryName(e) {
return [e.salutation, e.first_name, e.last_name].filter(Boolean).join(' ')
|| e.company || (e.free_text ? String(e.free_text).split('\n')[0].trim() : '') || '—';
|| freitextName(e.free_text) || '—';
}
async function laden(leise = false) {
@@ -84,7 +89,7 @@ const Board = {
api.get('/items/orders', { params: {
limit: -1, sort: 'date_created',
filter: { production_status: { _in: AKTIV_STATUS } },
fields: 'id,order_number,order_name,production_status,needs_envelope,brief_gedruckt,kuvert_gedruckt,frankiert,'
fields: 'id,order_number,order_name,production_status,needs_envelope,kuvertieren,brief_gedruckt,kuvert_gedruckt,frankiert,'
+ 'format.key,format.name,shipping_day,shipping_type,versanddatum,date_created,entries_count,source,'
+ 'envelope_labeling,envelope_format,api_client.id,api_client.name,product.name,'
+ 'customer.first_name,customer.last_name,customer.company',
@@ -282,10 +287,11 @@ const Board = {
const fertig = ['versandfertig', 'versendet'].includes(o.production_status);
const versendet = o.production_status === 'versendet';
const frankiert = (typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel');
const kuvertieren = !!o.needs_envelope && o.kuvertieren !== false; // nur Kuvert ohne Blatt → kein Kuvertieren
const steps = [
['Druck Schriftstück', true, !!o.brief_gedruckt],
['Druck Kuvert', !!o.needs_envelope, !!o.kuvert_gedruckt],
['Kuvertieren', !!o.needs_envelope, fertig],
['Kuvertieren', kuvertieren, fertig],
['Frankieren', !!frankiert, fertig],
['Versand', true, versendet],
];
@@ -313,13 +319,14 @@ const Board = {
// Aggregierter Ablauf über alle Aufträge einer Charge.
function ablaufStripAgg(orders) {
const needEnv = orders.filter((o) => o.needs_envelope);
const needKuv = orders.filter((o) => o.needs_envelope && o.kuvertieren !== false);
const needFrank = orders.filter((o) => (typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel'));
const alle = (arr, fn) => arr.length > 0 && arr.every(fn);
const fertigOrder = (o) => ['versandfertig', 'versendet'].includes(o.production_status);
const steps = [
['Druck Schriftstück', true, alle(orders, (o) => !!o.brief_gedruckt)],
['Druck Kuvert', needEnv.length > 0, alle(needEnv, (o) => !!o.kuvert_gedruckt)],
['Kuvertieren', needEnv.length > 0, alle(needEnv, fertigOrder)],
['Kuvertieren', needKuv.length > 0, alle(needKuv, fertigOrder)],
['Frankieren', needFrank.length > 0, alle(needFrank, fertigOrder)],
['Versand', true, alle(orders, (o) => o.production_status === 'versendet')],
];