# Calculations

## Obligation and Balance Calculations

### Scheduled Monthly Payment Amount

**Field 13:** Scheduled Monthly Payment Amount

This field represents the regular payment amount due.

**Calculation:**


```
IF snapshot.loan_status IN ('ChargedOff', 'PaidOff'):
  scheduled_payment = 0
ELIF lender_supplied_account_status = '97':  # ChargedOff
  scheduled_payment = 0
ELSE:
  # Get current period obligation
  obligation = find_obligation_in_period(month_start, month_end)
  IF obligation:
    scheduled_payment = obligation.obligation_amount
  ELSE:
    scheduled_payment = 0

RETURN round_to_whole_dollars(scheduled_payment)
```

### Amount Past Due

**Field 22:** Amount Past Due

This field reports the total amount that is past due on the account.

**Calculation:**


```
FUNCTION calculate_amount_past_due(loan, snapshot, lender_supplied_status):
  # Priority 1: Paid-in-full statuses always report zero
  IF lender_supplied_status IN ('61', '62', '63'):  # Paid in full statuses
    RETURN 0
  
  # Priority 2: Voluntary Surrender when account is current
  IF lender_supplied_status = '95' AND account_is_current:
    RETURN 0
  
  # Priority 3: Based on loan status
  IF snapshot.loan_status IN ('Active', 'Frozen'):
    amount_past_due = snapshot.overdue_over_30_days_balance_total_amount
  
  ELIF snapshot.loan_status IN ('Accelerated', 'ChargedOff'):
    amount_past_due = snapshot.outstanding_balance_total_amount
  
  ELIF snapshot.loan_status IN ('Pending', 'Originated', 'Declined', 'PaidOff', 'Canceled'):
    amount_past_due = 0
  
  ELSE:
    RAISE ERROR "Unknown loan status"
  
  RETURN round_to_whole_dollars(max(amount_past_due, 0))
```

**Logic Explanation:**

| Loan Status | Amount Past Due Source | Rationale |
|  --- | --- | --- |
| Active, Frozen | `overdue_over_30_days_balance_total_amount` | Only amounts 30+ days past due are reported |
| Accelerated, ChargedOff | `outstanding_balance_total_amount` | Entire balance is considered past due |
| PaidOff, Canceled, Pending, Originated, Declined | 0 | No amount past due |


**Override Conditions:**

| Condition | Result | CRRG Requirement |
|  --- | --- | --- |
| Status 61, 62, 63 (Paid in Full) | Always 0 | "Requires Current Balance and Amount Past Due = zero" |
| Status 95 (Voluntary Surrender) + Account Current | 0 | Account surrendered before becoming delinquent |


### Current Balance

**Field 21:** Current Balance

**Data Source:** `snapshot.outstanding_balance_total_amount`

**Components:**

- Principal balance
- Accrued interest
- Outstanding fees


**Special Rules:**

- For status codes 13, 61, 62, 63: Always report $0
- Negative balances: Report as $0
- Round according to loan type configuration


## Transaction Types and Payment Calculations

### Overview

The Actual Payment Amount field (Field 17) requires understanding which transactions count as "payments" for credit reporting purposes.

### Transaction Types

| Transaction Type | Description |
|  --- | --- |
| Payment | Consumer payment (AutoPay, OneTimePayment, Settlement) |
| ServiceCredit | Lender-applied credit adjustment |


### Service Credit Types

| Credit Type | Reports as Payment? | Description |
|  --- | --- | --- |
| `serviceAgent` | [YES] | Agent-applied credit |
| `serviceSupervisor` | [YES] | Supervisor-approved credit |
| `balanceTransfer` | [YES] | Balance transferred from another account |
| `usuryCap` | [YES] | Usury cap adjustment |
| `settlementOfDebt` | [NO] | Settlement credit (considered a loss) |
| `fraud` | [NO] | Fraud adjustment credit (considered a loss) |
| `badDebt` | [NO] | Bad debt write-off credit (considered a loss) |
| `deceased` | [NO] | Deceased borrower adjustment (considered a loss) |
| `bankruptcy` | [NO] | Bankruptcy adjustment (special Account Status handling) |
| `refund` | [NO] | Refund (always excluded) |
| `interestAdjustment` | [NO] | Interest adjustment (always excluded) |
| `rewards` | [YES] | Rewards credit (e.g., cashback applied to balance) |
| `rounding` | [NO] | Rounding adjustment (always excluded) |
| `rewards` | [YES] | Rewards credit (e.g., cashback applied to balance) |


> **Important:** Service credits with types `settlementOfDebt`, `settlementOfDebtNoLoss`, `fraud`, `badDebt`, `deceased`, and `bankruptcy` are NOT considered "real" payments for Actual Payment Amount or Date of Last Payment calculations.
Of those, only `settlementOfDebt`, `fraud`, `badDebt`, and `deceased` are also treated as "loss" transactions for Account Status determination — their presence prevents a charged-off loan from reporting as status 64 (Paid in Full). `settlementOfDebtNoLoss` is explicitly **excluded** from the loss check — it is the variant for settlement scenarios where the lender does not want a loss recorded, allowing a 64 outcome.
`bankruptcy` is also excluded from the loss check because it is the mechanism for zeroing a balance after a bankruptcy discharge and does not by itself force a 97 status. See [Account Status Codes](/credit-reporting/account-status) for the full algorithm.
The `bankruptcy` service credit type should only be used when a bankruptcy case has been discharged and the lender needs to zero the loan balance.


### Actual Payment Amount Algorithm


```
FUNCTION calculate_actual_payment_amount(loan, month_start, month_end):
  
  total_payment = 0
  
  # Query payments in the reporting period
  transactions = query_transactions(
    loan_id = loan.id,
    is_virtual = False,
    status IN ('Initiated', 'Pending', 'Succeeded'),
    display_date BETWEEN month_start AND month_end
  )
  
  # Also include transactions that failed AFTER the report cutoff
  # (they were valid at the time of reporting)
  transactions += query_transactions(
    loan_id = loan.id,
    status = 'Failed',
    failed_at > file_creation_max_datetime,
    initiated_or_pending_at BETWEEN file_creation_min_datetime AND file_creation_max_datetime
  )
  
  FOR EACH tx IN transactions:
    
    # Payment transactions
    IF tx.transaction_type = 'Payment':
      IF tx.payment_reason IN ('AutoPay', 'OneTimePayment', 'Settlement'):
        total_payment += tx.effective_amount
    
    # Service credits - ONLY these four types count as "real" payments
    IF tx.transaction_type = 'ServiceCredit':
      IF tx.credit_type IN ('serviceAgent', 'serviceSupervisor', 'balanceTransfer', 'usuryCap'):
        total_payment += tx.effective_amount
  
  RETURN round_to_whole_dollars(total_payment)
```

### Date of Last Payment

**Field 27:** Date of Last Payment

Returns the date of the most recent qualifying transaction (same inclusion criteria as Actual Payment Amount):


```
FUNCTION get_date_of_last_payment(loan, date_of_account_info):
  
  # Find most recent payment transaction
  payment_tx = query_most_recent_transaction(
    loan_id = loan.id,
    transaction_type = 'Payment',
    payment_reason IN ('AutoPay', 'OneTimePayment', 'Settlement'),
    status IN ('Initiated', 'Pending', 'Succeeded'),
    display_date <= date_of_account_info,
    is_virtual = False
  )
  
  # Find most recent qualifying service credit
  service_tx = query_most_recent_transaction(
    loan_id = loan.id,
    transaction_type = 'ServiceCredit',
    credit_type IN ('serviceAgent', 'serviceSupervisor', 'balanceTransfer', 'usuryCap'),
    status IN ('Initiated', 'Pending', 'Succeeded'),
    display_date <= date_of_account_info,
    is_virtual = False
  )
  
  # Return the most recent of the two
  IF payment_tx AND service_tx:
    RETURN max(payment_tx.display_date, service_tx.display_date)
  ELIF payment_tx:
    RETURN payment_tx.display_date
  ELIF service_tx:
    RETURN service_tx.display_date
  ELSE:
    RETURN NULL

> **Note:** The implementation combines these two lookups into a single query for efficiency, but the logic is equivalent.
```

> **Note:** The implementation combines these two lookups into a single query for efficiency, but the logic is equivalent.


## Loan Replay and Retroactive Changes

### What is Loan Replay?

Loan replay is the process of recalculating a loan's history when:

- Interest rates change retroactively
- Payments are backdated
- Errors are corrected


### Impact on Credit Reporting

When a loan is replayed:

- Previous Metro 2 files remain unchanged (already submitted)
- Next Metro 2 file reflects corrected current state
- Payment History Profile may show different values for past months


## Terminal Reporting Events

### When Reporting Stops — Key Change

**BEFORE (Old Logic):**


```
IF loan_status = 'ChargedOff' AND chargedOffReason = 'bankruptcy':
  → Report loan for the LAST time (stop immediately)
```

**AFTER (New Logic - Matches Spec):**


```
IF loan_status = 'ChargedOff' AND outstanding_balance = 0:
  → Report loan for the LAST time (stop when balance reaches zero)
```

> **Key Difference:** Previously, bankruptcy charge-offs stopped reporting immediately regardless of balance. Now, all charge-offs (including bankruptcy) continue reporting until the balance reaches $0.


### When Reporting Stops

Credit reporting stops (terminal event) when:

| Condition | Final Status | Behavior |
|  --- | --- | --- |
| Loan paid off | 13 | Report once with status 13, then stop |
| Loan charged off with zero balance AND no service credit transaction types (settlementOfDebt, fraud, badDebt, deceased) | 64 | Report once with status 64, then stop |
| Loan charged off with zero balance BUT has service credit transaction types (settlementOfDebt, fraud, badDebt, deceased) | 97 | Report with status 97 (loss occurred) |
| Bankruptcy charge-off with balance | 97 | Continue reporting until balance reaches zero |
| Bankruptcy charge-off, balance reaches zero (no loss service credits) | 64 | Report once with status 64, then stop |
| Fraudulent charge-off | DF | Send deletion, then stop |
| Legal deletion | DA | Send deletion, then stop |
| Loan data corrupted | DA | Send deletion, then stop |


> **Service Credit Transaction Types Indicating Loss:** Service credits of type `settlementOfDebt`, `fraud`, `badDebt`, or `deceased` indicate the lender took a loss. If any of these were used to bring the balance to zero, the account reports status 97 instead of 64.


### Bankruptcy Charge-Off Terminal Logic

Special handling for bankruptcy charge-offs:


```
IF charged_off_reason = 'bankruptcy':
  # Check for service credit transaction types that indicate a loss
  # (excludes 'bankruptcy' serviceCreditType)
  loss_service_credits = find_service_credits(
    types = ['settlementOfDebt', 'fraud', 'badDebt', 'deceased'],
    status = 'Succeeded'
  )
  
  IF outstanding_balance > 0:
    # Continue reporting with status 97
    reporting_continues = True
    account_status = '97'
  ELIF count(loss_service_credits) > 0:
    # Balance is zero but loss occurred
    reporting_continues = False
    account_status = '97'
  ELSE:
    # Balance is zero, no loss - final report with 64
    reporting_continues = False
    account_status = '64'
```

### Term Charge-Off Terminal Logic


```
IF charged_off_reason = 'term':
  loss_service_credits = find_service_credits(
    types = ['settlementOfDebt', 'fraud', 'badDebt', 'deceased'],
    status = 'Succeeded'
  )
  
  IF outstanding_balance > 0:
    reporting_continues = True
    account_status = '97'
  ELIF count(loss_service_credits) > 0:
    # Balance is zero but loss occurred
    reporting_continues = False
    account_status = '97'
  ELSE:
    # Balance is zero, no loss - final report with 64
    reporting_continues = False
    account_status = '64'
```

### Stop Reporting Process

When reporting should stop:

1. `CreditReportingStatus.reporting_status` is set to `Stopped`
2. `reporting_end_date` is set to the final reporting date
3. Final file includes the loan with terminal status
4. Subsequent files exclude the loan