Files
butcem/src/features/transactions/actions.ts
T

137 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { tr } from "@/i18n/tr";
import { fail, ok, type ActionResult } from "@/lib/action-result";
import { getHouseholdActionContext } from "@/lib/dal";
import { reconcileSchema } from "@/shared/schemas/reconciliation";
import { transactionFormSchema } from "@/shared/schemas/transaction";
import {
createTransaction,
getTransaction,
getTransactionFormOptions,
searchPayees,
setTransactionDeleted,
updateTransaction,
type PayeeSuggestion,
type TransactionDetail,
type TransactionFormOptions,
} from "./service";
import { previewReconciliation, reconcileAccount, type ReconcilePreview } from "./statement";
const idSchema = z.string().regex(/^[a-f0-9]{24}$/i);
/** zod hatalarını alan bazlı mesajlara çevirir ("splits.1.amount" gibi yollar korunur). */
function fieldErrors(error: z.ZodError) {
const out: Record<string, string> = {};
for (const issue of error.issues) {
const key = issue.path.join(".");
if (key && !out[key]) out[key] = issue.message;
}
return out;
}
function revalidate() {
// Bakiye, liste ve (ileride) dashboard aynı veriden beslenir.
revalidatePath("/", "layout");
}
export async function createTransactionAction(
input: unknown,
): Promise<ActionResult<{ id: string }>> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const parsed = transactionFormSchema.safeParse(input);
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
const result = await createTransaction(ctx.user.id, ctx.household.id, parsed.data);
if (result.ok) revalidate();
return result;
}
export async function updateTransactionAction(
transactionId: unknown,
input: unknown,
): Promise<ActionResult> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const id = idSchema.safeParse(transactionId);
if (!id.success) return fail(tr.common.notFound);
const parsed = transactionFormSchema.safeParse(input);
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
const result = await updateTransaction(ctx.user.id, ctx.household.id, id.data, parsed.data);
if (result.ok) revalidate();
return result;
}
export async function setTransactionDeletedAction(
transactionId: unknown,
deleted: unknown,
): Promise<ActionResult> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const id = idSchema.safeParse(transactionId);
const d = z.boolean().safeParse(deleted);
if (!id.success || !d.success) return fail(tr.common.genericError);
const result = await setTransactionDeleted(ctx.user.id, ctx.household.id, id.data, d.data);
if (result.ok) revalidate();
return result;
}
/** Form açılırken: yazılabilir hesaplar ve kategoriler (hızlı ekle her ekrandan çağırır). */
export async function getTransactionFormOptionsAction(): Promise<
ActionResult<TransactionFormOptions>
> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const options = await getTransactionFormOptions(ctx.user.id, ctx.household.id);
return options ? ok(options) : fail(tr.common.unauthorized);
}
export async function getTransactionAction(
transactionId: unknown,
): Promise<ActionResult<TransactionDetail>> {
const ctx = await getHouseholdActionContext("viewer");
if (!ctx) return fail(tr.common.unauthorized);
const id = idSchema.safeParse(transactionId);
if (!id.success) return fail(tr.common.notFound);
const detail = await getTransaction(ctx.user.id, ctx.household.id, id.data);
return detail ? ok(detail) : fail(tr.common.notFound);
}
export async function searchPayeesAction(query: unknown): Promise<PayeeSuggestion[]> {
const ctx = await getHouseholdActionContext("member");
const q = z.string().max(80).safeParse(query);
if (!ctx || !q.success) return [];
return searchPayees(ctx.user.id, ctx.household.id, q.data);
}
/** Mutabakat önizlemesi: ekstre bakiyesi ile uygulama bakiyesi arasındaki fark. */
export async function previewReconciliationAction(
accountId: unknown,
input: unknown,
): Promise<ActionResult<ReconcilePreview>> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const id = idSchema.safeParse(accountId);
if (!id.success) return fail(tr.common.notFound);
const parsed = reconcileSchema.safeParse(input);
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
return previewReconciliation(ctx.user.id, ctx.household.id, id.data, parsed.data);
}
export async function reconcileAccountAction(
accountId: unknown,
input: unknown,
): Promise<ActionResult<{ adjustmentId: string | null; markedCount: number }>> {
const ctx = await getHouseholdActionContext("member");
if (!ctx) return fail(tr.common.unauthorized);
const id = idSchema.safeParse(accountId);
if (!id.success) return fail(tr.common.notFound);
const parsed = reconcileSchema.safeParse(input);
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
const result = await reconcileAccount(ctx.user.id, ctx.household.id, id.data, parsed.data);
if (result.ok) revalidate();
return result;
}