import { useEffect, useState } from "react"; import { InlineNotification, Modal, Select, SelectItem, TextInput } from "@carbon/react"; import { api } from "../lib/api"; import { todayIst } from "../lib/fiscal"; import { formatPaise, paiseToInput, parseRupeesToPaise } from "../lib/money"; import { PAYMENT_MODES, type Payment, type PaymentMode } from "../lib/payments"; import { useReturnFocus } from "../hooks/useReturnFocus"; interface Props { open: boolean; invoiceId: number; invoiceNumber: string; /** What is still owed, in paise. */ balancePaise: number; onClose: () => void; onRecorded: (payment: Payment) => void; } /** "Record payment": cash received plus TDS deducted, validated against the balance before it is sent. */ export default function RecordPaymentModal({ open, invoiceId, invoiceNumber, balancePaise, onClose, onRecorded }: Props) { const [paidOn, setPaidOn] = useState(todayIst()); const [amount, setAmount] = useState(""); const [tds, setTds] = useState(""); const [mode, setMode] = useState("bank_transfer"); const [reference, setReference] = useState(""); const [notes, setNotes] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); useReturnFocus(open); // Each opening starts from the balance, so the common case (paid in full) is one click. useEffect(() => { if (!open) return; setPaidOn(todayIst()); setAmount(paiseToInput(balancePaise)); setTds(""); setMode("bank_transfer"); setReference(""); setNotes(""); setError(null); setBusy(false); }, [open, balancePaise]); const amountPaise = parseRupeesToPaise(amount); const tdsPaise = parseRupeesToPaise(tds); const amountInvalid = amountPaise === null; const tdsInvalid = tdsPaise === null; const total = (amountPaise ?? 0) + (tdsPaise ?? 0); const overBalance = !amountInvalid && !tdsInvalid && total > balancePaise; const empty = !amountInvalid && !tdsInvalid && total === 0; const dateInvalid = !/^\d{4}-\d{2}-\d{2}$/.test(paidOn); const canSubmit = !busy && !amountInvalid && !tdsInvalid && !overBalance && !empty && !dateInvalid; const submit = async () => { if (!canSubmit || amountPaise === null || tdsPaise === null) return; setBusy(true); setError(null); try { const payment = await api.recordPayment({ invoiceId, paidOn, amountPaise, tdsPaise, mode, reference, notes }); onRecorded(payment); } catch (e) { setError(String(e)); setBusy(false); } }; return ( void submit()} >

Balance due: {formatPaise(balancePaise)}

{error ? : null} setPaidOn(e.target.value)} /> setAmount(e.target.value)} /> setTds(e.target.value)} /> setReference(e.target.value)} /> setNotes(e.target.value)} />
); }