Consultation Session Completion Lifecycle
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.
Core Entities
consultation_sessions(ConsultationSessions): runtime session state (scheduled,ongoing,done,missed, etc.)ledger(Ledger): provider credits per consultation session, initiallyPENDINGwallet(Wallet): derived balances from ledger + withdrawalswithdrawal(Withdrawal): payout pipeline constrained bywallet.availableBalance
Lifecycle Sequence
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
- Session finalization:
server/src/consultation/sessions.scheduler.ts- Expired + both joined =>
done - Expired + not both joined =>
missed
- Emitted events:
consultation.session.updatedon bothdoneandmissedconsultation.completedondoneconsultation.session.missedonmissed
- Payment release:
donesessions schedule delayed fund release withPAYMENT_HOLD_DAYSmissedsessions do not auto-release
- Wallet availability:
- Only
RELEASEDledger credits contribute toavailableBalance - Withdrawals are validated against
availableBalance
- Review/completion gating:
- Consultation completion logic requires terminal sessions and at least one
done - This impacts ability to submit reviews through enrollment completion checks
- Metrics:
- Student/analytics “finished consultation” counts use
donesessions - Calendar excludes terminal statuses by default (
done,missed,cancelled,pending_payment)
Faker Testing Behavior
When calling:
POST /faker/session/:sessionId/time
with payload:
{
"hoursFromNow": -1,
"triggerCompletionCheck": false
}
behavior is:
- Session time is shifted.
- If shifted end time is already expired (
<= now - 15 minutes) andtriggerCompletionCheckis 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
- Use
hoursFromNownegative enough soendTime <= now - 15m. - Ensure
bothPartiesJoined=truebefore forcing completion if you expectdoneand release scheduling. - Use admin
POST /sessions/:id/release-fundsfor missed-session dispute resolution path.