feat(transactions): ek bağlama, hesap ekstresi ve mutabakat servisi, 004 migration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 07:55:04 +03:00
parent 2e1f4d7929
commit 2e2e33233f
15 changed files with 1049 additions and 22 deletions
+31
View File
@@ -5,6 +5,7 @@ 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,
@@ -17,6 +18,7 @@ import {
type TransactionDetail,
type TransactionFormOptions,
} from "./service";
import { previewReconciliation, reconcileAccount, type ReconcilePreview } from "./statement";
const idSchema = z.string().regex(/^[a-f0-9]{24}$/i);
@@ -103,3 +105,32 @@ export async function searchPayeesAction(query: unknown): Promise<PayeeSuggestio
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;
}