217 lines
6.9 KiB
TypeScript
217 lines
6.9 KiB
TypeScript
'use server'
|
|
|
|
import { createClient } from '@/lib/supabase/server'
|
|
import { createAdminClient } from '@/lib/supabase/admin'
|
|
import { revalidatePath } from 'next/cache'
|
|
import type { EndCustomer } from '@/lib/types'
|
|
|
|
// ─── Typen ───────────────────────────────────────────────────────────────────
|
|
|
|
export type EndCustomerFormData = {
|
|
company_name: string
|
|
vat_id?: string
|
|
first_name?: string
|
|
last_name?: string
|
|
street?: string
|
|
zip?: string
|
|
city?: string
|
|
email?: string
|
|
bank_iban?: string
|
|
bank_bic?: string
|
|
bank_name?: string
|
|
bank_owner?: string
|
|
}
|
|
|
|
// ─── Lesen ───────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Holt alle (nicht anonymisierten) Endkunden des eingeloggten Partners.
|
|
* RLS stellt sicher, dass nur eigene Kunden zurückgegeben werden.
|
|
*/
|
|
export async function getEndCustomers(): Promise<EndCustomer[]> {
|
|
const supabase = await createClient()
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
if (!user) throw new Error('Not authenticated')
|
|
|
|
const { data: dbUser, error: dbUserError } = await supabase
|
|
.from('users')
|
|
.select('company_id')
|
|
.eq('id', user.id)
|
|
.single()
|
|
|
|
if (dbUserError || !dbUser || !dbUser.company_id) {
|
|
return []
|
|
}
|
|
|
|
const { data, error } = await supabase
|
|
.from('end_customers')
|
|
.select('*')
|
|
.eq('partner_id', dbUser.company_id)
|
|
.order('company_name', { ascending: true })
|
|
|
|
if (error) throw error
|
|
return data as EndCustomer[]
|
|
}
|
|
|
|
/**
|
|
* Holt einen einzelnen Endkunden (Eigentümerprüfung via RLS).
|
|
*/
|
|
export async function getEndCustomer(id: string): Promise<EndCustomer | null> {
|
|
const supabase = await createClient()
|
|
const { data, error } = await supabase
|
|
.from('end_customers')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single()
|
|
|
|
if (error) return null
|
|
return data as EndCustomer
|
|
}
|
|
|
|
// ─── Erstellen ────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Legt einen neuen Endkunden für den eingeloggten Partner an.
|
|
* partner_id wird serverseitig aus der Session gesetzt (kein Client-Trust).
|
|
*/
|
|
export async function createEndCustomer(formData: EndCustomerFormData, companyId?: string): Promise<EndCustomer> {
|
|
const supabase = await createClient()
|
|
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
if (!user) throw new Error('Not authenticated')
|
|
|
|
const { data: dbUser } = await supabase
|
|
.from('users')
|
|
.select('role, company_id')
|
|
.eq('id', user.id)
|
|
.single()
|
|
|
|
const isAdmin = dbUser?.role === 'admin'
|
|
const resolvedCompanyId = dbUser?.company_id || companyId
|
|
|
|
if (!isAdmin && !resolvedCompanyId) {
|
|
throw new Error('Kein Unternehmen zugewiesen.')
|
|
}
|
|
|
|
// Admin ohne company_id braucht adminClient (RLS erlaubt kein INSERT ohne passende partner_id)
|
|
const client = (!dbUser?.company_id && isAdmin) ? createAdminClient() : supabase
|
|
|
|
const { data, error } = await client
|
|
.from('end_customers')
|
|
.insert([{
|
|
partner_id: resolvedCompanyId || user.id,
|
|
company_name: formData.company_name,
|
|
vat_id: formData.vat_id || null,
|
|
first_name: formData.first_name || null,
|
|
last_name: formData.last_name || null,
|
|
street: formData.street || null,
|
|
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()
|
|
|
|
if (error) throw error
|
|
|
|
revalidatePath('/my-customers')
|
|
return data as EndCustomer
|
|
}
|
|
|
|
// ─── Aktualisieren ────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Aktualisiert Adressdaten eines Endkunden.
|
|
* RLS verhindert Zugriff auf fremde Kunden.
|
|
*/
|
|
export async function updateEndCustomer(id: string, formData: EndCustomerFormData): Promise<void> {
|
|
const supabase = await createClient()
|
|
|
|
const { error } = await supabase
|
|
.from('end_customers')
|
|
.update({
|
|
company_name: formData.company_name,
|
|
vat_id: formData.vat_id || null,
|
|
first_name: formData.first_name || null,
|
|
last_name: formData.last_name || null,
|
|
street: formData.street || null,
|
|
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)
|
|
|
|
if (error) throw error
|
|
|
|
revalidatePath('/my-customers')
|
|
revalidatePath(`/my-customers/${id}`)
|
|
}
|
|
|
|
// ─── DSGVO: Anonymisieren ─────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Anonymisiert einen Endkunden gemäß DSGVO „Recht auf Vergessenwerden".
|
|
*
|
|
* - Überschreibt alle personenbezogenen Felder mit [GELÖSCHT]
|
|
* - Setzt is_anonymized = true (verhindert weitere Bearbeitung)
|
|
* - LÖSCHT NICHT die Zeile (FK in orders bleibt erhalten)
|
|
* - Bestell-Snapshots in orders.customer_data werden NICHT verändert
|
|
* (steuerrechtliche Aufbewahrungspflicht)
|
|
*/
|
|
export async function anonymizeEndCustomer(id: string): Promise<void> {
|
|
const supabase = await createClient()
|
|
|
|
const { error } = await supabase
|
|
.from('end_customers')
|
|
.update({
|
|
company_name: '[GELÖSCHT]',
|
|
vat_id: '[GELÖSCHT]',
|
|
first_name: '[GELÖSCHT]',
|
|
last_name: '[GELÖSCHT]',
|
|
street: '[GELÖSCHT]',
|
|
zip: '[GELÖSCHT]',
|
|
city: '[GELÖSCHT]',
|
|
email: '[GELÖSCHT]',
|
|
bank_iban: '[GELÖSCHT]',
|
|
bank_bic: '[GELÖSCHT]',
|
|
bank_name: '[GELÖSCHT]',
|
|
bank_owner: '[GELÖSCHT]',
|
|
is_anonymized: true,
|
|
})
|
|
.eq('id', id)
|
|
.eq('is_anonymized', false) // Keine doppelte Anonymisierung
|
|
|
|
if (error) throw error
|
|
|
|
revalidatePath('/my-customers')
|
|
revalidatePath(`/my-customers/${id}`)
|
|
}
|
|
|
|
export async function getPartnerEndCustomers(partnerId: string): Promise<EndCustomer[]> {
|
|
const admin = createAdminClient()
|
|
const { data: dbUser } = await admin
|
|
.from('users')
|
|
.select('company_id')
|
|
.eq('id', partnerId)
|
|
.single()
|
|
|
|
const targetId = dbUser?.company_id || partnerId
|
|
|
|
const { data, error } = await admin
|
|
.from('end_customers')
|
|
.select('*')
|
|
.eq('partner_id', targetId)
|
|
.order('company_name', { ascending: true })
|
|
|
|
if (error) throw error
|
|
return data as EndCustomer[]
|
|
}
|