Ledger, Billing & Insurance Copay

v0.1.0

Patient 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 Money Value 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 CategoryCDT RangeExamples
DiagnosticD0100–D0999D0120 (Periodic oral eval), D0150 (Comprehensive eval), D0210 (Full mouth series)
PreventiveD1000–D1999D1110 (Adult prophylaxis), D1206 (Fluoride varnish), D1351 (Dental sealant)
RestorativeD2000–D2999D2391 (1-surface posterior composite), D2740 (Crown - porcelain/ceramic)
EndodonticsD3000–D3999D3310 (Anterior root canal), D3330 (Molar root canal)
PeriodonticsD4000–D4999D4341 (Scaling & root planing, 4+ teeth per quad), D4910 (Perio maintenance)
ProsthodonticsD5000–D5899D5110 (Complete upper denture), D5213 (Maxillary partial denture)
Implant ServicesD6000–D6199D6010 (Surgical implant placement), D6056 (Prefabricated abutment)
Oral SurgeryD7000–D7999D7140 (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 ClassTriggerPayload
InvoiceGeneratedDispatched when a procedure charge is finalized into a dental invoice.DentalInvoice $invoice