94 lines
3.9 KiB
Markdown
94 lines
3.9 KiB
Markdown
---
|
|
title: PointTransaction
|
|
tags: [data-model, mongoose]
|
|
aliases: [Point Ledger, Loyalty Transaction, IPointTransaction]
|
|
---
|
|
|
|
# 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 definition
|
|
> `backend/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] `order` reference
|
|
> The schema declares `ref: 'Order'`, but there is no `Order` model in `backend/src/models/`. In practice this slot is used for the [[PurchaseRequest]] id; consumers should not rely on Mongoose `populate('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
|
|
|
|
```ts
|
|
// 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]].
|