since3-portal/modules/shared/actions/stammdaten.ts
2026-09-28 15:53:52 +02:00

178 lines
6.4 KiB
TypeScript

"use server";
import * as z from "zod";
import { revalidatePath } from "next/cache";
import { createAdminClient } from "@/lib/supabase/admin";
import { verifySession } from "@/lib/supabase/dal";
const StammdatenSchema = z.object({
profileId: z.string().uuid(),
companyName: z.string().min(1, { error: "Firmenname ist erforderlich." }),
legalForm: z.string().min(1, { error: "Rechtsform ist erforderlich." }),
industry: z.string().optional(),
vatId: z.string().optional(),
taxNumber: z.string().optional(),
commercialRegisterNumber: z.string().optional(),
street: z.string().min(1, { error: "Straße ist erforderlich." }),
postalCode: z.string().min(1, { error: "PLZ ist erforderlich." }),
city: z.string().min(1, { error: "Stadt ist erforderlich." }),
country: z.string().length(2).default("DE"),
billingStreet: z.string().optional(),
billingPostalCode: z.string().optional(),
billingCity: z.string().optional(),
billingCountry: z.string().optional(),
contactName: z.string().min(1, { error: "Ansprechpartner ist erforderlich." }),
contactPosition: z.string().optional(),
contactEmail: z.email({ error: "Bitte eine gültige Email eingeben." }),
contactPhone: z.string().optional(),
paymentMethod: z.enum(["invoice", "sepa_debit", "card"]).default("invoice"),
accountHolder: z.string().optional(),
iban: z.string().optional(),
bic: z.string().optional(),
});
export type StammdatenState = { error: string } | { success: true } | undefined;
function emptyToUndefined(value: FormDataEntryValue | null) {
const stringValue = String(value ?? "").trim();
return stringValue === "" ? undefined : stringValue;
}
export async function saveStammdaten(
_prevState: StammdatenState,
formData: FormData
): Promise<StammdatenState> {
const session = await verifySession();
const profileId = String(formData.get("profileId") ?? "");
if (!session.isAdmin && session.userId !== profileId) {
return { error: "Keine Berechtigung." };
}
const validatedFields = StammdatenSchema.safeParse({
profileId,
companyName: emptyToUndefined(formData.get("companyName")),
legalForm: emptyToUndefined(formData.get("legalForm")),
industry: emptyToUndefined(formData.get("industry")),
vatId: emptyToUndefined(formData.get("vatId")),
taxNumber: emptyToUndefined(formData.get("taxNumber")),
commercialRegisterNumber: emptyToUndefined(formData.get("commercialRegisterNumber")),
street: emptyToUndefined(formData.get("street")),
postalCode: emptyToUndefined(formData.get("postalCode")),
city: emptyToUndefined(formData.get("city")),
country: emptyToUndefined(formData.get("country")) ?? "DE",
billingStreet: emptyToUndefined(formData.get("billingStreet")),
billingPostalCode: emptyToUndefined(formData.get("billingPostalCode")),
billingCity: emptyToUndefined(formData.get("billingCity")),
billingCountry: emptyToUndefined(formData.get("billingCountry")),
contactName: emptyToUndefined(formData.get("contactName")),
contactPosition: emptyToUndefined(formData.get("contactPosition")),
contactEmail: emptyToUndefined(formData.get("contactEmail")),
contactPhone: emptyToUndefined(formData.get("contactPhone")),
paymentMethod: emptyToUndefined(formData.get("paymentMethod")) ?? "invoice",
accountHolder: emptyToUndefined(formData.get("accountHolder")),
iban: emptyToUndefined(formData.get("iban")),
bic: emptyToUndefined(formData.get("bic")),
});
if (!validatedFields.success) {
const { fieldErrors } = z.flattenError(validatedFields.error);
const firstError = Object.values(fieldErrors).flat()[0];
return { error: firstError ?? "Bitte alle Pflichtfelder ausfüllen." };
}
const {
billingStreet,
billingPostalCode,
billingCity,
billingCountry,
paymentMethod,
accountHolder,
iban,
bic,
...data
} = validatedFields.data;
const supabase = createAdminClient();
const { data: profile, error: profileError } = await supabase
.from("profiles")
.select("client_id")
.eq("id", data.profileId)
.single();
if (profileError || !profile) {
return { error: "Profil konnte nicht geladen werden." };
}
const clientPayload = {
company_name: data.companyName,
legal_form: data.legalForm,
industry: data.industry ?? null,
vat_id: data.vatId ?? null,
tax_number: data.taxNumber ?? null,
commercial_register_number: data.commercialRegisterNumber ?? null,
street: data.street,
postal_code: data.postalCode,
city: data.city,
country: data.country,
billing_street: billingStreet ?? null,
billing_postal_code: billingPostalCode ?? null,
billing_city: billingCity ?? null,
billing_country: billingCountry ?? null,
contact_name: data.contactName,
contact_position: data.contactPosition ?? null,
contact_email: data.contactEmail,
contact_phone: data.contactPhone ?? null,
};
let clientId = profile.client_id;
if (clientId) {
const { error } = await supabase.from("clients").update(clientPayload).eq("id", clientId);
if (error) return { error: "Stammdaten konnten nicht gespeichert werden." };
} else {
const { data: newClient, error } = await supabase
.from("clients")
.insert(clientPayload)
.select("id")
.single();
if (error || !newClient) return { error: "Stammdaten konnten nicht gespeichert werden." };
clientId = newClient.id;
const { error: linkError } = await supabase
.from("profiles")
.update({ client_id: clientId })
.eq("id", data.profileId);
if (linkError) return { error: "Verknüpfung mit Profil fehlgeschlagen." };
}
const { error: paymentError } = await supabase.from("client_payment_details").upsert({
client_id: clientId,
payment_method: paymentMethod,
account_holder: accountHolder ?? null,
iban: iban ?? null,
bic: bic ?? null,
});
if (paymentError) {
return { error: "Zahlungsdaten konnten nicht gespeichert werden." };
}
revalidatePath("/client/data");
revalidatePath(`/admin/clients/${data.profileId}/data`);
return { success: true };
}