"use client"; import { useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { RefreshCw } from "lucide-react"; import { accountingApi } from "@/lib/accounting-api"; import { useTenantId } from "@/hooks/useTenantId"; import { formatMoney } from "@/lib/utils"; import { PageHeader, Button, Select, DataTable, EmptyState, LoadingState, ErrorState, Badge, Card, CardContent, StatCard, } from "@/components/ds"; export default function LedgerPage() { const { tenantId } = useTenantId(); const qc = useQueryClient(); const periodsQ = useQuery({ queryKey: ["accounting", tenantId, "fiscal-periods"], queryFn: () => accountingApi.fiscal.listPeriods(tenantId!), enabled: !!tenantId, }); const defaultPeriod = useMemo(() => { const periods = periodsQ.data ?? []; return periods.find((p) => p.is_current)?.id ?? periods[0]?.id ?? ""; }, [periodsQ.data]); const [periodId, setPeriodId] = useState(""); const activePeriodId = periodId || defaultPeriod; const accountsQ = useQuery({ queryKey: ["accounting", tenantId, "accounts"], queryFn: () => accountingApi.accounts.list(tenantId!), enabled: !!tenantId, }); const balancesQ = useQuery({ queryKey: ["accounting", tenantId, "ledger-balances", activePeriodId], queryFn: () => accountingApi.ledger.balances(tenantId!, activePeriodId), enabled: !!tenantId && !!activePeriodId, }); const trialQ = useQuery({ queryKey: ["accounting", tenantId, "trial-balance", activePeriodId], queryFn: () => accountingApi.ledger.trialBalance(tenantId!, activePeriodId), enabled: !!tenantId && !!activePeriodId, }); const recalcM = useMutation({ mutationFn: () => accountingApi.ledger.recalculate(tenantId!, activePeriodId), onSuccess: async (res) => { toast.success(`${res.recalculated_accounts} حساب محاسبه مجدد شد`); await qc.invalidateQueries({ queryKey: ["accounting", tenantId, "ledger-balances", activePeriodId] }); await qc.invalidateQueries({ queryKey: ["accounting", tenantId, "trial-balance", activePeriodId] }); }, onError: (e: Error) => toast.error(e.message), }); const accountMap = useMemo( () => new Map((accountsQ.data ?? []).map((a) => [a.id, `${a.code} — ${a.name}`])), [accountsQ.data] ); const rows = useMemo(() => { const balances = balancesQ.data ?? []; return [...balances] .map((b) => ({ ...b, account_label: accountMap.get(b.account_id) ?? b.account_id, })) .sort((a, b) => a.account_label.localeCompare(b.account_label, "fa")); }, [balancesQ.data, accountMap]); if (!tenantId || periodsQ.isLoading) return ; if (periodsQ.error) { return periodsQ.refetch()} />; } const periodName = periodsQ.data?.find((p) => p.id === activePeriodId)?.name ?? "—"; return (
recalcM.mutate()} > محاسبه مجدد } />
{activePeriodId ? (

دوره انتخاب‌شده: {periodName}

) : null}
{!activePeriodId ? ( ) : ( <>
{trialQ.isLoading ? ( ) : trialQ.error ? ( trialQ.refetch()} /> ) : trialQ.data ? ( <>

تراز آزمایشی

{trialQ.data.is_balanced ? "متوازن" : "نامتوازن"}
) : null}
{balancesQ.isLoading ? ( ) : balancesQ.error ? ( balancesQ.refetch()} /> ) : ( String(r.account_label ?? r.account_id), }, { key: "opening_balance", header: "افتتاحیه", render: (r) => formatMoney(String(r.opening_balance)), }, { key: "debit_total", header: "گردش بدهکار", render: (r) => formatMoney(String(r.debit_total)), }, { key: "credit_total", header: "گردش بستانکار", render: (r) => formatMoney(String(r.credit_total)), }, { key: "closing_balance", header: "اختتامیه", render: (r) => formatMoney(String(r.closing_balance)), }, { key: "current_balance", header: "مانده جاری", render: (r) => formatMoney(String(r.current_balance)), }, ]} rows={rows as unknown as Record[]} empty={ recalcM.mutate()} disabled={recalcM.isPending}> محاسبه مجدد } /> } /> )} )}
); }