mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
Reconciliation: require a vendor on manual withdrawals
The Add Deposit/Withdrawal dialog on the reconciliation screen now shows a required vendor dropdown for withdrawals (debits) and stores vendor_id on the transaction, matching the vendor-required rule on Bills/Expenses/Banking. Deposits (credits) are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -90,8 +90,8 @@ export default function AccountingReconcileDetailPage() {
|
||||
// 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 [addTx, setAddTx] = useState<{ type: "credit" | "debit"; date: string; amount: string; description: string; coa_account_id: string; reference: string; vendor_id: string }>(
|
||||
{ type: "credit", date: "", amount: "", description: "", coa_account_id: "", reference: "", vendor_id: "" },
|
||||
);
|
||||
|
||||
const { data: account } = useQuery({
|
||||
@@ -128,6 +128,13 @@ export default function AccountingReconcileDetailPage() {
|
||||
(await accounting.from("accounts").select("id,name,type,is_bank").eq("company_id", cid).eq("is_archived", false).order("name")).data ?? [],
|
||||
});
|
||||
|
||||
const { data: vendors = [] } = useQuery({
|
||||
queryKey: ["vendors-lookup", cid],
|
||||
enabled: !!cid,
|
||||
queryFn: async () =>
|
||||
(await accounting.from("vendors").select("id,name").eq("company_id", cid).order("name")).data ?? [],
|
||||
});
|
||||
|
||||
const { data: txs = [] } = useQuery({
|
||||
queryKey: ["recon-txs", accountId, active?.statement_end_date, priorReconDate],
|
||||
enabled: !!accountId && !!active,
|
||||
@@ -328,7 +335,7 @@ export default function AccountingReconcileDetailPage() {
|
||||
setAddTx({
|
||||
type,
|
||||
date: active?.statement_end_date ?? new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }),
|
||||
amount: "", description: "", coa_account_id: "", reference: "",
|
||||
amount: "", description: "", coa_account_id: "", reference: "", vendor_id: "",
|
||||
});
|
||||
setAddOpen(true);
|
||||
};
|
||||
@@ -339,6 +346,7 @@ export default function AccountingReconcileDetailPage() {
|
||||
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");
|
||||
if (addTx.type === "debit" && !addTx.vendor_id) return toast.error("Vendor is required for withdrawals");
|
||||
setAddSaving(true);
|
||||
try {
|
||||
const coaName = (allAccounts as any[]).find((a) => a.id === addTx.coa_account_id)?.name ?? "";
|
||||
@@ -353,6 +361,7 @@ export default function AccountingReconcileDetailPage() {
|
||||
type: addTx.type, // credit = deposit (money in), debit = withdrawal (money out)
|
||||
category: coaName,
|
||||
coa_account_id: addTx.coa_account_id,
|
||||
vendor_id: addTx.type === "debit" ? (addTx.vendor_id || null) : null,
|
||||
reference: addTx.reference.trim() || null,
|
||||
cleared: true,
|
||||
})
|
||||
@@ -687,6 +696,20 @@ export default function AccountingReconcileDetailPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{addTx.type === "debit" && (
|
||||
<div>
|
||||
<Label>Vendor <span className="text-destructive">*</span></Label>
|
||||
<Select value={addTx.vendor_id} onValueChange={(v) => setAddTx({ ...addTx, vendor_id: v })}>
|
||||
<SelectTrigger><SelectValue placeholder="Select vendor" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{(vendors as any[]).map((v: any) => <SelectItem key={v.id} value={v.id}>{v.name}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{(vendors as any[]).length === 0 && (
|
||||
<p className="mt-1 text-xs text-muted-foreground">No vendors yet — add one on the Vendors page first.</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Label>Reference (optional)</Label>
|
||||
<Input value={addTx.reference} onChange={(e) => setAddTx({ ...addTx, reference: e.target.value })} placeholder="Check # / memo" />
|
||||
|
||||
Reference in New Issue
Block a user