// src/pages/DashboardPage/BillingPage/AllowPartialPaymentsSettingsSection.tsx // Billing v2-2 follow-on (Task 5b): the org "Allow partial payments" house default, living in // BillingSettingsTab next to the tax rate + default payment terms. Mirrors InvoiceTermsSettingsSection's // save pattern - seed from the org, edit locally, PUT the SAME billing:manage // `organization-invoice-terms` endpoint (Task 5a extended it to also carry `allowPartialPayments`), then // push the returned Organization back into auth via setWpOrganization. The endpoint requires `netDays` on // every request, so this section resends the org's current (unchanged) net days alongside the toggle - // it never touches the terms value itself. Radix ui/* + Tailwind, zero new WordPress. import { useEffect, useRef, useState } from 'react'; import { AlertCircle, CheckCircle2, Loader2 } from 'lucide-react'; import { useOrganizationServiceContext } from '../../../services/OrganizationService.tsx'; import { Label } from '../../../components/ui/label.tsx'; import { Checkbox } from '../../../components/ui/checkbox.tsx'; import { Alert, AlertDescription } from '../../../components/ui/alert.tsx'; import { getDefaultNetDays, getOrgAllowPartialPayments } from '../../../utils/organizationInvoiceTerms'; import { relayServerFailure } from '../../../utils/fetchFailureMessage'; import { READ_BACK_FAILED, useBillingDefaultSave } from './useBillingDefaultSave'; const AllowPartialPaymentsSettingsSection = () => { const { updateInvoiceTermsSettings } = useOrganizationServiceContext(); const [allowPartialPayments, setAllowPartialPayments] = useState(false); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(); const [saveSuccess, setSaveSuccess] = useState(false); // Seeded from the practice on screen whenever it, or the person signed in, changes; whatever was saving is retired. const seed = useRef(() => undefined as void); const { organization, practiceId, save, readBack, unsettled, checksHere } = useBillingDefaultSave('organization-invoice-terms', 'partial-payments', () => seed.current()); seed.current = () => { setAllowPartialPayments(getOrgAllowPartialPayments(organization ?? undefined)); setSaving(false); setSaveError(undefined); setSaveSuccess(false); }; useEffect(() => { seed.current(); }, [practiceId]); const handleSave = async () => { setSaving(true); setSaveError(undefined); setSaveSuccess(false); // The route needs net days with the toggle; they are the practice's as saved when this save's turn comes. const result = await save((transport, current) => updateInvoiceTermsSettings({ netDays: getDefaultNetDays(current ?? undefined), allowPartialPayments, }, transport)); if (result.outcome === 'retired') return; setSaving(false); if (result.outcome === 'saved') { setSaveSuccess(true); } else if (result.outcome !== 'unconfirmed' && result.outcome !== 'held') { console.error('Error saving partial payments setting:', result.error); setSaveError(relayServerFailure(result.error, 'Failed to save partial payments setting. Please try again.')); } }; const checkSaved = async () => { setSaving(true); const fresh = await readBack(); if (fresh === 'retired') return; setSaving(false); if (!fresh) { setSaveError(READ_BACK_FAILED); return; } setAllowPartialPayments(getOrgAllowPartialPayments(fresh)); setSaveError(undefined); }; return (

Allow partial payments

When on, staff can allow partial payment per invoice, and patients can pay less than the full balance on those invoices.

{(saveError || unsettled) && ( {saveError ?? unsettled} )} {saveSuccess && ( Partial payments setting saved. )}
{ setAllowPartialPayments(checked === true); setSaveSuccess(false); setSaveError(undefined); }} />
{checksHere && ( )}
); }; export default AllowPartialPaymentsSettingsSection;