import { useAtomValue, useSetAtom } from "jotai" import { bankRecClosingBalanceAtom, bankRecDateAtom, SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms" import { FrappeConfig, FrappeContext, useFrappeGetDocCount, useFrappeGetDocList, useFrappePostCall, useSWRConfig } from "frappe-react-sdk" import { BankTransaction } from "@/types/Accounts/BankTransaction" import { Progress } from "@/components/ui/progress" import { useGetAccountClosingBalance, useGetAccountClosingBalanceAsPerStatement, useGetAccountOpeningBalance, useGetUnreconciledTransactions } from "./utils" import { flt, formatCurrency } from "@/lib/numbers" import { Skeleton } from "@/components/ui/skeleton" import { StatContainer, StatLabel, StatValue } from "@/components/ui/stats" import { Edit, Info, Trash2 } from "lucide-react" import { H4, Paragraph } from "@/components/ui/typography" import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card" import { getCompanyCurrency } from "@/lib/company" import _ from "@/lib/translate" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { formatDate } from "@/lib/date" import { Form } from "@/components/ui/form" import { CurrencyFormField } from "@/components/ui/form-elements" import { useForm } from "react-hook-form" import { Button } from "@/components/ui/button" import { useContext, useState } from "react" import { Separator } from "@/components/ui/separator" import { BankAccountBalance } from "@/types/Accounts/BankAccountBalance" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { toast } from "sonner" import ErrorBanner from "@/components/ui/error-banner" const BankBalance = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) if (!bankAccount) { return null } return (
) } const OpeningBalance = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) const { data, isLoading } = useGetAccountOpeningBalance() return {_("Opening Balance")} {isLoading ? : {formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}} } const ClosingBalance = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) const { data, isLoading } = useGetAccountClosingBalance() return (
{_("Closing Balance as per system")}

{_("Closing balance as per system")}

{_("This is what the system expects the closing balance to be in your bank statement.")}
{_("It takes into account all the transactions that have been posted and subtracts the transactions that have not cleared yet.")}
{_("If your bank statement shows a different closing balance, it is because all transactions have not reconciled yet.")}

For more information, click on the Bank Reconciliation Statement tab below.
{isLoading ? : {formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}}
) } const Difference = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) const { data, isLoading } = useGetAccountClosingBalance() const value = useAtomValue(bankRecClosingBalanceAtom(bankAccount?.name ?? '')) const difference = flt(value.value - (data?.message ?? 0)) const isError = difference !== 0 return {_("Difference")} {isLoading ? : {formatCurrency(difference, bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? '')) }} } const ReconcileProgress = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) const dates = useAtomValue(bankRecDateAtom) const { data: totalCount } = useFrappeGetDocCount('Bank Transaction', [ ["bank_account", "=", bankAccount?.name ?? ''], ['docstatus', '=', 1], ['date', '<=', dates?.toDate], ['date', '>=', dates?.fromDate] ], false, undefined, { revalidateOnFocus: false }) const { data: unreconciledTransactions, } = useGetUnreconciledTransactions() const reconciledCount = (totalCount ?? 0) - (unreconciledTransactions?.message?.length ?? 0) const progress = (totalCount ? reconciledCount / totalCount : 0) * 100 return
} const ClosingBalanceAsPerStatement = () => { const bankAccount = useAtomValue(selectedBankAccountAtom) const dates = useAtomValue(bankRecDateAtom) const setValue = useSetAtom(bankRecClosingBalanceAtom(bankAccount?.name ?? '')) const { data, isLoading } = useGetAccountClosingBalanceAsPerStatement({ onSuccess: (data) => { if (data?.message && data?.message?.balance) { setValue({ value: data?.message?.balance, stringValue: data?.message?.balance.toString() }) } } }) const isDateSame = data?.message?.date === dates.toDate const [isOpen, setIsOpen] = useState(false) return {_("Closing Balance as per statement")}
{isLoading ? : {formatCurrency(flt(data?.message?.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}}
{_("Click to set the closing balance as per statement")}
setIsOpen(false)} />
{!isDateSame && data?.message.date && {_("As of {0}", [formatDate(data?.message?.date ?? '', 'Do MMM YYYY')])}}
} const ClosingBalanceForm = ({ defaultBalance, date, bankAccount, onClose }: { defaultBalance: number, date: string, bankAccount: SelectedBank | null, onClose: VoidFunction }) => { const { mutate } = useSWRConfig() const form = useForm<{ balance: number }>({ defaultValues: { balance: defaultBalance } }) const setValue = useSetAtom(bankRecClosingBalanceAtom(bankAccount?.name ?? '')) const { call, loading, error } = useFrappePostCall("erpnext.accounts.doctype.bank_account.bank_account.set_closing_balance_as_per_statement") const onSubmit = (data: { balance: number }) => { if (data.balance) { call({ bank_account: bankAccount?.name ?? '', date: date, balance: data.balance }) .then(() => { // Mutate the closing balance as per statement mutate(`bank-reconciliation-account-closing-balance-as-per-statement-${bankAccount?.name}-${date}`) setValue({ value: data.balance, stringValue: data.balance.toString() }) toast.success(_("Closing balance set.")) onClose() }) } else { toast.error(_("Closing balance is required.")) } } const currency = bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? '') return
{_("Set closing balance as per bank statement")} {_("Enter the closing balance you see in your bank statement for {0} as of the {1}", [bankAccount?.account_name ?? bankAccount?.name ?? '', formatDate(date, 'Do MMM YYYY')])} {error && }
} const ClosingBalancesList = ({ bankAccount, date }: { bankAccount: SelectedBank | null, date: string }) => { const { data, mutate } = useFrappeGetDocList("Bank Account Balance", { filters: [["bank_account", "=", bankAccount?.name ?? ''], ["date", "<=", date]], orderBy: { field: "date", order: "desc" }, fields: ["date", "balance", "name"], limit: 10 }) const { db } = useContext(FrappeContext) as FrappeConfig const onDelete = (name: string) => { toast.promise(db.deleteDoc("Bank Account Balance", name).then(() => { mutate() }), { loading: _("Deleting closing balance..."), success: _("Closing balance deleted."), error: _("Failed to delete closing balance.") }) } if (data?.length === 0) { return null } return

{_("Balances as per bank statement before {0}", [formatDate(date, 'Do MMM YYYY')])}

{_("Date")} {_("Balance")} {data?.map((item) => ( {formatDate(item.date, 'Do MMM YYYY')} {formatCurrency(flt(item.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))} ))}
} export default BankBalance