diff --git a/src/pages/accounting/AccountingReconcileDetailPage.tsx b/src/pages/accounting/AccountingReconcileDetailPage.tsx index cca3945..73c753a 100644 --- a/src/pages/accounting/AccountingReconcileDetailPage.tsx +++ b/src/pages/accounting/AccountingReconcileDetailPage.tsx @@ -18,7 +18,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table"; -import { ArrowLeft, CheckCircle2, AlertTriangle, FileDown, Search, Loader2, ArrowUp, ArrowDown, ChevronsUpDown } from "lucide-react"; +import { ArrowLeft, CheckCircle2, AlertTriangle, FileDown, Search, Loader2, ArrowUp, ArrowDown, ChevronsUpDown, Plus } from "lucide-react"; import { cn } from "@/lib/utils"; import { toast } from "sonner"; import { money, fmtDate } from "./lib/format"; @@ -87,6 +87,13 @@ export default function AccountingReconcileDetailPage() { const [adjAccount, setAdjAccount] = useState(""); const [adjNote, setAdjNote] = useState("Bank reconciliation adjustment"); + // Add a deposit/withdrawal directly from the reconciliation screen. + const [addOpen, setAddOpen] = useState(false); + const [addSaving, setAddSaving] = useState(false); + const [addTx, setAddTx] = useState<{ type: "credit" | "debit"; date: string; amount: string; description: string; coa_account_id: string; reference: string }>( + { type: "credit", date: "", amount: "", description: "", coa_account_id: "", reference: "" }, + ); + const { data: account } = useQuery({ queryKey: ["account", accountId], enabled: !!accountId, @@ -118,7 +125,7 @@ export default function AccountingReconcileDetailPage() { queryKey: ["accounts", cid], enabled: !!cid, queryFn: async () => - (await accounting.from("accounts").select("id,name,type").eq("company_id", cid).eq("is_archived", false).order("name")).data ?? [], + (await accounting.from("accounts").select("id,name,type,is_bank").eq("company_id", cid).eq("is_archived", false).order("name")).data ?? [], }); const { data: txs = [] } = useQuery({ @@ -317,6 +324,51 @@ export default function AccountingReconcileDetailPage() { setChecked((prev) => new Set(prev).add(data.id)); }; + const openAdd = (type: "credit" | "debit") => { + setAddTx({ + type, + date: active?.statement_end_date ?? new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }), + amount: "", description: "", coa_account_id: "", reference: "", + }); + setAddOpen(true); + }; + + const addTransaction = async () => { + if (!active) return; + const amt = Number(addTx.amount); + if (!amt || amt <= 0) return toast.error("Enter an amount"); + if (!addTx.description.trim()) return toast.error("Enter a description"); + if (!addTx.coa_account_id) return toast.error("Pick a category account"); + setAddSaving(true); + try { + const coaName = (allAccounts as any[]).find((a) => a.id === addTx.coa_account_id)?.name ?? ""; + const { data, error } = await accounting + .from("transactions") + .insert({ + company_id: cid, + account_id: accountId, + date: addTx.date, + description: addTx.description.trim(), + amount: Math.abs(amt), + type: addTx.type, // credit = deposit (money in), debit = withdrawal (money out) + category: coaName, + coa_account_id: addTx.coa_account_id, + reference: addTx.reference.trim() || null, + cleared: true, + }) + .select("id") + .single(); + if (error || !data) return toast.error(error?.message ?? "Failed to add transaction"); + setAddOpen(false); + toast.success(`${addTx.type === "credit" ? "Deposit" : "Withdrawal"} added`); + qc.invalidateQueries({ queryKey: ["recon-txs", accountId] }); + qc.invalidateQueries({ queryKey: ["transactions", cid] }); + setChecked((prev) => new Set(prev).add(data.id)); // auto-clear into this reconciliation + } finally { + setAddSaving(false); + } + }; + if (!associationId) return

Select an association.

; if (companyLoading) return
; if (companyError || !companyId) return

{companyError || "Accounting setup is not ready."}

; @@ -370,6 +422,12 @@ export default function AccountingReconcileDetailPage() { Withdrawals only + + @@ -590,6 +648,60 @@ export default function AccountingReconcileDetailPage() { + {/* Add deposit / withdrawal modal */} + + + + Add {addTx.type === "credit" ? "Deposit" : "Withdrawal"} + + Record a {addTx.type === "credit" ? "deposit (money in)" : "withdrawal (money out)"} on{" "} + {(account as any)?.name ?? "this account"}. It's added to this reconciliation automatically. + + +
+
+
+ + setAddTx({ ...addTx, date: e.target.value })} /> +
+
+ + setAddTx({ ...addTx, amount: e.target.value })} placeholder="0.00" /> +
+
+
+ + setAddTx({ ...addTx, description: e.target.value })} + placeholder={addTx.type === "credit" ? "e.g. Interest earned" : "e.g. Bank service charge"} /> +
+
+ + +
+
+ + setAddTx({ ...addTx, reference: e.target.value })} placeholder="Check # / memo" /> +
+
+ + + + +
+
+ {/* Adjustment modal */}