diff --git a/src/components/CreateTicketModal.jsx b/src/components/CreateTicketModal.jsx index b093183..a203888 100644 --- a/src/components/CreateTicketModal.jsx +++ b/src/components/CreateTicketModal.jsx @@ -5,7 +5,7 @@ import { useEmployees } from '../hooks/useEmployees' // Fallback-Werte falls Config nicht geladen werden kann const DEFAULT_TICKET_TYPES = [ - 'Home Office', 'Holidays', 'Trip', 'Supportrequest', 'Change Request', + 'Akquise', 'Home Office', 'Holidays', 'Trip', 'Supportrequest', 'Change Request', 'Maintenance', 'Project', 'Webpage', 'Controlling', 'Development', 'Documentation', 'Meeting/Conference', 'IT Management', 'IT Security', 'Procurement', 'Rollout', 'Emergency Call', 'Other Services' diff --git a/src/components/CreateWorksheetModal.jsx b/src/components/CreateWorksheetModal.jsx index 49ac32d..d5f5828 100644 --- a/src/components/CreateWorksheetModal.jsx +++ b/src/components/CreateWorksheetModal.jsx @@ -30,6 +30,9 @@ const RESPONSE_LEVELS = [ 'n/a' ] +// Akquise-Tickets: Ergebnis des Kundenkontakts (statt technischer Status) +const ACQUISITION_STATUS = ['In Kontakt', 'Zugesagt', 'Abgesagt'] + export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCreate }) { const { user } = useAuth() const today = new Date().toLocaleDateString('de-DE', { @@ -55,12 +58,18 @@ export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCre const [error, setError] = useState('') const [autoCalculate, setAutoCalculate] = useState(true) + const isAcquisition = workorder?.type === 'Akquise' + const statusOptions = isAcquisition ? ACQUISITION_STATUS : STATUS_OPTIONS + const defaultStatus = isAcquisition + ? (ACQUISITION_STATUS.includes(workorder?.status) ? workorder.status : 'In Kontakt') + : (workorder?.status || 'Open') + // Reset form wenn Modal geöffnet wird useEffect(() => { if (isOpen && workorder) { setFormData({ serviceType: 'Remote', - newStatus: workorder.status || 'Open', + newStatus: defaultStatus, newResponseLevel: workorder.responseLevel || '', totalTime: 0, startDate: today, @@ -189,17 +198,22 @@ export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCre
- + + {isAcquisition && ( + + "Zugesagt" macht den Lead automatisch zum festen Kunden. + + )}
diff --git a/src/components/InvoicePanel.jsx b/src/components/InvoicePanel.jsx index 7c4ea8b..bd2f6f1 100644 --- a/src/components/InvoicePanel.jsx +++ b/src/components/InvoicePanel.jsx @@ -3,6 +3,7 @@ import { FaFilePdf, FaPlus, FaLink, FaSpinner, FaRotate } from 'react-icons/fa6' import { databases, DATABASE_ID, COLLECTIONS } from '../lib/appwrite' import { integrationsApi, formatMoney, INVOICE_STATUS } from '../lib/integrationsApi' import { useInvoices } from '../hooks/useInvoices' +import { customerStage, isInvoiceReady } from '../lib/leads' const card = { background: 'rgba(45, 55, 72, 0.5)', @@ -74,7 +75,15 @@ export default function InvoicePanel({ ticket }) { {customerError &&
{customerError}
} - {!clientId ? ( + {customer && customerStage(customer) === 'lead' ? ( +

+ Dieser Kunde ist noch ein Lead. Erst zum festen Kunden machen (im Kunden-Bereich, vollstaendige Daten noetig) - dann sind Rechnungen moeglich. +

+ ) : !clientId && !isInvoiceReady(customer) ? ( +

+ Rechnungsdaten unvollstaendig. Bitte im Kunden-Bereich Unternehmen, E-Mail, Strasse, PLZ und Stadt ergaenzen. +

+ ) : !clientId ? ( ) : (
{c.name} - {c.number ? {c.number} : null} + {c.number ? {c.number} : null} {c.email ?
{c.email}
: null}
} />
+ const stage = customerStage(customer) const syntheticTicket = { customerId: customer.$id, customerName: customer.name, woid: '' } return ( @@ -50,30 +52,48 @@ export default function CustomerDetailPage() { {customer.importantCustomer && } {customer.name} + {stage === 'lead' && Lead} + {stage === 'customer' && Fester Kunde} + {stage === 'lost' && Abgesagt} } - subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' ')} + subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' ')} actions={} /> - {tab === 'overview' && } + {tab === 'overview' && } {tab === 'tickets' && } - {tab === 'invoices' && } + {tab === 'invoices' && ( + stage === 'lead' ? ( + setTab('overview')}>Daten erfassen} /> + ) : !isInvoiceReady(customer) ? ( + setTab('overview')}>Daten vervollstaendigen} /> + ) : ( + + ) + )} {tab === 'documents' && } {tab === 'projects' && } ) } -function OverviewTab({ customer, onSaved }) { +function OverviewTab({ customer, stage, onSaved }) { const [form, setForm] = useState({ name: customer.name || '', + companyName: customer.companyName || customer.name || '', code: customer.code || '', - location: customer.location || '', email: customer.email || '', phone: customer.phone || '', + contactName: customer.contactName || '', + street: customer.street || '', + postalCode: customer.postalCode || '', + city: customer.city || '', + country: customer.country || 'Deutschland', + vatNumber: customer.vatNumber || '', + location: customer.location || '', notes: customer.notes || '', importantCustomer: !!customer.importantCustomer, paperlessCorrespondent: customer.paperlessCorrespondent || '', @@ -81,56 +101,76 @@ function OverviewTab({ customer, onSaved }) { const [busy, setBusy] = useState(false) const [msg, setMsg] = useState(null) + const ready = isInvoiceReady({ ...customer, ...form }) + const save = async () => { setBusy(true); setMsg(null) try { - await databases.updateDocument(DATABASE_ID, COLLECTIONS.CUSTOMERS, customer.$id, form) - setMsg('Gespeichert') + const updated = await databases.updateDocument(DATABASE_ID, COLLECTIONS.CUSTOMERS, customer.$id, form) + let note = 'Gespeichert' + // Wenn fester Kunde: Aenderungen nach InvoiceNinja syncen (sofern Daten reichen oder bereits verknuepft) + if (stage === 'customer' && (updated.invoiceNinjaClientId || isInvoiceReady(updated))) { + try { await syncCustomerToNinja(updated) } catch (e) { note = 'Gespeichert, InvoiceNinja-Sync fehlgeschlagen: ' + e.message } + } + setMsg(note) onSaved() } catch (err) { setMsg('Fehler: ' + err.message) } finally { setBusy(false) } } + const upgrade = async () => { + if (!ready) { setMsg('Bitte zuerst alle Pflichtfelder (Unternehmen, E-Mail, Strasse, PLZ, Stadt) ausfuellen.'); return } + setBusy(true); setMsg(null) + const r = await upgradeToCustomer(customer, form) + if (r.clientId) setMsg('Zum festen Kunden gemacht und mit InvoiceNinja verknuepft.') + else setMsg('Status gesetzt, aber InvoiceNinja-Verknuepfung fehlgeschlagen: ' + (r.syncError || 'unbekannt')) + setBusy(false) + onSaved() + } + return ( -
+
- setForm({ ...form, name: e.target.value })} /> + setForm({ ...form, name: e.target.value, companyName: e.target.value })} /> setForm({ ...form, code: e.target.value })} />
- setForm({ ...form, location: e.target.value })} />
- setForm({ ...form, email: e.target.value })} /> + setForm({ ...form, email: e.target.value })} /> setForm({ ...form, phone: e.target.value })} />
+ setForm({ ...form, contactName: e.target.value })} />