feat(customer): add bank account details support
This commit is contained in:
@@ -22,6 +22,7 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
|
|||||||
const [showGdprConfirm, setShowGdprConfirm] = useState(false)
|
const [showGdprConfirm, setShowGdprConfirm] = useState(false)
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
|
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
|
||||||
|
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -38,6 +39,10 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
|
|||||||
zip: c.zip ?? '',
|
zip: c.zip ?? '',
|
||||||
city: c.city ?? '',
|
city: c.city ?? '',
|
||||||
email: c.email ?? '',
|
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)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
@@ -126,6 +131,10 @@ export default function CustomerDetailPage({ params }: { params: Promise<{ id: s
|
|||||||
{ label: 'Straße & Hausnummer', key: 'street', span: true },
|
{ label: 'Straße & Hausnummer', key: 'street', span: true },
|
||||||
{ label: 'PLZ', key: 'zip', span: false },
|
{ label: 'PLZ', key: 'zip', span: false },
|
||||||
{ label: 'Ort', key: 'city', 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 }) => (
|
] as const).map(({ label, key, span }) => (
|
||||||
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
||||||
<Label className="text-slate-300 text-sm">{label}</Label>
|
<Label className="text-slate-300 text-sm">{label}</Label>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default function NewCustomerPage() {
|
|||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
|
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
|
||||||
|
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
@@ -62,6 +63,10 @@ export default function NewCustomerPage() {
|
|||||||
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: 'Musterstraße 1' },
|
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: 'Musterstraße 1' },
|
||||||
{ label: 'PLZ', key: 'zip', span: false, placeholder: '12345' },
|
{ label: 'PLZ', key: 'zip', span: false, placeholder: '12345' },
|
||||||
{ label: 'Ort', key: 'city', span: false, placeholder: 'Musterstadt' },
|
{ 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 }) => (
|
] as const).map(({ label, key, span, placeholder }) => (
|
||||||
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
||||||
<Label className="text-slate-300 text-sm">{label}</Label>
|
<Label className="text-slate-300 text-sm">{label}</Label>
|
||||||
|
|||||||
@@ -194,6 +194,14 @@ export default async function OrderSuccessPage({
|
|||||||
<p className="font-semibold text-white">{o.customer_data?.company_name}</p>
|
<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?.first_name} {o.customer_data?.last_name}</p>
|
||||||
<p>{o.customer_data?.address}, {o.customer_data?.zip_code} {o.customer_data?.city}</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>
|
</div>
|
||||||
|
|
||||||
{/* PDF-Download */}
|
{/* PDF-Download */}
|
||||||
|
|||||||
@@ -134,6 +134,11 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
|
|||||||
<Text>{customer.address}</Text>
|
<Text>{customer.address}</Text>
|
||||||
<Text>{customer.zip_code} {customer.city}</Text>
|
<Text>{customer.zip_code} {customer.city}</Text>
|
||||||
<Text>USt-IdNr: {customer.vat_id}</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>
|
||||||
|
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ export function OrderWizard({
|
|||||||
const [isCreatingCustomer, setIsCreatingCustomer] = useState(false)
|
const [isCreatingCustomer, setIsCreatingCustomer] = useState(false)
|
||||||
const [newCustomerForm, setNewCustomerForm] = useState({
|
const [newCustomerForm, setNewCustomerForm] = useState({
|
||||||
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '',
|
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)
|
// Abrechnungsmodell-Auswahl ('one_time' = Kauf, 'monthly' = Abo)
|
||||||
@@ -388,7 +389,10 @@ export function OrderWizard({
|
|||||||
setEndCustomers(prev => [...prev, created])
|
setEndCustomers(prev => [...prev, created])
|
||||||
setSelectedEndCustomerId(created.id)
|
setSelectedEndCustomerId(created.id)
|
||||||
setCustomerMode('select')
|
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) {
|
} catch (e) {
|
||||||
alert('Fehler beim Anlegen des Kunden.')
|
alert('Fehler beim Anlegen des Kunden.')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -573,6 +577,10 @@ export function OrderWizard({
|
|||||||
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: '' },
|
{ label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: '' },
|
||||||
{ label: 'PLZ', key: 'zip', span: false, placeholder: '' },
|
{ label: 'PLZ', key: 'zip', span: false, placeholder: '' },
|
||||||
{ label: 'Ort', key: 'city', 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 }) => (
|
] as const).map(({ label, key, span, placeholder }) => (
|
||||||
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
|
||||||
<Label className="text-slate-300 text-sm">{label}</Label>
|
<Label className="text-slate-300 text-sm">{label}</Label>
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ export async function adminCreateEndCustomer(data: {
|
|||||||
zip?: string
|
zip?: string
|
||||||
city?: string
|
city?: string
|
||||||
email?: string
|
email?: string
|
||||||
|
bank_iban?: string
|
||||||
|
bank_bic?: string
|
||||||
|
bank_name?: string
|
||||||
|
bank_owner?: string
|
||||||
}) {
|
}) {
|
||||||
const admin = createAdminClient()
|
const admin = createAdminClient()
|
||||||
const { data: dbData, error } = await admin
|
const { data: dbData, error } = await admin
|
||||||
@@ -188,6 +192,10 @@ export async function adminCreateEndCustomer(data: {
|
|||||||
zip: data.zip || null,
|
zip: data.zip || null,
|
||||||
city: data.city || null,
|
city: data.city || null,
|
||||||
email: data.email || 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()
|
.select()
|
||||||
.single()
|
.single()
|
||||||
@@ -205,6 +213,10 @@ export async function adminUpdateEndCustomer(id: string, data: {
|
|||||||
zip?: string
|
zip?: string
|
||||||
city?: string
|
city?: string
|
||||||
email?: string
|
email?: string
|
||||||
|
bank_iban?: string
|
||||||
|
bank_bic?: string
|
||||||
|
bank_name?: string
|
||||||
|
bank_owner?: string
|
||||||
}) {
|
}) {
|
||||||
const admin = createAdminClient()
|
const admin = createAdminClient()
|
||||||
const { data: dbData, error } = await admin
|
const { data: dbData, error } = await admin
|
||||||
@@ -219,6 +231,10 @@ export async function adminUpdateEndCustomer(id: string, data: {
|
|||||||
zip: data.zip || null,
|
zip: data.zip || null,
|
||||||
city: data.city || null,
|
city: data.city || null,
|
||||||
email: data.email || 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)
|
.eq('id', id)
|
||||||
.select()
|
.select()
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ export type EndCustomerFormData = {
|
|||||||
zip?: string
|
zip?: string
|
||||||
city?: string
|
city?: string
|
||||||
email?: string
|
email?: string
|
||||||
|
bank_iban?: string
|
||||||
|
bank_bic?: string
|
||||||
|
bank_name?: string
|
||||||
|
bank_owner?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Lesen ───────────────────────────────────────────────────────────────────
|
// ─── Lesen ───────────────────────────────────────────────────────────────────
|
||||||
@@ -74,6 +78,10 @@ export async function createEndCustomer(formData: EndCustomerFormData): Promise<
|
|||||||
zip: formData.zip || null,
|
zip: formData.zip || null,
|
||||||
city: formData.city || null,
|
city: formData.city || null,
|
||||||
email: formData.email || 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()
|
.select()
|
||||||
.single()
|
.single()
|
||||||
@@ -104,6 +112,10 @@ export async function updateEndCustomer(id: string, formData: EndCustomerFormDat
|
|||||||
zip: formData.zip || null,
|
zip: formData.zip || null,
|
||||||
city: formData.city || null,
|
city: formData.city || null,
|
||||||
email: formData.email || 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)
|
.eq('id', id)
|
||||||
|
|
||||||
@@ -138,6 +150,10 @@ export async function anonymizeEndCustomer(id: string): Promise<void> {
|
|||||||
zip: null,
|
zip: null,
|
||||||
city: null,
|
city: null,
|
||||||
email: null,
|
email: null,
|
||||||
|
bank_iban: null,
|
||||||
|
bank_bic: null,
|
||||||
|
bank_name: null,
|
||||||
|
bank_owner: null,
|
||||||
is_anonymized: true,
|
is_anonymized: true,
|
||||||
})
|
})
|
||||||
.eq('id', id)
|
.eq('id', id)
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ export function buildCustomerSnapshot(
|
|||||||
address: endCustomer.street ?? '',
|
address: endCustomer.street ?? '',
|
||||||
zip_code: endCustomer.zip ?? '',
|
zip_code: endCustomer.zip ?? '',
|
||||||
city: endCustomer.city ?? '',
|
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 {
|
return {
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ export type EndCustomer = {
|
|||||||
zip: string | null
|
zip: string | null
|
||||||
city: string | null
|
city: string | null
|
||||||
email?: string | null
|
email?: string | null
|
||||||
|
bank_iban: string | null
|
||||||
|
bank_bic: string | null
|
||||||
|
bank_name: string | null
|
||||||
|
bank_owner: string | null
|
||||||
is_anonymized: boolean
|
is_anonymized: boolean
|
||||||
created_at: string
|
created_at: string
|
||||||
}
|
}
|
||||||
@@ -100,6 +104,10 @@ export type CustomerSnapshot = {
|
|||||||
zip_code: string
|
zip_code: string
|
||||||
city: string
|
city: string
|
||||||
email?: string
|
email?: string
|
||||||
|
bank_iban?: string
|
||||||
|
bank_bic?: string
|
||||||
|
bank_name?: string
|
||||||
|
bank_owner?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrderModuleSnapshot = {
|
export type OrderModuleSnapshot = {
|
||||||
|
|||||||
@@ -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;
|
||||||
Reference in New Issue
Block a user