From 7b6be623aeffd07ce1ba8e0d15dd28845170cc17 Mon Sep 17 00:00:00 2001 From: kovakmedya Date: Thu, 30 Apr 2026 07:22:51 +0300 Subject: [PATCH] feat(banking A): bank accounts module + finance integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First of 3-step banking expansion. Banks tracked separately from customer/supplier debts so we can compute real cash position later. Schema: - New bank_accounts table: bankName, accountName, iban, openingBalance, notes, archived. Indexes on (tenantId, archived). - New column finance_entries.bankAccountId (FK, optional). Index on (tenantId, bankAccountId). - schema.ts: TABLES.bankAccounts, BankAccount type, FinanceEntry gains bankAccountId. Server side: - lib/validation/bank-accounts.ts (Zod): IBAN normalized to upper-case no-spaces; openingBalance defaults to 0. - lib/appwrite/bank-account-actions.ts: create/update/archive(toggle)/ delete with audit. Delete refuses if any finance_entry still references the account; archive toggle replaces it for safe disable. - lib/appwrite/bank-account-queries.ts: * listBankAccounts * getBankAccountBalances — computes opening + Σ(income) − Σ(expense) per account by scanning up to 5000 entries with bankAccountId set. Pure cash flow; debt/receivable don't move balance. * listEntriesForAccount UI: - /finance/banks server page renders BanksClient with computed balances. - BanksClient: card grid for active accounts, collapsed details for archived. Sum card on top showing total active balance (color-coded by sign). Each card shows bank, account name, formatted IBAN, current balance + opening (if drifted). Dropdown: Düzenle / Arşivle / Sil. - BankFormSheet: bank/account/IBAN/openingBalance/notes form. - Finance form gets a bank-account Select (sentinel-stripped). Existing finance entries get a 'bankAccountLabel' subtitle in their row. Sidebar: Finans group expanded with Bankalar submenu (Banka hesapları / Krediler / Kredi kartları). The latter two land in B and C. --- .../banks/components/bank-form-sheet.tsx | 159 ++++++++++ .../finance/banks/components/banks-client.tsx | 292 ++++++++++++++++++ .../finance/banks/components/types.ts | 10 + src/app/(dashboard)/finance/banks/page.tsx | 52 ++++ .../finance/components/finance-client.tsx | 5 +- .../finance/components/finance-form-sheet.tsx | 58 +++- .../(dashboard)/finance/components/types.ts | 3 + src/app/(dashboard)/finance/page.tsx | 15 +- src/components/app-sidebar.tsx | 10 + src/lib/appwrite/bank-account-actions.ts | 244 +++++++++++++++ src/lib/appwrite/bank-account-queries.ts | 93 ++++++ src/lib/appwrite/bank-account-types.ts | 7 + src/lib/appwrite/finance-actions.ts | 1 + src/lib/appwrite/schema.ts | 13 + src/lib/validation/bank-accounts.ts | 28 ++ src/lib/validation/finance.ts | 1 + 16 files changed, 972 insertions(+), 19 deletions(-) create mode 100644 src/app/(dashboard)/finance/banks/components/bank-form-sheet.tsx create mode 100644 src/app/(dashboard)/finance/banks/components/banks-client.tsx create mode 100644 src/app/(dashboard)/finance/banks/components/types.ts create mode 100644 src/app/(dashboard)/finance/banks/page.tsx create mode 100644 src/lib/appwrite/bank-account-actions.ts create mode 100644 src/lib/appwrite/bank-account-queries.ts create mode 100644 src/lib/appwrite/bank-account-types.ts create mode 100644 src/lib/validation/bank-accounts.ts diff --git a/src/app/(dashboard)/finance/banks/components/bank-form-sheet.tsx b/src/app/(dashboard)/finance/banks/components/bank-form-sheet.tsx new file mode 100644 index 0000000..46d633e --- /dev/null +++ b/src/app/(dashboard)/finance/banks/components/bank-form-sheet.tsx @@ -0,0 +1,159 @@ +"use client"; + +import { useActionState, useEffect } from "react"; +import { Loader2, Save } from "lucide-react"; +import { toast } from "sonner"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet"; +import { Textarea } from "@/components/ui/textarea"; +import { + createBankAccountAction, + updateBankAccountAction, +} from "@/lib/appwrite/bank-account-actions"; +import { initialBankAccountState } from "@/lib/appwrite/bank-account-types"; + +import type { BankAccountRow } from "./types"; + +type Props = { + open: boolean; + onOpenChange: (v: boolean) => void; + account?: BankAccountRow | null; +}; + +export function BankFormSheet({ open, onOpenChange, account }: Props) { + const isEdit = Boolean(account); + const action = isEdit ? updateBankAccountAction : createBankAccountAction; + const [state, formAction, isPending] = useActionState(action, initialBankAccountState); + + useEffect(() => { + if (state.ok) { + toast.success(isEdit ? "Hesap güncellendi." : "Hesap eklendi."); + onOpenChange(false); + } else if (state.error) { + toast.error(state.error); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state]); + + return ( + + + + {isEdit ? "Hesabı düzenle" : "Yeni banka hesabı"} + + Açılış bakiyesi sonradan değiştirilirse bütün hareketler aynı kalır, sadece toplam + kayar. + + + +
+ {isEdit && account && } + +
+
+
+ + + {state.fieldErrors?.bankName && ( +

{state.fieldErrors.bankName}

+ )} +
+
+ + + {state.fieldErrors?.accountName && ( +

{state.fieldErrors.accountName}

+ )} +
+
+ +
+ + +
+ +
+ + +

+ Bu hesabı sisteme eklediğinizdeki bakiye. Sonraki hareketler bu rakamın üstüne eklenir. +

+
+ +
+ +