Postkarten-Maße-Fix, Sammelversand-Staffel, Extern-Format, Mustertexte

- render_formats: Upsert statt nur-anlegen → korrekte A6-Maße werden beim
  Bootstrap erzwungen (behebt „Postkarten passen nicht, weder quer noch hoch")
- Sammelversand-Staffel: bis 5 Schriftstücke 1,80 € (neues Preisitem
  sammelversand_klein), ab 6 der reguläre Satz – Backend + App-Option
- Extern-Modus: Postkarten-Format (hoch/quer) wieder wählbar; optionaler
  ?format=-Parameter; kein Auto-Vorbelegen bei mehreren Formaten
- Unbegrenzte Mustertexte: neue Collection muster_texts (Dropdown im
  „Manueller Druck", Du/Sie als Fallback + Seeds)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lucas Orth
2026-08-04 07:49:22 +02:00
parent 26040bd68f
commit 0b2f2ba50a
4 changed files with 60 additions and 19 deletions

View File

@@ -46,6 +46,8 @@ const NeuAuftrag = defineComponent({
const formate = ref([]);
const verbotenRe = ref(null);
const musterTexte = ref({ text_du: '', text_sie: '' });
// Frei anlegbare Mustertexte (Collection muster_texts). Fallback: Du/Sie aus muster_settings.
const musterListe = ref([]);
const geladen = ref(false);
// Aktives Unterwerkzeug: 'neu' (manueller Druck) oder 'signatur' (Unterschriftenservice).
const ansicht = ref('neu');
@@ -79,15 +81,17 @@ const NeuAuftrag = defineComponent({
async function laden() {
try {
const [p, f, ps, mt] = await Promise.all([
const [p, f, ps, mt, mtx] = await Promise.all([
api.get('/items/products', { params: { limit: -1, filter: { active: { _eq: true }, type: { _in: ['letter', 'postcard'] } }, fields: 'key,name,type' } }),
api.get('/items/formats', { params: { limit: -1, filter: { active: { _eq: true } }, fields: 'key,name,product.key' } }),
api.get('/items/pricing_settings', { params: { fields: 'char_whitelist' } }).catch(() => ({ data: { data: {} } })),
api.get('/items/muster_settings', { params: { fields: 'text_du,text_sie' } }).catch(() => ({ data: { data: {} } })),
api.get('/items/muster_texts', { params: { limit: -1, filter: { active: { _eq: true } }, sort: 'sort', fields: 'id,label,text' } }).catch(() => ({ data: { data: [] } })),
]);
produkte.value = p.data?.data || [];
formate.value = f.data?.data || [];
musterTexte.value = mt.data?.data || { text_du: '', text_sie: '' };
musterListe.value = mtx.data?.data || [];
const wl = ps.data?.data?.char_whitelist;
// Zeilenumbrüche immer erlauben (Brieftext ist mehrzeilig). Einzeilige
// Felder entfernen \n/\r ohnehin separat, daher hier gefahrlos.
@@ -207,8 +211,11 @@ const NeuAuftrag = defineComponent({
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '',
});
}
const musterEinfuegen = (form) => {
const t = form === 'du' ? musterTexte.value.text_du : musterTexte.value.text_sie;
const musterEinfuegen = (wert) => {
// Freie Mustertexte (per ID) bevorzugt; sonst Fallback Du/Sie.
let t = '';
if (wert === 'du' || wert === 'sie') t = wert === 'du' ? musterTexte.value.text_du : musterTexte.value.text_sie;
else t = (musterListe.value.find((m) => String(m.id) === String(wert)) || {}).text || '';
if (!t) return;
neu.text = neu.text.trim() ? `${neu.text}\n\n${t}` : t;
neu.previewSession = null;
@@ -287,11 +294,12 @@ const NeuAuftrag = defineComponent({
const b3 = h('div', { style: BEREICH }, [
h('div', { style: 'display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px;' }, [
bereichKopf('✍ Text'),
h('select', { style: WSELECT + 'font-size:13px;padding:7px 10px;width:auto;', onChange: (e) => { if (e.target.value) { musterEinfuegen(e.target.value); e.target.value = ''; } } }, [
h('option', { value: '' }, 'Mustertext einfügen …'),
h('option', { value: 'sie' }, 'Sie-Form'),
h('option', { value: 'du' }, 'Du-Form'),
]),
h('select', { style: WSELECT + 'font-size:13px;padding:7px 10px;width:auto;', onChange: (e) => { if (e.target.value) { musterEinfuegen(e.target.value); e.target.value = ''; } } },
[h('option', { value: '' }, 'Mustertext einfügen …')].concat(
musterListe.value.length
? musterListe.value.map((m) => h('option', { value: m.id }, m.label || `Text ${m.id}`))
: [h('option', { value: 'sie' }, 'Sie-Form'), h('option', { value: 'du' }, 'Du-Form')],
)),
]),
label(`Brieftext · ${neu.text.length} Zeichen · [[Platzhalter]] erlaubt`),
h('textarea', { value: neu.text, rows: 8, style: WFELD + 'width:100%;font-family:var(--family-monospace,monospace);', onInput: (e) => { neu.text = saeubere(e.target.value); neu.previewSession = null; } }),

View File

@@ -344,7 +344,10 @@ const handler = (router, { services, getSchema, logger }) => {
add('Porto Inland', priceOf('porto_inland') * inlandCount, true);
add('Porto Ausland', priceOf('porto_ausland') * auslandCount, true);
} else {
add('Sammelversand', priceOf('sammelversand'), false);
// Sammelversand-Staffel: bis 5 Schriftstücke günstiger (sammelversand_klein),
// ab 6 der reguläre Satz. Fallback 1,80 €, falls der kleine Satz fehlt.
const sammelKlein = priceItems['sammelversand_klein'] ? priceOf('sammelversand_klein') : 1.80;
add('Sammelversand', menge <= 5 ? sammelKlein : priceOf('sammelversand'), false);
}
// 3) Kuvert + Beschriftung – NUR bei Sammelversand.