// src/pages/DashboardPage/BillingPage/BillingInvoicesPage.tsx // Billing workspace v2-1: the Invoices nav page - extracted from the former BillingPage's "Invoices" tab. // Renders the org-wide InvoiceList plus a canManage('billing')-gated "New Invoice" action that opens the // InvoiceEditor with no `patientId` (create-only from Billing, so the editor shows its own patient-search // picker rather than locking to a patient context). The editor's onSaved pushes the created/issued invoice // straight into the shared invoices Paginator via `actions.addItem` - the FHIR search index lags a fresh // POST, so a refetch here could miss it. The PaginatorServiceProvider is app-wide, so // usePaginatorServiceContext works here exactly as it did on BillingPage; live invoice updates are handled // inside InvoiceList itself (it subscribes to the invoice broadcasts directly). import { useState } from 'react'; import type { Invoice } from 'fhir/r4'; import { useStaffPermissionServiceContext, usePaginatorServiceContext } from '../../../services'; import { DismissibleAlert } from '../../../components/ui/dismissible-alert'; import InvoiceEditor from './InvoiceEditor'; import InvoiceList from './InvoiceList'; /** The invoice's own number, which is what a biller calls it - never the FHIR id. */ const invoiceNumber = (invoice: Invoice): string | undefined => invoice.identifier?.find((id) => typeof id.value === 'string' && id.value)?.value; const BillingInvoicesPage = () => { const { canManage } = useStaffPermissionServiceContext(); const { actions } = usePaginatorServiceContext(); const [editorOpen, setEditorOpen] = useState(false); // WHAT JUST HAPPENED, BECAUSE NOTHING SAID SO. Issuing an invoice closed the dialog and left the // biller looking at a list - no confirmation, no number, and (until the editor stopped invalidating // the paginator) not even the row. Saying nothing after an act that bills a patient is the same // defect as a refusal that renders as nothing, wearing the other sign. const [justSaved, setJustSaved] = useState(); const canManageBilling = canManage('billing'); return (

Invoices

{canManageBilling && ( )}
{justSaved && ( setJustSaved(undefined)}> {justSaved} )}
{ // Push the created/issued invoice straight into the shared invoices Paginator so it // shows immediately - the FHIR search index lags a fresh POST, so a refetch here could // miss it (the same lag HealthcareService polls around). This editor is create-only // from Billing, so a plain prepend can't duplicate an existing row. actions.addItem(saved); setEditorOpen(false); // The NUMBER, not just "saved": it is what the biller will quote to the patient, and // it is the one thing on the screen that proves this is the invoice they just made // rather than one that was already there. const number = invoiceNumber(saved); setJustSaved( saved.status === 'issued' ? `Invoice ${number ?? ''} issued.`.replace(' ', ' ') : `Invoice ${number ?? ''} saved as a draft.`.replace(' ', ' '), ); }} />
); }; export default BillingInvoicesPage;