mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 21:41:16 +00:00
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
"use server";
|
|
|
|
import * as z from "zod";
|
|
import { revalidatePath } from "next/cache";
|
|
import { createClient } from "@/lib/supabase/server";
|
|
import { verifySession } from "@/lib/supabase/dal";
|
|
import { getClientId } from "@/modules/shared/queries/profile";
|
|
import {
|
|
INVOICE_STATUSES,
|
|
InvoiceDraftSchema,
|
|
firstIssueMessage,
|
|
type InvoiceDraft,
|
|
type InvoiceStatus,
|
|
} from "@/modules/shared/schemas/invoice";
|
|
import { toIsoDate } from "@/modules/shared/lib/invoice-math";
|
|
|
|
export type InvoiceActionState = { error: string } | { success: true };
|
|
|
|
function revalidateBilling(profileId: string) {
|
|
revalidatePath("/client/billing");
|
|
revalidatePath(`/admin/clients/${profileId}/billing`);
|
|
}
|
|
|
|
export async function createInvoice(draft: InvoiceDraft): Promise<InvoiceActionState> {
|
|
const session = await verifySession();
|
|
if (!session.isAdmin) return { error: "Keine Berechtigung." };
|
|
|
|
const validated = InvoiceDraftSchema.safeParse(draft);
|
|
if (!validated.success) return { error: firstIssueMessage(validated.error) };
|
|
|
|
const data = validated.data;
|
|
const clientId = await getClientId(data.profileId);
|
|
if (!clientId) return { error: "Client hat noch keine Stammdaten hinterlegt." };
|
|
|
|
const supabase = await createClient();
|
|
const { error } = await supabase.rpc("create_invoice", {
|
|
p_client_id: clientId,
|
|
p_invoice_date: data.invoiceDate,
|
|
p_due_date: data.dueDate,
|
|
p_company_name: data.companyName,
|
|
p_billing_street: data.billingStreet,
|
|
p_billing_postal_code: data.billingPostalCode,
|
|
p_billing_city: data.billingCity,
|
|
p_billing_country: data.billingCountry.toUpperCase(),
|
|
p_items: data.items.map((item) => ({
|
|
product_template_id: item.productTemplateId,
|
|
description: item.description,
|
|
quantity: item.quantity,
|
|
unit_price: item.unitPrice,
|
|
tax_rate: item.taxRate,
|
|
})),
|
|
});
|
|
|
|
if (error) return { error: "Rechnung konnte nicht erstellt werden." };
|
|
|
|
revalidateBilling(data.profileId);
|
|
return { success: true };
|
|
}
|
|
|
|
const StatusSchema = z.object({
|
|
profileId: z.string().uuid(),
|
|
invoiceId: z.string().uuid(),
|
|
status: z.enum(INVOICE_STATUSES),
|
|
});
|
|
|
|
export async function updateInvoiceStatus(
|
|
profileId: string,
|
|
invoiceId: string,
|
|
status: InvoiceStatus
|
|
): Promise<InvoiceActionState> {
|
|
const session = await verifySession();
|
|
if (!session.isAdmin) return { error: "Keine Berechtigung." };
|
|
|
|
const validated = StatusSchema.safeParse({ profileId, invoiceId, status });
|
|
if (!validated.success) return { error: "Ungültiger Status." };
|
|
|
|
const clientId = await getClientId(validated.data.profileId);
|
|
if (!clientId) return { error: "Status konnte nicht geändert werden." };
|
|
|
|
const supabase = await createClient();
|
|
const { data, error } = await supabase
|
|
.from("invoices")
|
|
.update({
|
|
status: validated.data.status,
|
|
paid_at: validated.data.status === "paid" ? toIsoDate(new Date()) : null,
|
|
})
|
|
.eq("id", validated.data.invoiceId)
|
|
.eq("client_id", clientId)
|
|
.select("id");
|
|
|
|
if (error || !data?.length) return { error: "Status konnte nicht geändert werden." };
|
|
|
|
revalidateBilling(validated.data.profileId);
|
|
return { success: true };
|
|
}
|