feat(customer): add bank account details support

This commit is contained in:
DanielS
2026-07-03 09:46:47 +02:00
parent 250dd82847
commit f5f577ea73
10 changed files with 86 additions and 1 deletions

View File

@@ -22,6 +22,7 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
const [showGdprConfirm, setShowGdprConfirm] = useState(false)
const [form, setForm] = useState({
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
})
useEffect(() => {
@@ -38,6 +39,10 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
zip: c.zip ?? '',
city: c.city ?? '',
email: c.email ?? '',
bank_iban: c.bank_iban ?? '',
bank_bic: c.bank_bic ?? '',
bank_name: c.bank_name ?? '',
bank_owner: c.bank_owner ?? '',
})
setLoading(false)
})
@@ -126,6 +131,10 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
{ label: 'Straße & Hausnummer', key: 'street', span: true },
{ label: 'PLZ', key: 'zip', span: false },
{ label: 'Ort', key: 'city', span: false },
{ label: 'Kontoinhaber', key: 'bank_owner', span: false },
{ label: 'IBAN', key: 'bank_iban', span: false },
{ label: 'BIC', key: 'bank_bic', span: false },
{ label: 'Bankname', key: 'bank_name', span: false },
] as const).map(({ label, key, span }) => (
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
<Label className="text-slate-300 text-sm">{label}</Label>

View File

@@ -15,6 +15,7 @@ export default function NewCustomerPage() {
const [saving, setSaving] = useState(false)
const [form, setForm] = useState({
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
})
const handleSave = async () => {
@@ -62,6 +63,10 @@ export default function NewCustomerPage() {
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: 'Musterstraße 1' },
{ label: 'PLZ', key: 'zip', span: false, placeholder: '12345' },
{ label: 'Ort', key: 'city', span: false, placeholder: 'Musterstadt' },
{ label: 'Kontoinhaber', key: 'bank_owner', span: false, placeholder: 'Max Mustermann' },
{ label: 'IBAN', key: 'bank_iban', span: false, placeholder: 'DE89370400440532013000' },
{ label: 'BIC', key: 'bank_bic', span: false, placeholder: 'SOLADE21XXX' },
{ label: 'Bankname', key: 'bank_name', span: false, placeholder: 'Musterbank' },
] as const).map(({ label, key, span, placeholder }) => (
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
<Label className="text-slate-300 text-sm">{label}</Label>

View File

@@ -194,6 +194,14 @@ export default async function OrderSuccessPage({
<p className="font-semibold text-white">{o.customer_data?.company_name}</p>
<p>{o.customer_data?.first_name} {o.customer_data?.last_name}</p>
<p>{o.customer_data?.address}, {o.customer_data?.zip_code} {o.customer_data?.city}</p>
{o.customer_data?.bank_iban && (
<div className="text-xs text-slate-500 pt-2 border-t border-white/5 mt-2 space-y-0.5">
<p><span className="font-semibold text-slate-400">Kontoinhaber:</span> {o.customer_data.bank_owner}</p>
<p><span className="font-semibold text-slate-400">IBAN:</span> {o.customer_data.bank_iban}</p>
{o.customer_data.bank_bic && <p><span className="font-semibold text-slate-400">BIC:</span> {o.customer_data.bank_bic}</p>}
{o.customer_data.bank_name && <p><span className="font-semibold text-slate-400">Bank:</span> {o.customer_data.bank_name}</p>}
</div>
)}
</div>
{/* PDF-Download */}

View File

@@ -134,6 +134,11 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
<Text>{customer.address}</Text>
<Text>{customer.zip_code} {customer.city}</Text>
<Text>USt-IdNr: {customer.vat_id}</Text>
{customer.bank_iban && (
<Text style={{ marginTop: 4, color: '#666' }}>
Bank: {customer.bank_name || '-'} · IBAN: {customer.bank_iban} · BIC: {customer.bank_bic || '-'} · Inhaber: {customer.bank_owner || '-'}
</Text>
)}
</View>
<View style={styles.section}>

View File

@@ -135,6 +135,7 @@ export function OrderWizard({
const [isCreatingCustomer, setIsCreatingCustomer] = useState(false)
const [newCustomerForm, setNewCustomerForm] = useState({
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '',
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
})
// Abrechnungsmodell-Auswahl ('one_time' = Kauf, 'monthly' = Abo)
@@ -388,7 +389,10 @@ export function OrderWizard({
setEndCustomers(prev => [...prev, created])
setSelectedEndCustomerId(created.id)
setCustomerMode('select')
setNewCustomerForm({ company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '' })
setNewCustomerForm({
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '',
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
})
} catch (e) {
alert('Fehler beim Anlegen des Kunden.')
} finally {
@@ -573,6 +577,10 @@ export function OrderWizard({
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: '' },
{ label: 'PLZ', key: 'zip', span: false, placeholder: '' },
{ label: 'Ort', key: 'city', span: false, placeholder: '' },
{ label: 'Kontoinhaber', key: 'bank_owner', span: false, placeholder: 'Max Mustermann' },
{ label: 'IBAN', key: 'bank_iban', span: false, placeholder: 'DE89370400440532013000' },
{ label: 'BIC', key: 'bank_bic', span: false, placeholder: 'SOLADE21XXX' },
{ label: 'Bankname', key: 'bank_name', span: false, placeholder: 'Musterbank' },
] as const).map(({ label, key, span, placeholder }) => (
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
<Label className="text-slate-300 text-sm">{label}</Label>

View File

@@ -174,6 +174,10 @@ export async function adminCreateEndCustomer(data: {
zip?: string
city?: string
email?: string
bank_iban?: string
bank_bic?: string
bank_name?: string
bank_owner?: string
}) {
const admin = createAdminClient()
const { data: dbData, error } = await admin
@@ -188,6 +192,10 @@ export async function adminCreateEndCustomer(data: {
zip: data.zip || null,
city: data.city || null,
email: data.email || null,
bank_iban: data.bank_iban || null,
bank_bic: data.bank_bic || null,
bank_name: data.bank_name || null,
bank_owner: data.bank_owner || null,
}])
.select()
.single()
@@ -205,6 +213,10 @@ export async function adminUpdateEndCustomer(id: string, data: {
zip?: string
city?: string
email?: string
bank_iban?: string
bank_bic?: string
bank_name?: string
bank_owner?: string
}) {
const admin = createAdminClient()
const { data: dbData, error } = await admin
@@ -219,6 +231,10 @@ export async function adminUpdateEndCustomer(id: string, data: {
zip: data.zip || null,
city: data.city || null,
email: data.email || null,
bank_iban: data.bank_iban || null,
bank_bic: data.bank_bic || null,
bank_name: data.bank_name || null,
bank_owner: data.bank_owner || null,
})
.eq('id', id)
.select()

View File

@@ -16,6 +16,10 @@ export type EndCustomerFormData = {
zip?: string
city?: string
email?: string
bank_iban?: string
bank_bic?: string
bank_name?: string
bank_owner?: string
}
// ─── Lesen ───────────────────────────────────────────────────────────────────
@@ -74,6 +78,10 @@ export async function createEndCustomer(formData: EndCustomerFormData): Promise<
zip: formData.zip || null,
city: formData.city || null,
email: formData.email || null,
bank_iban: formData.bank_iban || null,
bank_bic: formData.bank_bic || null,
bank_name: formData.bank_name || null,
bank_owner: formData.bank_owner || null,
}])
.select()
.single()
@@ -104,6 +112,10 @@ export async function updateEndCustomer(id: string, formData: EndCustomerFormDat
zip: formData.zip || null,
city: formData.city || null,
email: formData.email || null,
bank_iban: formData.bank_iban || null,
bank_bic: formData.bank_bic || null,
bank_name: formData.bank_name || null,
bank_owner: formData.bank_owner || null,
})
.eq('id', id)
@@ -138,6 +150,10 @@ export async function anonymizeEndCustomer(id: string): Promise<void> {
zip: null,
city: null,
email: null,
bank_iban: null,
bank_bic: null,
bank_name: null,
bank_owner: null,
is_anonymized: true,
})
.eq('id', id)

View File

@@ -58,6 +58,10 @@ export function buildCustomerSnapshot(
address: endCustomer.street ?? '',
zip_code: endCustomer.zip ?? '',
city: endCustomer.city ?? '',
bank_iban: endCustomer.bank_iban ?? undefined,
bank_bic: endCustomer.bank_bic ?? undefined,
bank_name: endCustomer.bank_name ?? undefined,
bank_owner: endCustomer.bank_owner ?? undefined,
}
}
return {

View File

@@ -68,6 +68,10 @@ export type EndCustomer = {
zip: string | null
city: string | null
email?: string | null
bank_iban: string | null
bank_bic: string | null
bank_name: string | null
bank_owner: string | null
is_anonymized: boolean
created_at: string
}
@@ -100,6 +104,10 @@ export type CustomerSnapshot = {
zip_code: string
city: string
email?: string
bank_iban?: string
bank_bic?: string
bank_name?: string
bank_owner?: string
}
export type OrderModuleSnapshot = {

View File

@@ -0,0 +1,6 @@
-- Migration: Add bank details to end_customers table
ALTER TABLE end_customers
ADD COLUMN IF NOT EXISTS bank_iban TEXT,
ADD COLUMN IF NOT EXISTS bank_bic TEXT,
ADD COLUMN IF NOT EXISTS bank_name TEXT,
ADD COLUMN IF NOT EXISTS bank_owner TEXT;