Ledger, Billing & Insurance Copay
v0.1.0Patient financial ledgers, itemized dental invoices, ADA/CDT procedure codes, and dual-payer coordination of benefits (COB).
Patient Ledger, Invoicing & Dual-Payer Copay
The Ledger & Billing bounded context manages dental practice revenue cycles. It compiles completed clinical procedures into itemized invoices, resolves standard ADA/CDT dental procedure codes, computes dual-payer Coordination of Benefits (COB), and posts patient and insurance payments.
The Financial Ledger Architecture
Financial figures are maintained with exact mathematical precision:
- The
MoneyValue Object: All monetary balances, fees, deductibles, and payments are stored as integer minor units (e.g. cents / pennies) to prevent floating-point rounding errors. - Invoices (
DentalInvoice): Header records capturing total fees, insurance responsibility splits, and patient balance due. - Line Items (
InvoiceLineItem): Granular itemized entries linking procedures, tooth numbers, tooth surfaces, and CDT procedure codes. - Payments (
LedgerPayment): Direct credits applied to the patient’s balance.
use Obelaw\Ium\Dental\ValueObjects\Money;
$fee = Money::fromMinor(125000); // Represents $1,250.00
echo $fee->formatted(); // "$1,250.00"
echo $fee->toMinor(); // 125000
Dual-Payer Coordination of Benefits (COB) Algorithm
When a patient is covered by both a primary policy (e.g., employee plan) and a secondary policy (e.g., spouse’s plan), Obelawium Dental coordinates benefit allocations automatically:
[Total Procedure Fee]
|
v
[1. Primary Insurance Calculation]
- Subtract Primary Deductible
- Apply Primary Coverage % (e.g. 80%)
- Cap at Primary Annual Maximum
|
v
[Remaining Balance After Primary]
|
v
[2. Secondary Insurance Calculation (COB)]
- Subtract Secondary Deductible
- Apply Secondary Coverage % (e.g. 50%)
- Cap at Secondary Annual Maximum
|
v
[3. Patient Out-of-Pocket Copay]
= Total Fee - Primary Paid - Secondary Paid
Mathematical Formulation
// Step 1: Primary Insurance
$primaryDeductibleApplied = min($totalFee, $primaryDeductible);
$primarySubject = max(0, $totalFee - $primaryDeductibleApplied);
$primaryPaid = (int) round($primarySubject * $primaryCoveragePercent);
if ($primaryMaxRemaining !== null) {
$primaryPaid = min($primaryPaid, $primaryMaxRemaining);
}
$remaining = max(0, $totalFee - $primaryPaid);
// Step 2: Secondary Insurance COB
$secondaryDeductibleApplied = min($remaining, $secondaryDeductible);
$secondarySubject = max(0, $remaining - $secondaryDeductibleApplied);
$secondaryPaid = (int) round($secondarySubject * $secondaryCoveragePercent);
if ($secondaryMaxRemaining !== null) {
$secondaryPaid = min($secondaryPaid, $secondaryMaxRemaining);
}
// Step 3: Final Patient Out-of-Pocket Copay
$patientCopay = max(0, $totalFee - $primaryPaid - $secondaryPaid);
Standard ADA/CDT Dental Procedure Codes
Obelawium Dental validates and indexes American Dental Association (ADA) Code on Dental Procedures and Nomenclature (CDT):
| Code Category | CDT Range | Examples |
|---|---|---|
| Diagnostic | D0100–D0999 | D0120 (Periodic oral eval), D0150 (Comprehensive eval), D0210 (Full mouth series) |
| Preventive | D1000–D1999 | D1110 (Adult prophylaxis), D1206 (Fluoride varnish), D1351 (Dental sealant) |
| Restorative | D2000–D2999 | D2391 (1-surface posterior composite), D2740 (Crown - porcelain/ceramic) |
| Endodontics | D3000–D3999 | D3310 (Anterior root canal), D3330 (Molar root canal) |
| Periodontics | D4000–D4999 | D4341 (Scaling & root planing, 4+ teeth per quad), D4910 (Perio maintenance) |
| Prosthodontics | D5000–D5899 | D5110 (Complete upper denture), D5213 (Maxillary partial denture) |
| Implant Services | D6000–D6199 | D6010 (Surgical implant placement), D6056 (Prefabricated abutment) |
| Oral Surgery | D7000–D7999 | D7140 (Simple extraction), D7210 (Surgical tooth extraction) |
Fluent Service Operations
All billing workflows are accessed via ium()->dental()->ledgers():
1. Calculating Dual-Payer Copay Estimates
use Obelaw\Ium\Dental\Data\CalculateCopayDto;
$copay = ium()->dental()->ledgers()->calculateDualPayerCopay(CalculateCopayDto::from([
'total_fee_minor' => 100000, // $1,000.00 (Crown D2740)
'primary_coverage_percent' => 0.80, // 80% coverage
'primary_deductible_minor' => 5000, // $50.00 deductible
'primary_max_remaining_minor' => 150000, // $1,500.00 annual benefit remaining
'secondary_coverage_percent' => 0.50, // 50% secondary coverage
'secondary_deductible_minor' => 0, // $0 secondary deductible
'secondary_max_remaining_minor' => 100000,
]));
echo $copay->primaryPaid->formatted(); // "$760.00"
echo $copay->secondaryPaid->formatted(); // "$120.00"
echo $copay->patientCopay->formatted(); // "$120.00"
2. Charging a Completed Procedure
When a procedure finishes chairside, generate an itemized invoice:
use Obelaw\Ium\Dental\Data\ChargeProcedureDto;
use Obelaw\Ium\Dental\ValueObjects\AdacdtCode;
$invoice = ium()->dental()->ledgers()->charge(ChargeProcedureDto::from([
'patient_id' => $patient->id,
'cdt_code' => AdacdtCode::from('D2740'),
'total_fee_minor' => 100000,
'primary_paid_minor' => 76000,
'secondary_paid_minor' => 12000,
'patient_copay_minor' => 12000,
'procedure_id' => $plannedProcedure->id,
'tooth_number' => '14',
'notes' => 'Full contour zirconia crown on tooth 14.',
'actor_id' => 'DR-MARTINEZ',
]));
3. Posting a Payment to the Patient Ledger
Post payments from patients (cash, card) or direct insurance electronic fund transfers (EFT):
use Obelaw\Ium\Dental\Data\PostPaymentDto;
use Obelaw\Ium\Dental\Enums\PayerType;
$payment = ium()->dental()->ledgers()->postPayment(PostPaymentDto::from([
'patient_id' => $patient->id,
'invoice_id' => $invoice->id,
'amount_minor' => 12000, // $120.00 patient copay
'payer_type' => PayerType::PATIENT,
'payment_method' => 'credit_card',
'reference_number' => 'AUTH-STRIPE-948102',
'actor_id' => 'USER-FRONTDESK',
]));
4. Inquiring Patient Account Balance
$balance = ium()->dental()->ledgers()->balance($patient->id);
echo $balance->formatted(); // "$0.00" (if copay fully settled)
Emitted Domain Events
| Event Class | Trigger | Payload |
|---|---|---|
InvoiceGenerated | Dispatched when a procedure charge is finalized into a dental invoice. | DentalInvoice $invoice |