PDF-Auftragsvorlagen-System, Objekttyp/Lage-Felder, Eigentümer-Fallback bei Bankverbindung

- PDF-Template-Editor in Einstellungen: Vorlagen hochladen, Formularfelder automatisch auslesen, CRM-Felder zuordnen
- PDF-Vorschau mit annotierten Feldnamen, seitenweise Sortierung der Felder
- Auftrag generieren aus Vertragsdaten (Button im Vertrags-Detail)
- Dynamische Rufnummern-Felder mit Vorwahl-Extraktion und konfigurierbarer Maximalanzahl
- Nicht zugeordnete Felder bleiben editierbar im generierten PDF
- Eigentümer-Felder mit Namens-Kombinationen (Firma+Name etc.) und Fallback auf Kundendaten
- Stressfrei-E-Mail als Feld-Option im Template-Editor
- Objekttyp, Lage und Lage des Anschlusses als neue Felder bei Festnetz-Verträgen (DSL, Glasfaser, Kabel)
- Bankverbindung-Fallback: wenn keine am Vertrag verknüpft, wird automatisch die neueste aktive des Kunden genommen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-05 19:16:47 +02:00
parent 5e9e553882
commit 29eceef26b
16 changed files with 1881 additions and 21 deletions
@@ -717,6 +717,14 @@ function AddressesTab({
</p>
<p className="text-gray-500">{addr.country}</p>
</CopyableBlock>
{(addr.ownerFirstName || addr.ownerLastName || addr.ownerCompany) && (
<div className="mt-2 pt-2 border-t text-xs text-gray-500">
<span className="font-medium">Eigentümer: </span>
{addr.ownerCompany && <span>{addr.ownerCompany} </span>}
{addr.ownerFirstName} {addr.ownerLastName}
{addr.ownerPhone && <span> · {addr.ownerPhone}</span>}
</div>
)}
</div>
))}
</div>
@@ -2097,6 +2105,16 @@ function AddressModal({
city: address?.city || '',
country: address?.country || 'Deutschland',
isDefault: address?.isDefault || false,
ownerCompany: address?.ownerCompany || '',
ownerFirstName: address?.ownerFirstName || '',
ownerLastName: address?.ownerLastName || '',
ownerStreet: address?.ownerStreet || '',
ownerHouseNumber: address?.ownerHouseNumber || '',
ownerPostalCode: address?.ownerPostalCode || '',
ownerCity: address?.ownerCity || '',
ownerPhone: address?.ownerPhone || '',
ownerMobile: address?.ownerMobile || '',
ownerEmail: address?.ownerEmail || '',
});
const [formData, setFormData] = useState(getInitialFormData);
@@ -2106,15 +2124,7 @@ function AddressModal({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
setFormData({
type: 'DELIVERY_RESIDENCE',
street: '',
houseNumber: '',
postalCode: '',
city: '',
country: 'Deutschland',
isDefault: false,
});
setFormData(getInitialFormData());
},
});
@@ -2205,6 +2215,82 @@ function AddressModal({
Als Standard setzen
</label>
{/* Eigentümer (optional, nur bei Liefer-/Meldeadresse) */}
{formData.type === 'DELIVERY_RESIDENCE' && (
<div className="pt-4 border-t">
<h4 className="text-sm font-medium text-gray-700 mb-1">Eigentümer</h4>
<p className="text-xs text-gray-500 mb-3">
Nur ausfüllen wenn der Kunde nicht selbst Eigentümer ist (z.B. Mietwohnung).
</p>
<div className="space-y-3">
<Input
label="Firma (optional)"
value={formData.ownerCompany}
onChange={(e) => setFormData({ ...formData, ownerCompany: e.target.value })}
placeholder="z.B. Wohnungsbaugesellschaft"
/>
<div className="grid grid-cols-2 gap-3">
<Input
label="Vorname"
value={formData.ownerFirstName}
onChange={(e) => setFormData({ ...formData, ownerFirstName: e.target.value })}
/>
<Input
label="Nachname"
value={formData.ownerLastName}
onChange={(e) => setFormData({ ...formData, ownerLastName: e.target.value })}
/>
</div>
<div className="grid grid-cols-3 gap-3">
<div className="col-span-2">
<Input
label="Straße"
value={formData.ownerStreet}
onChange={(e) => setFormData({ ...formData, ownerStreet: e.target.value })}
/>
</div>
<Input
label="Hausnr."
value={formData.ownerHouseNumber}
onChange={(e) => setFormData({ ...formData, ownerHouseNumber: e.target.value })}
/>
</div>
<div className="grid grid-cols-3 gap-3">
<Input
label="PLZ"
value={formData.ownerPostalCode}
onChange={(e) => setFormData({ ...formData, ownerPostalCode: e.target.value })}
/>
<div className="col-span-2">
<Input
label="Ort"
value={formData.ownerCity}
onChange={(e) => setFormData({ ...formData, ownerCity: e.target.value })}
/>
</div>
</div>
<div className="grid grid-cols-3 gap-3">
<Input
label="Telefon"
value={formData.ownerPhone}
onChange={(e) => setFormData({ ...formData, ownerPhone: e.target.value })}
/>
<Input
label="Mobil"
value={formData.ownerMobile}
onChange={(e) => setFormData({ ...formData, ownerMobile: e.target.value })}
/>
<Input
label="E-Mail"
value={formData.ownerEmail}
onChange={(e) => setFormData({ ...formData, ownerEmail: e.target.value })}
type="email"
/>
</div>
</div>
</div>
)}
<div className="flex justify-end gap-2">
<Button type="button" variant="secondary" onClick={onClose}>
Abbrechen