> ## Documentation Index
> Fetch the complete documentation index at: https://ihub.learnille.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Consultation Session Completion Lifecycle

> Migrated from the previous Learnille internal documentation hub

## Purpose

This document describes what happens in the backend when consultation sessions move to terminal states (`done` or `missed`), including payments, wallet availability, review gating, and testing behavior from faker.

## Missed-session recovery and repeat purchases

A `missed` session can be rescheduled when eligible; it is not permanently closed to
recovery. The owning consultant may prompt the student, who selects a replacement
time and pays any fee. Rescheduling retains the session identity and resets attendance;
rebooking instead creates a new purchase and session set. See the
[rescheduling and rebooking lifecycle](https://ihub.learnille.com/engineering/architecture/consultation-rescheduling-and-rebooking.md)
and [QA checklist](https://ihub.learnille.com/engineering/testing-and-qa/consultation-rescheduling-and-rebooking.md).

## Core Entities

* `consultation_sessions` (`ConsultationSessions`): runtime session state (`scheduled`, `ongoing`, `done`, `missed`, etc.)
* `ledger` (`Ledger`): provider credits per consultation session, initially `PENDING`
* `wallet` (`Wallet`): derived balances from ledger + withdrawals
* `withdrawal` (`Withdrawal`): payout pipeline constrained by `wallet.availableBalance`

## Lifecycle Sequence

```mermaid theme={null}
sequenceDiagram
    participant F as Faker
    participant SS as SessionsScheduler
    participant S as ConsultationSessions
    participant E as EventEmitter
    participant L as LedgerService
    participant Q as BullMQ(FUND_RELEASE)
    participant FR as FundReleaseProcessor
    participant W as WalletService
    participant WD as WithdrawalAdminService

    F->>S: adjustSessionTime(sessionId, hoursFromNow, triggerCompletionCheck)
    alt session is expired (endTime <= now - 15m)
        F->>SS: handleSessionCompletion()
    end

    SS->>S: find pending sessions and detect expired
    alt bothPartiesJoined = true
        SS->>S: status = done, save
        SS->>E: emit consultation.session.updated
        SS->>E: emit consultation.completed
        SS->>L: findBySessionId(sessionId)
        SS->>L: scheduleRelease(ledgerId, holdDelay)
        L->>Q: enqueue release-funds job
    else bothPartiesJoined = false
        SS->>S: status = missed, save
        SS->>E: emit consultation.session.updated
        SS->>E: emit consultation.session.missed
        SS->>SS: send SESSION_MISSED notifications
    end

    Q->>FR: process release-funds job
    FR->>L: releaseEntry(ledgerId)
    FR->>W: reconcileWallet(providerId)

    WD->>W: getBalance(providerId, includePending=true)
    W-->>WD: availableBalance (released credits only)
```

## Runtime Side Effects Tied to Completion

1. Session finalization:

* `server/src/consultation/sessions.scheduler.ts`
* Expired + both joined => `done`
* Expired + not both joined => `missed`

2. Emitted events:

* `consultation.session.updated` on both `done` and `missed`
* `consultation.completed` on `done`
* `consultation.session.missed` on `missed`

3. Payment release:

* `done` sessions schedule delayed fund release with `PAYMENT_HOLD_DAYS`
* `missed` sessions do not auto-release

4. Wallet availability:

* Only `RELEASED` ledger credits contribute to `availableBalance`
* Withdrawals are validated against `availableBalance`

5. Review/completion gating:

* Consultation completion logic requires terminal sessions and at least one `done`
* This impacts ability to submit reviews through enrollment completion checks

6. Metrics:

* Student/analytics “finished consultation” counts use `done` sessions
* Calendar excludes terminal statuses by default (`done`, `missed`, `cancelled`, `pending_payment`)

## Faker Testing Behavior

When calling:

* `POST /faker/session/:sessionId/time`

with payload:

```json theme={null}
{
  "hoursFromNow": -1,
  "triggerCompletionCheck": false
}
```

behavior is:

* Session time is shifted.
* If shifted end time is already expired (`<= now - 15 minutes`) and `triggerCompletionCheck` is true, faker immediately runs the same scheduler completion pass (`handleSessionCompletion`), so downstream effects start without waiting for cron.
* Default is `false`, so completion is not auto-triggered unless explicitly requested.

## Notes for Deterministic Tests

1. Use `hoursFromNow` negative enough so `endTime <= now - 15m`.
2. Ensure `bothPartiesJoined=true` before forcing completion if you expect `done` and release scheduling.
3. Use admin `POST /sessions/:id/release-funds` for missed-session dispute resolution path.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.