feat(finance): income/expense/debt/receivable tracking + summary

Multi-tenant cash flow tracker. All amounts in TRY, decimals preserved.

Schema/validation:
- lib/validation/finance.ts: financeEntrySchema with type enum, positive
  amount, date required, optional customer/invoice link, optional payment
  method.
- lib/appwrite/finance-actions.ts: create/update/delete with audit; date
  HTML input normalized to ISO before write.
- lib/appwrite/finance-queries.ts: listFinanceEntries ordered by date desc.

UI:
- /finance server page passes entries + customers to FinanceClient.
- 5 stat cards: Gelir / Gider / Net (income-expense, color-coded by sign)
  / Alacaklar / Borçlar.
- Type filter dropdown (Tümü/Gelir/Gider/Alacaklar/Borçlar) + global
  search (description/customer/amount).
- 4 quick-add buttons let users start a new entry pre-filled with the
  desired type. Single FinanceFormSheet handles all 4 types via a Select.
- Table: type badge (color-coded), signed amount (+ for income/receivable,
  − for expense/debt), date, customer, payment method label, description
  preview. Row dropdown: Edit / Delete.
- Inline destructive Sil button in Sheet footer when editing.
This commit is contained in:
kovakmedya
2026-04-30 06:04:46 +03:00
parent b4c1073d91
commit 98ab73235f
8 changed files with 1003 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
import { z } from "zod";
export const financeEntrySchema = z.object({
type: z.enum(["income", "expense", "debt", "receivable"]),
amount: z
.union([z.number(), z.string()])
.transform((v) => (typeof v === "string" ? Number(v.replace(",", ".")) : v))
.pipe(z.number().positive("Tutar 0'dan büyük olmalı.")),
date: z.string().min(1, "Tarih zorunlu."),
description: z.string().trim().max(1000).optional().transform((v) => (v ? v : undefined)),
customerId: z.string().optional().transform((v) => (v ? v : undefined)),
invoiceId: z.string().optional().transform((v) => (v ? v : undefined)),
paymentMethod: z
.enum(["cash", "transfer", "card", "check", "other"])
.optional()
.transform((v) => v || undefined),
});
export type FinanceEntryInput = z.infer<typeof financeEntrySchema>;