From 1a4559589a9ae2d07dfec05051a1f891221f475b Mon Sep 17 00:00:00 2001 From: Fidelis Huber Date: Fri, 9 Oct 2026 20:16:19 +0200 Subject: [PATCH] Leeren Bestellschein aus Materialschein anlegen (bei Neuanlage und im bestehenden Schein) Co-Authored-By: Claude Sonnet 5.5 --- client/src/lib/schein.ts | 27 ++++++++++++++++++++++++++- client/src/pages/Dashboard.tsx | 12 +++++++++++- client/src/pages/ProjektDetail.tsx | 28 +++++++++++++++++++++++++++- client/src/styles.css | 2 ++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/client/src/lib/schein.ts b/client/src/lib/schein.ts index a02868b..a10673c 100644 --- a/client/src/lib/schein.ts +++ b/client/src/lib/schein.ts @@ -1,4 +1,5 @@ -import type { ScheinTyp } from './types'; +import { api } from './api'; +import type { Projekt, ScheinTyp } from './types'; // Materialschein und Bestellschein verhalten sich identisch – nur Beschriftung, Routen // und Dateiname des Exports unterscheiden sich. @@ -17,3 +18,27 @@ export function scheinInfo(typ: ScheinTyp): ScheinInfo { ? { name: 'Bestellschein', plural: 'Bestellscheine', liste: 'Bestellliste', druckTitel: 'Bestellschein', betreff: 'Bestellung', listBase: '/bestellungen', detailBase: '/bestellungen' } : { name: 'Materialschein', plural: 'Materialscheine', liste: 'Materialliste', druckTitel: 'Materialschein', betreff: 'Materialverbrauch', listBase: '/', detailBase: '/projekte' }; } + +// Legt zu einem Materialschein einen leeren Bestellschein mit denselben Kopfdaten an +// (Projekt/Baustelle, Kunde, Auftrags-Nr., Datum, Monteur, Notiz). Beide Scheine sind danach +// unabhängig voneinander – Löschen geht nur im jeweiligen Reiter. +export interface ScheinKopf { + titel: string; + auftraggeber: string; + auftragsNr: string; + datum: string; + monteur: string; + notiz: string; +} + +export const normName = (s: string) => s.trim().toLowerCase().replace(/\s+/g, ' '); + +export async function bestellscheinFuerKopf(kopf: ScheinKopf): Promise { + return api.projekte.create({ ...kopf, typ: 'Bestell' }); +} + +// Gibt es zu Projekt + Kunde schon einen Bestellschein? (Schutz vor versehentlichen Doppelten) +export async function findeBestellschein(kopf: Pick): Promise { + const alle = await api.projekte.list('Bestell'); + return alle.find(p => normName(p.titel) === normName(kopf.titel) && normName(p.auftraggeber ?? '') === normName(kopf.auftraggeber)); +} diff --git a/client/src/pages/Dashboard.tsx b/client/src/pages/Dashboard.tsx index a8e997b..41c815b 100644 --- a/client/src/pages/Dashboard.tsx +++ b/client/src/pages/Dashboard.tsx @@ -5,7 +5,7 @@ import type { Projekt, ProjektStatus, ScheinTyp } from '../lib/types'; import { useFavoriten } from '../lib/favoriten'; import MonteurField from '../components/MonteurField'; import FavStar from '../components/FavStar'; -import { scheinInfo } from '../lib/schein'; +import { bestellscheinFuerKopf, scheinInfo } from '../lib/schein'; function dd(s: string | null) { if (!s) return '–'; @@ -23,6 +23,7 @@ export default function Dashboard({ typ }: { typ: ScheinTyp }) { const [search, setSearch] = useState(''); const [showForm, setShowForm] = useState(false); const [busy, setBusy] = useState(false); + const [mitBestellschein, setMitBestellschein] = useState(false); const [err, setErr] = useState(''); const [form, setForm] = useState({ titel: '', auftraggeber: '', auftragsNr: '', datum: new Date().toISOString().slice(0, 10), monteur: '', notiz: '' }); @@ -59,7 +60,10 @@ export default function Dashboard({ typ }: { typ: ScheinTyp }) { setErr(''); try { const projekt = await api.projekte.create({ ...form, typ }); + // Optional zusätzlich ein leerer Bestellschein mit denselben Kopfdaten (Kunde etc.). + if (typ === 'Material' && mitBestellschein) await bestellscheinFuerKopf(form); setShowForm(false); + setMitBestellschein(false); navigate(`${info.detailBase}/${projekt.id}`); } catch (e) { setErr(e instanceof Error ? e.message : String(e)); @@ -121,6 +125,12 @@ export default function Dashboard({ typ }: { typ: ScheinTyp }) {