3.9 KiB
title, tags, aliases
| title | tags | aliases | |||||
|---|---|---|---|---|---|---|---|
| PointTransaction |
|
|
PointTransaction
Append-only ledger of loyalty point movements. Each row represents one earn / spend / expire event for a user, with a source attribution (purchase / referral / bonus / admin / redemption), the amount moved, and the resulting balance snapshot. Metadata is flexible to support different sources (order amount, commission, level changes, referenced PurchaseRequest).
[!note] Source
backend/src/models/PointTransaction.ts:25— schema definitionbackend/src/models/PointTransaction.ts:84— model export
Schema
| Field | Type | Required | Default | Validation | Index | Description |
|---|---|---|---|---|---|---|
user |
ObjectId → User | yes | — | — | yes (single + compound) | Owner of the transaction. |
type |
String | yes | — | enum: earn / spend / expire |
yes (compound) | Movement direction. |
source |
String | yes | — | enum: purchase / referral / bonus / admin / redemption |
yes (compound) | Source bucket. |
amount |
Number | yes | — | — | — | Points moved (positive integer; semantics by type). |
balance |
Number | yes | — | — | — | Available balance after the move. |
order |
ObjectId → Order | no | — | — | — | Linked order id (legacy ref, see warning). |
referredUser |
ObjectId → User | no | — | — | — | Referred user (for referral earns). |
description |
String | yes | — | — | — | Human label. |
metadata.orderAmount |
Number | no | — | — | — | Order amount snapshot. |
metadata.commission |
Number | no | — | — | — | Commission snapshot. |
metadata.levelBefore |
Number | no | — | — | — | Pre-level snapshot. |
metadata.levelAfter |
Number | no | — | — | — | Post-level snapshot. |
metadata.purchaseRequestId |
String | no | — | — | — | Linked PurchaseRequest id. |
expiresAt |
Date | no | — | — | yes (sparse) | When the points expire (for earn). |
createdAt |
Date | auto | — | — | yes (compound, desc) | Mongoose timestamp. |
updatedAt |
Date | auto | — | — | — | Mongoose timestamp. |
[!warning]
orderreference The schema declaresref: 'Order', but there is noOrdermodel inbackend/src/models/. In practice this slot is used for the PurchaseRequest id; consumers should not rely on Mongoosepopulate('order')working.
Virtuals
None defined.
Indexes
Defined at backend/src/models/PointTransaction.ts:80-82. Plus the implicit index from user being declared with index: true:
{ user: 1, createdAt: -1 }— user ledger view.{ type: 1, source: 1 }— analytics.{ expiresAt: 1 }(sparse) — expiry sweeps.
Pre/Post Hooks
None declared.
Instance Methods
None defined.
Static Methods
None defined.
Relationships
- References: User (
user,referredUser). - Referenced by: none. Loosely related to PurchaseRequest via
metadata.purchaseRequestId(string).
State Transitions
No status field — entries are immutable once written. A consumer scans for expiresAt < now to create offsetting type: 'expire' rows.
Common Queries
// User ledger
PointTransaction.find({ user: userId }).sort({ createdAt: -1 }).limit(50);
// Latest balance (most recent row)
PointTransaction.findOne({ user: userId }).sort({ createdAt: -1 });
// Referral earnings
PointTransaction.find({ user: userId, source: 'referral', type: 'earn' });
// Points expiring soon
PointTransaction.find({ expiresAt: { $lte: oneWeekFromNow }, type: 'earn' });
// Analytics: total earned vs spent per source
PointTransaction.aggregate([
{ $group: { _id: { type: '$type', source: '$source' }, total: { $sum: '$amount' } } }
]);
Related: User, LevelConfig, PurchaseRequest.