# Billing-to-Invoice Implementation Blueprint

> **Scope:** Full flow from adding patient bills → viewing/managing bill items → generating invoices → invoice listing → invoice detail (payment settlement & printing).
>
> **Excluded:** HMO pre-auth, Contacts, Contract Multipliers.

---

## Table of Contents

1. [High-Level Flow Overview](#1-high-level-flow-overview)
2. [Data Model & Types](#2-data-model--types)
3. [Backend API Endpoints](#3-backend-api-endpoints)
4. [Page 1 – Accountant Dashboard](#4-page-1--accountant-dashboard)
5. [Page 2 – Patient Bills List (AccountantBill)](#5-page-2--patient-bills-list-accountantbill)
6. [Page 3 – Add New Patient Bill](#6-page-3--add-new-patient-bill)
7. [Modal – Bill Form (View/Generate Invoice)](#7-modal--bill-form-viewgenerate-invoice)
8. [Page 4 – Invoice List](#8-page-4--invoice-list)
9. [Page 5 – Invoice Details (Payment & Settlement)](#9-page-5--invoice-details-payment--settlement)
10. [Print / Receipt Modal](#10-print--receipt-modal)
11. [Payment Method Modal](#11-payment-method-modal)
12. [Reducing Balance Algorithm](#12-reducing-balance-algorithm)
13. [Frontend Routing](#13-frontend-routing)
14. [State Management & Data Fetching](#14-state-management--data-fetching)
15. [Utility Functions](#15-utility-functions)

---

## 1. High-Level Flow Overview

```
┌──────────────────────┐
│ Accountant Dashboard │
│  (stats & overview)  │
└──────────┬───────────┘
           │ Click "Revenue" / "Unpaid Invoice" / "Invoice Created" cards
           ▼
┌──────────────────────────────┐         ┌────────────────────────┐
│ Patient Bills List           │────────►│ Add New Patient Bill   │
│ (/accountant-bills)          │ "Add"   │ (/add-patient-bill)    │
└──────────┬───────────────────┘         └──────────┬─────────────┘
           │ "View" (opens modal)                   │ Adds bill items via:
           ▼                                        │  • Laboratory referral
┌──────────────────────────┐                        │  • Radiology referral
│ BillFormModal            │                        │  • Pharmacy referral
│ (View items, select,     │                        │  • Procedure (direct)
│  delete, generate        │                        │
│  invoice)                │                        │ "Proceed" → Submits
└──────────┬───────────────┘                        │   procedure bills
           │ "Proceed" → Generate Invoice           │   & navigates back
           ▼                                        │
┌──────────────────────────┐                        │
│ Invoice List             │◄───────────────────────┘
│ (/invoice)               │
└──────────┬───────────────┘
           │ "View" → navigate to /invoice/:id
           ▼
┌──────────────────────────────────────┐
│ Invoice Details                      │
│ (/invoice/:invoiceId)                │
│                                      │
│  • View items with amounts           │
│  • Enter payment amount              │
│  • Reducing balance preview          │
│  • Select payment method (modal)     │
│  • Settle invoice                    │
│  • Print receipt (modal)             │
└──────────────────────────────────────┘
```

---

## 2. Data Model & Types

### 2.1 Patient

```typescript
interface IPatient {
  id: number
  hospital_id: number
  branch_id: number | null
  file_number: string
  first_name: string
  middle_name: string
  last_name: string
  name: string                    // computed full name
  date_of_birth: string
  email: string
  phone: string
  address: string
  gender: { id: number; name: string } | null
  title: { id: number; name: string } | null
  created_at: string
  updated_at: string
}
```

### 2.2 Bill Item (Catalog)

A sellable service/procedure item in the hospital's catalog.

```typescript
interface BillItem {
  id: number
  hospital_id: number
  branch_id: number | null
  bill_group_id: number | string | null
  name: string
  purchase_price: number
  selling_price: number
  status: number
  is_active: number
  bill_group: IBillGroupData | null    // e.g. "Laboratory", "Consultation"
  created_at: string
  updated_at: string
}
```

### 2.3 Bill Group

Groups bill items into categories (e.g., Laboratory, Radiology, Pharmacy, Procedure).

```typescript
interface IBillGroupData {
  id: number | string
  hospital_id: number | string
  bank_id: number | string
  name: string
  bank_name?: string
  account_number: string
  account_name: string
  is_active: number
}
```

### 2.4 Patient Bill Item Pivot

Represents a bill item assigned to a specific patient (pre-invoice stage).

```typescript
interface IPatientBillItemPivot {
  id: number
  patient_id: number
  bill_item_id: number
  quantity: number
  invoice_generated: number       // 0 = not yet invoiced, 1 = invoice generated
  payment_status: number          // FK to payment_statuses
  cbs_settlement: boolean
  final_amount: number            // computed: (custom_amount || selling_price) * qty - discount - waiver
  created_at: string
  updated_at: string
  bill_item: {
    id: number
    name: string
    selling_price: number
    bill_group: IBillGroupData | null
  }
}
```

### 2.5 Patient Bill (Aggregate)

A patient record enriched with their bill items.

```typescript
interface IPatientBill {
  id: number
  file_number: string
  name: string
  gender: { id: number; name: string } | null
  unbilled_total: number
  bill_items_pivot: IPatientBillItemPivot[]
}
```

### 2.6 Invoice

```typescript
interface IInvoice {
  id: number
  patient_id: number
  invoice_number: string
  invoice_date: string
  patient_name: string
  description: string
  total: number
  amount_due: number
  amount_paid: number
  payment_status: number          // FK to payment_statuses table
  payment_method: string
  payment_url: string | null      // for online payment redirect
  reference: string | null
  processed_by: string | null
  cbs_settlement: boolean
  created_at: string
}
```

### 2.7 Invoice Item (Detail Line)

```typescript
interface InvoiceItem {
  id: number
  invoice_id: number
  bill_item_id: number
  amount: number
  quantity: string
  bill_item: string               // item name
  name: string
  payment_status: number
  payment_status_name: string
  amount_paid: number
  payment_channel: string | null
  payment_date: string | null
  created_at: string
}
```

### 2.8 Payment Statuses (Lookup)

```typescript
// Fetched from /common/payment-statuses
// Returns array of { id: number; name: string }
// Common values: "Awaiting Payment", "Paid", "Partial"
```

### 2.9 Settle Invoice Request

```typescript
interface SettleInvoiceRequest {
  invoice_number: string
  items: { item_id: string; amount: string }[]
  payment_method: string            // "POS" | "CASH" | "Online Payment" | "Bank Transfer" | "wallet"
  transaction_id: string            // auto-generated or POS receipt number
  channel: string                   // e.g. "monnify"
  // Optional per payment method:
  pos_type?: 'internal' | 'external'
  bank_name?: string
  account_number?: string
  account_name?: string
  bank_id?: number
}
```

### 2.10 Medical Test / Radiology Test

```typescript
interface MedicalTest {
  id: number
  name: string
  description: string
  price: number
  percentage_discount: number
}

// RadiologyTest has the same shape
```

### 2.11 Medication

```typescript
interface Medication {
  id: number
  name: string
  description?: string
  price: number
  unit?: string
  strength?: string
  qty?: number                      // stock quantity
  stock_keeping_unit?: number       // SKU divisor for quantity calc
}
```

---

## 3. Backend API Endpoints

### 3.1 Patient Endpoints

| Method | Endpoint | Purpose | Request | Response |
|--------|----------|---------|---------|----------|
| `GET` | `/admin/patient/list?per_page=N&page=N&search=query` | Search & list patients | Query params | `{ status, response: { data: IPatient[], current_page, total } }` |

### 3.2 Bill Catalog Endpoints

| Method | Endpoint | Purpose | Request | Response |
|--------|----------|---------|---------|----------|
| `GET` | `/admin/bills/list?per_page=N&page=N&type=clinic&search=query` | List available bill items (procedures) | Query params | `{ status, response: { data: BillItem[], current_page, total } }` |

### 3.3 Patient Bill Endpoints

| Method | Endpoint | Purpose | Request | Response |
|--------|----------|---------|---------|----------|
| `GET` | `/admin/patient-bills?page=N&per_page=N&search=query` | List all patients with their bills | Query params | `{ status, response: { data: IPatientBill[], current_page, total } }` |
| `POST` | `/admin/patient-bills/item/all` | Get all bill items for a patient | `{ patient_id: number }` | `{ status, response: { items: IPatientBillItemPivot[], patient: IPatient } }` |
| `POST` | `/admin/patient-bills/item/add` | Add single bill item to patient | `{ patient_id, bill_item_id, quantity, custom_amount?, discount_amount?, waiver_amount? }` | `{ status, response: null, message }` |
| `POST` | `/admin/patient-bills/item/add-multiple` | Add multiple bill items to patient | `{ patient_id, bills: [{ bill_item_id, quantity, custom_amount?, discount_amount?, waiver_amount? }] }` | `{ status, response: null, message }` |
| `POST` | `/admin/patient-bills/item/delete` | Delete a patient bill item | `{ patient_id: string, bill_id: string }` | `{ status, response: null, message }` |

### 3.4 Department Referral Endpoints

| Method | Endpoint | Purpose | Request |
|--------|----------|---------|---------|
| `POST` | `/admin/patient/send-to-laboratory` | Send lab referral (creates bill items on backend) | `{ patient_id, test_ids: number[], lab_note, extra? }` |
| `POST` | `/admin/patient/send-to-radiology` | Send radiology referral | `{ patient_id, test_ids: number[], radiology_note, extra? }` |
| `POST` | `/admin/patient/send-to-pharmacy` | Send pharmacy prescription | `{ patient_id, medications: [{ medication_id, quantity, dosage, administration_mode, measurement, daily_frequency, start_date, end_date }], pharmacy_note, extra? }` |

### 3.5 Lookup Endpoints

| Method | Endpoint | Purpose |
|--------|----------|---------|
| `GET` | `/common/payment-statuses` | Payment status lookup (id → name) |
| `GET` | `/common/medical-tests?search=query` | Search lab tests |
| `GET` | `/common/radiology-tests?search=query` | Search radiology tests |
| `GET` | `/common/medications?search=query` | Search medications |
| `GET` | `/common/medication-details` | Get measurement units |

### 3.6 Invoice Endpoints

| Method | Endpoint | Purpose | Request | Response |
|--------|----------|---------|---------|----------|
| `POST` | `/admin/patient/invoice/generate` | Generate invoice from bill items | `{ patient_id: string, bill_items: number[] }` | `{ status, response: { invoice_number, ... }, message }` |
| `GET` | `/admin/invoices?page=N&per_page=20` | List all invoices (paginated) | Query params | `{ status, response: { data: IInvoice[], current_page, total } }` |
| `GET` | `/admin/invoices/:invoiceId` | Get invoice details with items | Path param | `{ status, response: InvoiceData }` |
| `POST` | `/admin/patient/invoice/pay` | Initiate online payment | `{ patient_id, invoice_id, items: [{ item_id, amount }] }` | `{ status, response: { url: string } }` — opens payment gateway |
| `POST` | `/admin/settle-invoice` | Settle invoice (manual methods) | `SettleInvoiceRequest` | `{ status, response, message }` |
| `GET` | `/admin/patient/invoice/print-details/:invoiceId/:patientId` | Get print-ready invoice data | Path params | `{ status, response: InvoicePrintDetailsResponse }` |

### 3.7 Stats Endpoint

| Method | Endpoint | Purpose |
|--------|----------|---------|
| `GET` | `/admin/stats` | Dashboard stats (revenue, unpaid invoices, patients, etc.) |

---

## 4. Page 1 – Accountant Dashboard

**Route:** `/accountant-dashboard`

### Purpose
Overview dashboard showing financial KPIs and recent payment activity.

### Dashboard Cards (KPIs)

| Card | Value Source | Click Navigation |
|------|-------------|------------------|
| Revenue | `adminData.response.total_revenue` | `/transactions` |
| Unpaid Invoice | `adminData.response.unpaid_invoice` | `/invoice` |
| Patients | `adminData.response.patients` | `/patients` |
| Invoice Created | `adminData.response.invoice_created` | `/invoice` |
| Book Appointment | — | `/patients` |

### Data Visualizations

1. **Revenue Line Chart** — Monthly revenue over time from `chartData.labels` + `chartData.data`
2. **Payment Channel Pie Chart** — Distribution between POS, Web, Cash from `paymentMethodDistribution`
3. **Recent Payments Table** — Shows Invoice ID, Patient ID, Patient Name, Description, Amount, Amount Paid, Transaction Date

### API Hook
```typescript
useAdminStats(dateFilter?: [string, string] | null)
// GET /admin/stats (with optional date range)
```

---

## 5. Page 2 – Patient Bills List (AccountantBill)

**Route:** `/accountant-bills`

### Purpose
List all patients who have bill items. Allows viewing bill details and navigating to add new bills.

### Features
- **Search** — Debounced input (300ms), resets pagination
- **Table Columns:** Patient Id (file_number), Patient Name, Gender, Unbilled Total, Payment Status, Date, Action
- **Pagination** — Server-side
- **Row Selection** — Checkbox multi-select (for potential batch operations)

### Table Row Actions
- **"View" Button** → Opens `BillFormModal` in an Ant Design Modal (95% width, max 800px)
- **"Add Bills to Patient" Header Button** → Navigates to `/add-patient-bill`

### Pre-selected Patient Flow
When a patient row exists in `localStorage.selectedPatientId`, auto-navigate to `/add-patient-bill` with that patient pre-selected via route state:
```typescript
navigate('/add-patient-bill', { state: { patientId: Number(selectedPatientId) } })
```

### Data Processing
```typescript
// Filter: only show patients with bill_items_pivot
patients.filter(p => p.bill_items_pivot?.length > 0)

// Map payment_status ID to name using paymentStatusMap
const paymentStatusMap = paymentStatuses.reduce((acc, status) => {
  acc[status.id] = status.name
  return acc
}, {})
```

### API Hooks
```typescript
useFetchAllPatientBillsList(page, perPage, search)   // GET /admin/patient-bills
useFetchPatientBills(patientId)                       // POST /admin/patient-bills/item/all
useFetchPaymentStatuses()                             // GET /common/payment-statuses
```

---

## 6. Page 3 – Add New Patient Bill

**Route:** `/add-patient-bill`

### Purpose
Full-page form for adding bill items to a patient across four departments: Laboratory, Radiology, Pharmacy, Procedure.

### Entry Points
1. Click "Add Bills to Patient" button on Patient Bills List
2. Click "+" button in BillFormModal (carries `patientId` in route state)
3. Auto-redirect from Patient Bills List when `localStorage.selectedPatientId` exists

### Layout Sections

#### Section 1: Patient Details
- **Patient Select** — Searchable dropdown (`useFetchPatientsList`), disabled if pre-selected
- **Patient Info Card** — Shows File Number, Patient Name, Gender (when selected)

#### Section 2: Existing Bill Items (Conditional)
- Only shown when patient is pre-selected (existing patient flow)
- Read-only table of current bill items with group, service, qty, amount
- Shows total of existing bills

#### Section 3: Add New Bill (Tabs)
Four tabs for different billing categories:

##### Tab: Laboratory
1. Multi-select dropdown for lab tests (searchable, `useMedicalTests`)
2. Shows selected tests in a summary box
3. Text area for lab note
4. **"Send to Laboratory"** — Calls `POST /admin/patient/send-to-laboratory`
5. On success: adds items to billing summary, clears form, shows toast

##### Tab: Radiology
1. Multi-select dropdown for radiology tests (searchable, `useRadiologyTests`)
2. Selected tests summary box
3. Text area for radiology note
4. **"Send to Radiology"** — Calls `POST /admin/patient/send-to-radiology`
5. On success: adds items to billing summary

##### Tab: Pharmacy
1. **Medication Select** — Single-select searchable dropdown (`useMedications`). Shows stock quantity prefix.
2. **Fields Grid (2 columns):**
   - Medication (select)
   - Stock Keeping Unit (read-only)
   - Dosage (number input)
   - Daily Frequency (1-6)
   - Duration in days (number)
   - Quantity (auto-calculated, disabled)
   - Price (auto-calculated, disabled)
   - Administration Mode (select: Oral, Topical, Injection, IV, IM, SC, Inhalation, Rectal, Sublingual)
   - Measurement Unit (select from API or defaults)
3. **"Add Medication" Button** — Adds to local prescribed medications list
4. **Prescribed Medications List** — Cards showing name, dosage, mode, frequency, quantity, dates. Each removable.
5. **Pharmacy Note** text area
6. **"Send to Pharmacy"** — Calls `POST /admin/patient/send-to-pharmacy`

**Pharmacy Quantity Auto-Calculation:**
```typescript
quantity = (dosage × dailyFrequency × duration) / stockKeepingUnit
price = quantity × medication.price
// quantity is Math.ceil'd
```

##### Tab: Procedure
1. **Procedure Select** — Searchable dropdown from bill items catalog (`useFetchBills` with type="clinic")
2. **Quantity** — Number input (min 0.01, step 0.01)
3. **Custom Amount** — Optional override of default selling price
4. **Discount Amount** — Fixed amount deducted from total
5. **Waiver Amount** — Amount waived from total
6. **"Add" Button** — Adds to billing summary

**Procedure Price Calculation:**
```typescript
unitPrice = customAmount > 0 ? customAmount : billItem.selling_price
totalAmount = unitPrice × quantity
finalAmount = Math.max(0, totalAmount - discountAmount - waiverAmount)
```

#### Section 4: Billing Summary Table
- **Columns:** Bill Group (color-coded badge), Billing Item, Qty, Amount, Status (Pending/Sent), Action (Delete)
- **Row Selection** — Checkbox with batch delete
- **Bill Group Color Coding:**
  - Laboratory: `bg-blue-100 text-blue-700`
  - Radiology: `bg-purple-100 text-purple-700`
  - Pharmacy: `bg-green-100 text-green-700`
  - Procedure: `bg-orange-100 text-orange-700`
- **Status Logic:**
  - Procedure items → "Pending" (yellow badge) — not yet submitted to API
  - Lab/Radiology/Pharmacy items → "Sent" (green badge) — already submitted via referral API

#### Billing Summary Item Data Structure
```typescript
interface BillSummaryItem {
  id: string                    // unique key: `bill-${Date.now()}-${random}`
  billGroup: string             // "Laboratory" | "Radiology" | "Pharmacy" | "Procedure"
  billingItem: string           // display name
  quantity: number
  amount: number                // gross amount
  billItemId?: number           // only for Procedure items
  customAmount?: number
  discountAmount?: number
  waiverAmount?: number
  finalAmount: number           // net amount after discounts
}
```

#### Footer Actions
- **Cancel** — Clears `localStorage.selectedPatientId`, navigates to `/accountant-bills`
- **Delete Selected** — Removes selected summary items
- **Add New Bill** — Re-shows billing section (when collapsed for existing patients)
- **Proceed** — Submits procedure items via `POST /admin/patient-bills/item/add-multiple`, then navigates to `/accountant-bills`

### Proceed Logic
```typescript
// 1. Separate procedure vs referral items
const procedureItems = billSummaryItems.filter(i => i.billGroup === "Procedure")
const referralItems = billSummaryItems.filter(i => i.billGroup !== "Procedure")

// 2. If procedure items exist → submit via API
if (procedureItems.length > 0) {
  const payload = {
    patient_id: parseInt(selectedPatient),
    bills: procedureItems.map(item => ({
      bill_item_id: item.billItemId,
      quantity: item.quantity,
      custom_amount: item.customAmount,
      discount_amount: item.discountAmount,
      waiver_amount: item.waiverAmount,
    })),
  }
  addPatientBills(payload, { onSuccess: () => navigate('/accountant-bills') })
}

// 3. If only referral items → they're already submitted via department APIs
else if (referralItems.length > 0) {
  toast.success("All items processed through department referrals")
  navigate('/accountant-bills')
}
```

### API Hooks Used
```typescript
useFetchPatientsList(100, 1, null, search)              // Patient search
useFetchBills(1, 500, "clinic", "")                     // Procedure catalog
useFetchPatientBills(patientId)                         // Existing bills
useAddMultipleBillItemsWithDiscounts()                  // Submit procedures
useMedicalTests(searchTerm)                             // Lab tests
useSendLabReferral()                                    // Lab referral
useRadiologyTests(searchTermRadiology)                  // Radiology tests
useSendRadiologyReferral()                              // Radiology referral
useMedications(searchTerm)                              // Medications
useMedicationDetails()                                  // Measurement units
useSendPharmacyReferral()                               // Pharmacy referral
```

---

## 7. Modal – Bill Form (View/Generate Invoice)

**Trigger:** "View" button on Patient Bills List row

### Purpose
View a patient's accumulated bill items, select items, delete items, and generate an invoice.

### Features
1. **Bill Items Table** — Service, Qty, Amount with checkbox selection
2. **Total Display** — Shows total of selected items (or all items if none selected)
3. **Actions:**
   - **Cancel** — Close modal
   - **Delete** — Delete selected bill item (one at a time). Confirmation modal required.
   - **Print** — Open `BillPrintModal` with current items
   - **"+" Button** — Navigate to `/add-patient-bill` with patient context to add more items
   - **Proceed** — Generate invoice from selected items

### Generate Invoice Flow
```typescript
// 1. Collect selected bill item IDs (the pivot IDs, not bill_item_id)
const billItemIds = selectedRowKeys.map(key =>
  billItems.find(item => item.key === key)?.id
).filter(Boolean)

// 2. Call generate invoice API
generateInvoice({
  patientId: patient.id.toString(),
  bill_items: billItemIds,
}, {
  onSuccess: (data) => {
    // 3. Navigate to invoice details page
    navigate(`/invoice/${data.response.invoice_number}`)
  }
})
```

### API Hooks
```typescript
useGenerateInvoice()            // POST /admin/patient/invoice/generate
useDeletePatientBillItem()      // POST /admin/patient-bills/item/delete
```

---

## 8. Page 4 – Invoice List

**Route:** `/invoice`

### Purpose
Paginated list of all generated invoices.

### Table Columns
| Column | Source | Notes |
|--------|--------|-------|
| Invoice ID | `invoice_number` | |
| Invoice Date | `invoice_date` | |
| Patient Name | `patient_name` | |
| Amount | `total` | Formatted as ₦ |
| Amount Paid | `amount_paid` | Formatted as ₦ |
| Amount Due | `amount_due` | Formatted as ₦ |
| CBS Settlement | `cbs_settlement` | "Awaiting Settlement" or "Not Awaiting" |
| Status | `payment_status` | Resolved via payment status lookup. Color-coded badge. |
| Action | — | "View" button → `navigate(/invoice/${invoice.id})` |

### Status Badge Colors
```typescript
const statusColors = {
  'Awaiting Payment': 'bg-red-100 text-red-600',
  'Paid': 'bg-green-100 text-green-600',
  'Partial': 'bg-yellow-100 text-yellow-600',
}
```

### Data Filtering
Invoices with `has_waiver === 1 && waiver_approval_status === 'pending'` are excluded from the list.

### Pagination
Server-side, 20 per page.

### API Hooks
```typescript
useInvoices(currentPage)           // GET /admin/invoices?page=N&per_page=20
useFetchPaymentStatuses()          // GET /common/payment-statuses
```

---

## 9. Page 5 – Invoice Details (Payment & Settlement)

**Route:** `/invoice/:invoiceId`

### Purpose
View invoice details, preview payment allocation with reducing balance, process payment via multiple methods, and print receipt.

### Layout Sections

#### Header
- Back button → `/invoice`
- Title: "Invoice Details - {invoice_number}"

#### Invoice Header Card
- Hospital logo
- Invoice ID, Amount Paid, Status badge

#### Billing & Payment Information Panel
- Billing Category (tag)
- Wallet Balance (if available)

#### Invoice From / To
| From | To |
|------|----|
| Hospital name | Patient name |
| Hospital address | File number |
| Invoice date | Phone number |

#### Bill Items Table

| Column | Description |
|--------|-------------|
| S/N | Sequential row number |
| Service | Bill item name |
| Amount | Original amount |
| Amount Paid | Payments already made |
| Balance Due | `amount - amount_paid` (red if > 0, green if 0) |
| Allocation | Preview of how current payment will be distributed |
| Payment Status | From API: "Paid", "Partial", "Awaiting Payment" |
| Reduction Status | Computed locally from reducing balance algorithm |

Items are sorted chronologically by `created_at` ascending.

#### Payment Input
- **Amount to Pay** — Number input, prefixed with ₦, max capped at `totalAmountDue`
- Auto-initialized to total amount due on first load

#### Reducing Balance Preview
Shown when amount > 0 and invoice is not fully paid. Lists each item that will receive allocation with partial/full indicators.

#### Action Buttons
- **Close** → `/invoice`
- **Print** → Opens `InvoicePrintModal`
- **Pay** → Opens `PaymentMethodModal`

### Data Flow
```typescript
// 1. Fetch invoice details
const { data: invoiceData } = useGetInvoiceDetails(invoiceId)

// 2. Build table data from invoice items (sorted by created_at)
const rawTableData = invoice.items.sort(byCreatedAt).map(item => ({
  bill_item: item.name || item.bill_item,
  amount: item.amount,
  amount_paid: item.amount_paid || 0,
  remaining_amount: item.amount - (item.amount_paid || 0),
  allocated_amount: 0,  // will be computed by reducing balance
  status_label: item.payment_status_name,
}))

// 3. Apply reducing balance preview
const tableData = computeReducingBalance(rawTableData, globalAmount)

// 4. Total due
const totalAmountDue = rawTableData.reduce((sum, item) => sum + item.remaining_amount, 0)
```

### Payment Processing

#### Payment Method Selection
When user clicks "Pay", the `PaymentMethodModal` opens. See [Section 11](#11-payment-method-modal) for details.

#### Settlement Payload Construction
```typescript
// 1. Apply reducing balance to determine per-item allocation
const allocatedItems = computeReducingBalance(rawTableData, globalAmount)

// 2. Build items payload (only items with allocation > 0)
const itemsPayload = allocatedItems
  .filter(item => item.allocated_amount > 0)
  .map(item => ({
    item_id: item.bill_id.toString(),
    amount: item.allocated_amount.toString(),
  }))

// 3. Build settlement request
const settlePayload = {
  invoice_number: invoice.invoice_number,
  items: itemsPayload,
  payment_method: method,              // 'POS' | 'CASH' | 'Bank Transfer' | 'wallet'
  transaction_id: transactionId,
  channel: 'monnify',
  // Method-specific:
  pos_type: method === 'POS' ? posType : undefined,
  bank_name: bankAccount?.bank_name,         // for Bank Transfer
  account_number: bankAccount?.account_number,
  account_name: bankAccount?.account_name,
  bank_id: bankAccount?.bank_id,
}

// 4. Execute
if (method === 'Online Payment') {
  // Opens payment gateway URL in new tab
  await payInvoice.mutateAsync({ patient_id, invoice_id, items: itemsPayload })
} else {
  await settleInvoiceAsync(settlePayload)
}

// 5. Refetch data
await refetchInvoices()
await refetchInvoice()
```

### API Hooks
```typescript
useGetInvoiceDetails(invoiceId)     // GET /admin/invoices/:invoiceId
usePayInvoice()                     // POST /admin/patient/invoice/pay
useSettleInvoice()                  // POST /admin/settle-invoice
useInvoices(1)                      // For refetching list after payment
usePaymentStatus()                  // Local hook for status mapping
```

---

## 10. Print / Receipt Modal

**Component:** `InvoicePrintModal`

### Purpose
Fetch print-ready data and auto-trigger browser print dialog formatted as a thermal receipt (80mm width).

### Flow
1. Modal opens → fetch print details from API
2. Data loaded → auto-trigger `window.print()` via `react-to-print`
3. After print → close modal

### Print Layout (Thermal Receipt Format)
```
┌─────────────────────────────────┐
│         [Hospital Logo]         │
│        Hospital Name            │
│       Hospital Address          │
│     Phone: xxx  Email: xxx      │
│                                 │
│          INVOICE                │
│  Invoice #: INV-xxxxx           │
│  Date: DD/MM/YYYY              │
│  Patient: Name                  │
│  File #: xxxxx                  │
│─────────────────────────────────│
│  Service    │ Billed │   Paid   │
│─────────────│────────│──────────│
│  Item 1     │ ₦xxx   │  ₦xxx   │
│  Item 2     │ ₦xxx   │  ₦xxx   │
│─────────────────────────────────│
│  Total:              │  ₦xxx   │
│  Status: Paid/Partial           │
│─────────────────────────────────│
│     Payment History             │
│  Date   │ Amount │ Channel      │
│  ...    │  ...   │  ...         │
│─────────────────────────────────│
│      Thank you!                 │
└─────────────────────────────────┘
```

### Print Data Response
```typescript
interface InvoicePrintDetailsResponse {
  hospital_name: string
  hospital_address: string
  contact_phone: string
  contact_email: string
  name: string                          // patient name
  file_number: string
  invoice_number: string
  bill_amount: number
  bill_status: string
  bill_items: Array<{
    name: string
    billed: number
    paid: number
    payment_channel: string
    bill_group_name?: string
  }>
  payment_history: Array<{
    tx_date: string
    amount_paid: number
    transaction_id: string
    payment_channel: string
  }>
}
```

### API
```typescript
useFetchInvoicePrintDetails()
// GET /admin/patient/invoice/print-details/:invoiceId/:patientId
```

---

## 11. Payment Method Modal

**Component:** `PaymentMethodModal`

### Purpose
Allow the user to select a payment method and provide method-specific details.

### Available Payment Methods

| Method | UI Label | Transaction ID | Extra Input |
|--------|----------|---------------|-------------|
| CareSol PoS | "CareSol PoS" | Auto: `POS-{timestamp}` | None (internal) |
| PoS (Other Banks) | "PoS (Other Banks)" | Manual input (min 5 chars) | POS receipt number |
| Cash | "CASH" | Auto: `CASH-{timestamp}` | None |
| Online Payment | "Online Payment" | `"internal"` | None — redirects to gateway |
| Bank Transfer | "Bank Transfer" | Manual input | Select bank account, enter transaction ID |
| Wallet | "Wallet" | Auto: `WALLET-{timestamp}` | None — deducts from patient wallet |

### Bank Transfer Sub-flow
1. Fetch bank accounts from API (`useFetchBankAccounts`)
2. User selects a bank account from dropdown
3. User enters bank transfer transaction ID (slip reference)
4. Submits: passes `bankAccount` object to callback

### Wallet Sub-flow
1. Check patient wallet balance
2. If sufficient: deduct wallet first, then proceed with settlement
3. If insufficient: show error with shortfall amount

### Callback Interface
```typescript
onSelectPaymentMethod: (
  method: 'POS' | 'CASH' | 'Online Payment' | 'Bank Transfer' | 'wallet',
  posReceiptNo?: string,
  posType?: 'internal' | 'external',
  bankAccount?: BankAccount
) => void
```

---

## 12. Reducing Balance Algorithm

### Purpose
Distribute a payment amount across multiple invoice items in chronological order (FIFO).

### Algorithm
```typescript
function computeReducingBalance(
  items: TableItem[],       // sorted by created_at ascending
  globalAmountToPay: number
): TableItem[] {
  // If no payment, preserve original statuses
  if (globalAmountToPay <= 0) {
    return items.map(item => ({ ...item, allocated_amount: 0 }))
  }

  let remaining = globalAmountToPay

  return items.map(item => {
    if (remaining <= 0) {
      return { ...item, allocated_amount: 0 }
    }

    const due = item.remaining_amount
    if (due <= 0) {
      // Already fully paid
      return { ...item, allocated_amount: 0, status_label: 'Paid' }
    }

    if (remaining >= due) {
      // Fully settle this item
      remaining -= due
      return { ...item, allocated_amount: due, status_label: 'Paid' }
    } else {
      // Partial settlement
      const partial = remaining
      remaining = 0
      return { ...item, allocated_amount: partial, status_label: 'Partial' }
    }
  })
}
```

### Rules
1. Items are processed in **chronological order** (oldest first)
2. Each item gets allocated as much as possible from the remaining payment
3. Only items with `remaining_amount > 0` receive allocation
4. The last item to receive money may be partially settled
5. Items after the money runs out get `allocated_amount: 0`

---

## 13. Frontend Routing

```typescript
// Protected routes (require authentication)
<Route path="/accountant-dashboard" element={<AccountantDashboard />} />
<Route path="/accountant-bills"     element={<AccountantBill />} />
<Route path="/add-patient-bill"     element={<AddNewPatientBill />} />
<Route path="/invoice"              element={<Invoice />} />
<Route path="/invoice/:invoiceId"   element={<InvoiceDetails />} />
```

### Navigation Flow Map
```
/accountant-dashboard
    │
    ├── Card click "Unpaid Invoice"  → /invoice
    ├── Card click "Invoice Created" → /invoice
    │
/accountant-bills
    │
    ├── "Add Bills to Patient" button → /add-patient-bill
    ├── "View" button → Opens BillFormModal
    │       │
    │       ├── "+" button → /add-patient-bill (with patientId state)
    │       └── "Proceed" → Generate Invoice → /invoice/:invoiceNumber
    │
/add-patient-bill
    │
    ├── "Back" / "Cancel" → /accountant-bills
    └── "Proceed" → /accountant-bills
    │
/invoice
    │
    └── "View" → /invoice/:invoiceId
    │
/invoice/:invoiceId
    │
    ├── "Close" / "Back" → /invoice
    ├── "Print" → InvoicePrintModal
    └── "Pay" → PaymentMethodModal → Settlement
```

---

## 14. State Management & Data Fetching

### Technology Stack
- **React Query (v3)** — Server state management
- **React Router v6** — Routing with `useNavigate`, `useParams`, `useLocation`
- **Ant Design** — UI components (Table, Select, Modal, Pagination, Tabs, etc.)
- **Tailwind CSS** — Styling
- **react-hot-toast** — Toast notifications
- **dayjs** — Date manipulation
- **react-to-print** — Print functionality
- **Axios** — HTTP client with instance pattern

### Axios Instance Pattern
All API calls go through a centralized Axios instance with:
- Base URL configuration
- Auth token injection (via interceptors)
- Error response handling

### React Query Patterns Used
```typescript
// Queries (data fetching)
useQuery(['key', ...deps], fetchFn, {
  keepPreviousData: true,       // for pagination
  staleTime: 5 * 60 * 1000,    // for lookups
  enabled: !!requiredParam,     // conditional fetching
})

// Mutations (data modification)
useMutation(mutationFn, {
  onSuccess: (data) => { /* handle success, show toast */ },
  onError: (error) => { /* handle error, show toast */ },
})
```

### Local State Patterns
- `useState` for form fields and UI toggles
- `useMemo` for computed/derived data
- `useEffect` for syncing state (pre-selected patient, auto-calculations)
- `localStorage` used sparingly for cross-page patient selection

---

## 15. Utility Functions

### Currency Formatting
```typescript
// formatNaira(amount: number): string
// Returns "₦X,XXX.XX" format
formatNaira(1500.5) // → "₦1,500.50"
```

### Date Formatting
```typescript
// formatDate(dateString: string): string
// Converts ISO date to human-readable format
formatDate("2026-02-23T10:30:00Z") // → "23 Feb, 2026"
```

### Unique ID Generation
```typescript
const generateId = () =>
  `bill-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
```

### Debounce (Search)
```typescript
import { debounce } from 'lodash'
const debouncedSearch = debounce((value) => {
  setSearch(value)
  setPage(1)
}, 300)
```

---

## Appendix A: Component Dependencies

```
AddNewPatientBill
├── Layout (HealthHubLayout)
├── HeaderSection
├── Ant Design: Tabs, TabPane, Card, Select, Input, InputNumber, Table, Button, Spin
├── Icon (@iconify/react)
├── react-hot-toast
├── dayjs
└── react-router-dom (useNavigate, useLocation)

BillFormModal
├── Ant Design: Table, Modal
├── PayDetailsModal
├── BillPrintModal
├── useGenerateInvoice
├── useDeletePatientBillItem
└── react-router-dom (useNavigate)

InvoiceDetails
├── Layout (HealthHubLayout)
├── HeaderSection
├── Ant Design: Table, Tag, Badge, Input, Alert, Spin, Button
├── PaymentMethodModal
├── InvoicePrintModal
├── BillingBreakdownModal
├── useGetInvoiceDetails
├── usePayInvoice
├── useSettleInvoice
├── usePaymentStatus (local hook)
└── react-to-print
```

---

## Appendix B: Implementation Checklist

### Backend (APIs to implement)
- [ ] `GET /admin/patient/list` — Patient search + list
- [ ] `GET /admin/bills/list` — Bill catalog (procedures/services)
- [ ] `GET /admin/patient-bills` — All patients with bills
- [ ] `POST /admin/patient-bills/item/all` — Patient's bill items
- [ ] `POST /admin/patient-bills/item/add` — Add single bill item
- [ ] `POST /admin/patient-bills/item/add-multiple` — Add multiple bill items (with discount/waiver)
- [ ] `POST /admin/patient-bills/item/delete` — Delete bill item
- [ ] `POST /admin/patient/send-to-laboratory` — Lab referral
- [ ] `POST /admin/patient/send-to-radiology` — Radiology referral
- [ ] `POST /admin/patient/send-to-pharmacy` — Pharmacy referral
- [ ] `GET /common/payment-statuses` — Payment status lookup
- [ ] `GET /common/medical-tests` — Lab test catalog
- [ ] `GET /common/radiology-tests` — Radiology test catalog
- [ ] `GET /common/medications` — Medication catalog
- [ ] `GET /common/medication-details` — Measurement units
- [ ] `POST /admin/patient/invoice/generate` — Generate invoice from bill items
- [ ] `GET /admin/invoices` — List invoices (paginated)
- [ ] `GET /admin/invoices/:id` — Invoice detail with items
- [ ] `POST /admin/patient/invoice/pay` — Online payment (returns gateway URL)
- [ ] `POST /admin/settle-invoice` — Manual settlement (POS/Cash/Transfer/Wallet)
- [ ] `GET /admin/patient/invoice/print-details/:invoiceId/:patientId` — Print data
- [ ] `GET /admin/stats` — Dashboard statistics

### Frontend (Pages/Components to build)
- [ ] Accountant Dashboard page
- [ ] Patient Bills List page
- [ ] Add New Patient Bill page (with 4 tabs)
- [ ] Bill Form Modal (view/select/delete/generate)
- [ ] Invoice List page
- [ ] Invoice Details page (with reducing balance)
- [ ] Payment Method Modal
- [ ] Invoice Print Modal (thermal receipt layout)
- [ ] Delete Confirmation Modal
- [ ] Dashboard Card component
- [ ] Header Section component
- [ ] Layout component
- [ ] Protected Route wrapper

### Shared/Utility
- [ ] Axios instance with auth interceptors
- [ ] React Query hooks for each endpoint
- [ ] Payment status resolution helper
- [ ] Currency formatting utility
- [ ] Date formatting utility
- [ ] Reducing balance algorithm
