@classytic/loyalty 0.7.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/dist/constants-DfrENxFG.mjs +75 -0
- package/dist/domain/earn-base.d.mts +35 -0
- package/dist/domain/earn-base.mjs +37 -0
- package/dist/domain/enums/index.d.mts +2 -2
- package/dist/domain/enums/index.mjs +1 -2
- package/dist/domain/rule-schedule.d.mts +24 -0
- package/dist/domain/rule-schedule.mjs +12 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +3 -3
- package/dist/{events-BLjg3uLA.mjs → events-DhZolW61.mjs} +104 -4
- package/dist/{index-BIPHUsfR.d.mts → index-BvyzKU7R.d.mts} +283 -34
- package/dist/{index-BIhVuwIZ.d.mts → index-CorB0JL6.d.mts} +255 -109
- package/dist/index-DRrEhE0r.d.mts +604 -0
- package/dist/{index-YUFamhDk.d.mts → index-Db5Gb7x_.d.mts} +187 -39
- package/dist/index-tjMMmPzU.d.mts +36 -0
- package/dist/index.d.mts +121 -30
- package/dist/index.mjs +59 -10
- package/dist/{loyalty-events-DXirolM_.mjs → loyalty-events-j29B66YW.mjs} +20 -13
- package/dist/models/index.d.mts +2 -2
- package/dist/models/index.mjs +1 -2
- package/dist/{models-weWqS5Bc.mjs → models-BEGLduz1.mjs} +276 -7
- package/dist/notifications/index.d.mts +3 -4
- package/dist/repositories/index.d.mts +2 -2
- package/dist/repositories/index.mjs +1 -2
- package/dist/{repositories-DErtHQou.mjs → repositories-ER68JmUo.mjs} +1161 -99
- package/dist/types/index.d.mts +2 -3
- package/package.json +39 -22
- package/dist/config-CtkGj3xZ.d.mts +0 -178
- package/dist/constants-DOPBh6kt.mjs +0 -48
- package/dist/index-Cp_sXNpG.d.mts +0 -349
- package/dist/index-D_Oe3aDk.d.mts +0 -19
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,55 @@
|
|
|
1
|
-
import "./constants-
|
|
2
|
-
import {
|
|
3
|
-
import { E as
|
|
4
|
-
import { t as defineLoyaltyModels } from "./models-
|
|
5
|
-
import {
|
|
1
|
+
import "./constants-DfrENxFG.mjs";
|
|
2
|
+
import { A as SessionNotInTransactionError, C as MemberNotFoundError, D as ReferralNotFoundError, E as ReferralLimitExceededError, M as TierNotFoundError, N as UnmanagedSessionError, O as RuleNotFoundError, P as ValidationError, S as MemberInactiveError, T as RedemptionNotFoundError, _ as CircularReferralError, a as StampRepository, b as LoyaltyCapabilityError, c as stampProgramPatchSchema, d as RedemptionRepository, f as PointTransactionRepository, g as EarningRuleRepository, h as flushPendingLoyaltyEvents, i as StampAwardRepository, j as TenantIsolationError, k as SelfReferralError, l as stampsForOrder, m as PENDING_EVENTS, n as TierDefinitionRepository, o as assertStampProgram, p as MemberRepository, r as StampProgramRepository, s as stampProgramInputSchema, t as createRepositories, u as ReferralRepository, v as DuplicateReferralError, w as RedemptionExpiredError, x as MemberAlreadyEnrolledError, y as InsufficientPointsError } from "./repositories-ER68JmUo.mjs";
|
|
3
|
+
import { A as loyaltyEventDefinitions, C as StampEarned, E as TierGraceStarted, S as StampAwardIssued, T as TierDowngraded, a as MemberSuspended, b as ReferralRejected, c as PointsEarned, f as PointsRedeemed, g as RedemptionReserved, h as RedemptionReleased, i as MemberReactivated, j as InProcessLoyaltyBus, k as TierUpgraded, l as PointsExpired, m as RedemptionConfirmed, n as MemberDeactivated, p as PointsReversed, r as MemberEnrolled, s as PointsAdjusted, u as PointsExpiringSoon, v as ReferralApproved, w as StampReversed, x as ReferralRewarded, y as ReferralRecorded } from "./events-DhZolW61.mjs";
|
|
4
|
+
import { t as defineLoyaltyModels } from "./models-BEGLduz1.mjs";
|
|
5
|
+
import { isRuleInSchedule } from "./domain/rule-schedule.mjs";
|
|
6
|
+
import { n as createEvent, t as LoyaltyEvents } from "./loyalty-events-j29B66YW.mjs";
|
|
7
|
+
import { earnBase } from "./domain/earn-base.mjs";
|
|
6
8
|
import { transactionResolutionOf, withTransaction } from "@classytic/mongokit";
|
|
7
9
|
import { isValidTimeZone } from "@classytic/primitives/timezone";
|
|
8
10
|
import { assertNoLegacyTenantKeys, resolveTenantConfig } from "@classytic/repo-core/tenant";
|
|
11
|
+
//#region src/domain/program-settings.ts
|
|
12
|
+
function assertProgram(p) {
|
|
13
|
+
if (!Number.isSafeInteger(p.pointValueMinor) || p.pointValueMinor <= 0) throw new ValidationError("pointValueMinor must be a positive integer (minor units per point)");
|
|
14
|
+
if (p.redemption.maxRedeemPercent < 0 || p.redemption.maxRedeemPercent > 100) throw new ValidationError("maxRedeemPercent must be between 0 and 100");
|
|
15
|
+
if (p.expiry.enabled && !(Number.isSafeInteger(p.expiry.defaultDays) && p.expiry.defaultDays >= 1)) throw new ValidationError("expiry.enabled requires expiry.defaultDays as a whole number of days >= 1");
|
|
16
|
+
}
|
|
17
|
+
/** The shape's own program settings. */
|
|
18
|
+
function programFromConfig(config) {
|
|
19
|
+
return {
|
|
20
|
+
pointValueMinor: config.pointValueMinor,
|
|
21
|
+
redemption: {
|
|
22
|
+
minPoints: config.redemption.minPoints,
|
|
23
|
+
minOrderAmount: config.redemption.minOrderAmount,
|
|
24
|
+
maxRedeemPercent: config.redemption.maxRedeemPercent
|
|
25
|
+
},
|
|
26
|
+
expiry: config.expiry
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** The per-operation reader: live settings over the shape's, validated every time. */
|
|
30
|
+
function createProgramSettings(config, live) {
|
|
31
|
+
const base = programFromConfig(config);
|
|
32
|
+
if (!live) return async () => base;
|
|
33
|
+
return async () => {
|
|
34
|
+
const l = await live();
|
|
35
|
+
const r = l.redemption ?? {};
|
|
36
|
+
const program = {
|
|
37
|
+
pointValueMinor: l.pointValueMinor ?? base.pointValueMinor,
|
|
38
|
+
redemption: {
|
|
39
|
+
minPoints: r.minPoints ?? base.redemption.minPoints,
|
|
40
|
+
minOrderAmount: r.minOrderAmount ?? base.redemption.minOrderAmount,
|
|
41
|
+
maxRedeemPercent: r.maxRedeemPercent ?? base.redemption.maxRedeemPercent
|
|
42
|
+
},
|
|
43
|
+
expiry: l.expiryDays === void 0 ? base.expiry : l.expiryDays === null ? { enabled: false } : {
|
|
44
|
+
enabled: true,
|
|
45
|
+
defaultDays: l.expiryDays
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
assertProgram(program);
|
|
49
|
+
return program;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
9
53
|
//#region src/index.ts
|
|
10
54
|
function resolveConfig(config) {
|
|
11
55
|
const tenant = resolveTenantConfig(config.tenant);
|
|
@@ -36,11 +80,14 @@ function resolveConfig(config) {
|
|
|
36
80
|
...config.timezone !== void 0 ? { timezone: config.timezone } : {},
|
|
37
81
|
tenant
|
|
38
82
|
};
|
|
83
|
+
assertResolvedConfig(resolved, config);
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
86
|
+
/** Fail loud at boot on a setting that would otherwise mis-evaluate silently at runtime. */
|
|
87
|
+
function assertResolvedConfig(resolved, config) {
|
|
39
88
|
if (config.timezone !== void 0 && !isValidTimeZone(config.timezone)) throw new ValidationError(`Invalid IANA timezone: '${config.timezone}'`);
|
|
40
|
-
if (!Number.isSafeInteger(resolved.pointValueMinor) || resolved.pointValueMinor <= 0) throw new ValidationError("pointValueMinor must be a positive integer (minor units per point)");
|
|
41
89
|
if (resolved.referral.codeLength < 4) throw new ValidationError("referral codeLength must be >= 4");
|
|
42
|
-
|
|
43
|
-
return resolved;
|
|
90
|
+
assertProgram(programFromConfig(resolved));
|
|
44
91
|
}
|
|
45
92
|
var MongoUnitOfWork = class {
|
|
46
93
|
connection;
|
|
@@ -120,7 +167,9 @@ function defineLoyalty(shape = {}) {
|
|
|
120
167
|
events,
|
|
121
168
|
outbox: runtime.outbox,
|
|
122
169
|
notifications: runtime.notifications ?? null,
|
|
123
|
-
unitOfWork
|
|
170
|
+
unitOfWork,
|
|
171
|
+
programSettings: createProgramSettings(resolvedConfig, runtime.programSettings),
|
|
172
|
+
rewardIssuer: runtime.rewardIssuer
|
|
124
173
|
});
|
|
125
174
|
{
|
|
126
175
|
const caps = repositories.pointTransaction.capabilities;
|
|
@@ -149,4 +198,4 @@ function defineLoyalty(shape = {}) {
|
|
|
149
198
|
};
|
|
150
199
|
}
|
|
151
200
|
//#endregion
|
|
152
|
-
export { CircularReferralError, DuplicateReferralError, EarningRuleRepository, InProcessLoyaltyBus, InsufficientPointsError, LoyaltyCapabilityError, LoyaltyEvents, MemberAlreadyEnrolledError, MemberDeactivated, MemberEnrolled, MemberInactiveError, MemberNotFoundError, MemberReactivated, MemberRepository, MemberSuspended, PENDING_EVENTS, PointTransactionRepository, PointsAdjusted, PointsEarned, PointsExpired, PointsExpiringSoon, PointsRedeemed, RedemptionConfirmed, RedemptionExpiredError, RedemptionNotFoundError, RedemptionReleased, RedemptionRepository, RedemptionReserved, ReferralApproved, ReferralLimitExceededError, ReferralNotFoundError, ReferralRecorded, ReferralRejected, ReferralRepository, ReferralRewarded, RuleNotFoundError, SelfReferralError, SessionNotInTransactionError, TenantIsolationError, TierDefinitionRepository, TierDowngraded, TierGraceStarted, TierNotFoundError, TierUpgraded, UnmanagedSessionError, ValidationError, createEvent, defineLoyalty, defineLoyaltyModels, flushPendingLoyaltyEvents, loyaltyEventDefinitions, resolveConfig };
|
|
201
|
+
export { CircularReferralError, DuplicateReferralError, EarningRuleRepository, InProcessLoyaltyBus, InsufficientPointsError, LoyaltyCapabilityError, LoyaltyEvents, MemberAlreadyEnrolledError, MemberDeactivated, MemberEnrolled, MemberInactiveError, MemberNotFoundError, MemberReactivated, MemberRepository, MemberSuspended, PENDING_EVENTS, PointTransactionRepository, PointsAdjusted, PointsEarned, PointsExpired, PointsExpiringSoon, PointsRedeemed, PointsReversed, RedemptionConfirmed, RedemptionExpiredError, RedemptionNotFoundError, RedemptionReleased, RedemptionRepository, RedemptionReserved, ReferralApproved, ReferralLimitExceededError, ReferralNotFoundError, ReferralRecorded, ReferralRejected, ReferralRepository, ReferralRewarded, RuleNotFoundError, SelfReferralError, SessionNotInTransactionError, StampAwardIssued, StampAwardRepository, StampEarned, StampProgramRepository, StampRepository, StampReversed, TenantIsolationError, TierDefinitionRepository, TierDowngraded, TierGraceStarted, TierNotFoundError, TierUpgraded, UnmanagedSessionError, ValidationError, assertStampProgram, createEvent, defineLoyalty, defineLoyaltyModels, earnBase, flushPendingLoyaltyEvents, isRuleInSchedule, loyaltyEventDefinitions, resolveConfig, stampProgramInputSchema, stampProgramPatchSchema, stampsForOrder };
|
|
@@ -4,24 +4,25 @@ function createEvent$1(type, payload, ctx, meta) {
|
|
|
4
4
|
const base = {};
|
|
5
5
|
if (ctx?.actorId !== void 0) base.userId = ctx.actorId;
|
|
6
6
|
if (ctx?.organizationId !== void 0) base.organizationId = ctx.organizationId;
|
|
7
|
-
return createEvent(type, payload, {
|
|
7
|
+
return createEvent(type, toWire(payload), {
|
|
8
8
|
...base,
|
|
9
9
|
...meta
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/events/loyalty-events.ts
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* only — member, points ledger, tier, redemption, and referral.
|
|
19
|
-
*
|
|
20
|
-
* For registration with an arc-compatible event registry, see
|
|
21
|
-
* `loyalty-event-catalog.ts` in this directory — it exposes each event as
|
|
22
|
-
* a `LoyaltyEventDefinition` with a Zod payload schema + JSON Schema
|
|
23
|
-
* derived via `z.toJSONSchema()` (PACKAGE_RULES §18.5).
|
|
13
|
+
* The catalog declares every id a STRING. A document's `_id` is an ObjectId in process, so it
|
|
14
|
+
* is converted here — the one seam every loyalty event passes — or an in-process subscriber
|
|
15
|
+
* sees a different shape than the outbox delivers, and a strict consumer refuses it.
|
|
24
16
|
*/
|
|
17
|
+
function toWire(value) {
|
|
18
|
+
if (value === null || typeof value !== "object") return value;
|
|
19
|
+
if (value._bsontype === "ObjectId") return String(value);
|
|
20
|
+
if (value instanceof Date) return value;
|
|
21
|
+
if (Array.isArray(value)) return value.map(toWire);
|
|
22
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, toWire(v)]));
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/events/loyalty-events.ts
|
|
25
26
|
const LoyaltyEvents = {
|
|
26
27
|
MEMBER_ENROLLED: "loyalty.member.enrolled",
|
|
27
28
|
MEMBER_DEACTIVATED: "loyalty.member.deactivated",
|
|
@@ -30,7 +31,10 @@ const LoyaltyEvents = {
|
|
|
30
31
|
POINTS_EARNED: "loyalty.points.earned",
|
|
31
32
|
POINTS_REDEEMED: "loyalty.points.redeemed",
|
|
32
33
|
POINTS_ADJUSTED: "loyalty.points.adjusted",
|
|
34
|
+
POINTS_REVERSED: "loyalty.points.reversed",
|
|
33
35
|
POINTS_EXPIRED: "loyalty.points.expired",
|
|
36
|
+
/** One expired lot, dispatched atomically with its expiry — the per-fact companion of POINTS_EXPIRED. */
|
|
37
|
+
POINTS_LOT_EXPIRED: "loyalty.points.lot_expired",
|
|
34
38
|
POINTS_EXPIRING_SOON: "loyalty.points.expiring_soon",
|
|
35
39
|
TIER_UPGRADED: "loyalty.tier.upgraded",
|
|
36
40
|
TIER_DOWNGRADED: "loyalty.tier.downgraded",
|
|
@@ -46,7 +50,10 @@ const LoyaltyEvents = {
|
|
|
46
50
|
REFERRAL_APPROVED: "loyalty.referral.approved",
|
|
47
51
|
REFERRAL_REJECTED: "loyalty.referral.rejected",
|
|
48
52
|
REFERRAL_REWARDED: "loyalty.referral.rewarded",
|
|
49
|
-
MULTI_TIER_REFERRAL_AWARDED: "loyalty.referral.multi_tier_awarded"
|
|
53
|
+
MULTI_TIER_REFERRAL_AWARDED: "loyalty.referral.multi_tier_awarded",
|
|
54
|
+
STAMP_EARNED: "loyalty.stamp.earned",
|
|
55
|
+
STAMP_REVERSED: "loyalty.stamp.reversed",
|
|
56
|
+
STAMP_AWARD_ISSUED: "loyalty.stamp.award_issued"
|
|
50
57
|
};
|
|
51
58
|
//#endregion
|
|
52
59
|
export { createEvent$1 as n, LoyaltyEvents as t };
|
package/dist/models/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as LoyaltyModels, r as defineLoyaltyModels, t as DefineLoyaltyModelsConfig } from "../index-
|
|
1
|
+
import { T as LoyaltyModelName } from "../index-DRrEhE0r.mjs";
|
|
2
|
+
import { n as LoyaltyModels, r as defineLoyaltyModels, t as DefineLoyaltyModelsConfig } from "../index-CorB0JL6.mjs";
|
|
3
3
|
export { DefineLoyaltyModelsConfig, type LoyaltyModelName, LoyaltyModels, defineLoyaltyModels };
|
package/dist/models/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { defineLoyaltyModels };
|
|
1
|
+
export { t as defineLoyaltyModels } from "../models-BEGLduz1.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as REDEMPTION_STATUSES, c as RULE_TYPES, d as STAMP_PROGRAM_STATUSES, f as STAMP_REWARD_KINDS, i as MEMBER_STATUSES, l as STAMP_AWARD_STATUSES, o as REFERRAL_STATUSES, p as TRANSACTION_TYPES, r as EARN_SOURCES, s as RULE_STATUSES, u as STAMP_EARN_UNITS } from "./constants-DfrENxFG.mjs";
|
|
2
2
|
import { defineModels } from "@classytic/mongokit";
|
|
3
3
|
import mongoose from "mongoose";
|
|
4
4
|
import { mongooseMinorUnits } from "@classytic/validation/money";
|
|
@@ -212,12 +212,14 @@ function createMemberSchema() {
|
|
|
212
212
|
externalType: 1
|
|
213
213
|
}, { unique: true });
|
|
214
214
|
schema.index({ cardId: 1 }, {
|
|
215
|
+
name: "cardId_str",
|
|
215
216
|
unique: true,
|
|
216
|
-
partialFilterExpression: { cardId: { $
|
|
217
|
+
partialFilterExpression: { cardId: { $gte: "" } }
|
|
217
218
|
});
|
|
218
219
|
schema.index({ referralCode: 1 }, {
|
|
220
|
+
name: "referralCode_str",
|
|
219
221
|
unique: true,
|
|
220
|
-
partialFilterExpression: { referralCode: { $
|
|
222
|
+
partialFilterExpression: { referralCode: { $gte: "" } }
|
|
221
223
|
});
|
|
222
224
|
return schema;
|
|
223
225
|
}
|
|
@@ -251,6 +253,16 @@ function createPointTransactionSchema() {
|
|
|
251
253
|
type: String,
|
|
252
254
|
required: true
|
|
253
255
|
},
|
|
256
|
+
/** Earn rows: why the points were granted (closed set). */
|
|
257
|
+
source: {
|
|
258
|
+
type: String,
|
|
259
|
+
enum: EARN_SOURCES
|
|
260
|
+
},
|
|
261
|
+
/** Minor units one point was worth when this row was written (the programme rate then). */
|
|
262
|
+
pointValueMinor: {
|
|
263
|
+
type: Number,
|
|
264
|
+
min: 1
|
|
265
|
+
},
|
|
254
266
|
referenceType: { type: String },
|
|
255
267
|
referenceId: { type: String },
|
|
256
268
|
idempotencyKey: { type: String },
|
|
@@ -282,9 +294,18 @@ function createPointTransactionSchema() {
|
|
|
282
294
|
memberId: 1,
|
|
283
295
|
createdAt: -1
|
|
284
296
|
});
|
|
297
|
+
schema.index({
|
|
298
|
+
expiredAt: 1,
|
|
299
|
+
expiresAt: 1,
|
|
300
|
+
_id: 1
|
|
301
|
+
}, {
|
|
302
|
+
name: "expiry_sweep",
|
|
303
|
+
partialFilterExpression: { expiresAt: { $exists: true } }
|
|
304
|
+
});
|
|
285
305
|
schema.index({ idempotencyKey: 1 }, {
|
|
306
|
+
name: "idempotencyKey_str",
|
|
286
307
|
unique: true,
|
|
287
|
-
partialFilterExpression: { idempotencyKey: { $
|
|
308
|
+
partialFilterExpression: { idempotencyKey: { $gte: "" } }
|
|
288
309
|
});
|
|
289
310
|
return schema;
|
|
290
311
|
}
|
|
@@ -421,6 +442,224 @@ function createReferralSchema() {
|
|
|
421
442
|
return schema;
|
|
422
443
|
}
|
|
423
444
|
//#endregion
|
|
445
|
+
//#region src/models/schemas/stamp-award.schema.ts
|
|
446
|
+
function createStampAwardSchema() {
|
|
447
|
+
const schema = new mongoose.Schema({
|
|
448
|
+
stampProgramId: {
|
|
449
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
450
|
+
required: true,
|
|
451
|
+
ref: "LoyaltyStampProgram"
|
|
452
|
+
},
|
|
453
|
+
memberId: {
|
|
454
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
455
|
+
required: true,
|
|
456
|
+
ref: "LoyaltyMember"
|
|
457
|
+
},
|
|
458
|
+
sequence: {
|
|
459
|
+
type: Number,
|
|
460
|
+
required: true,
|
|
461
|
+
min: 1
|
|
462
|
+
},
|
|
463
|
+
awardKey: {
|
|
464
|
+
type: String,
|
|
465
|
+
required: true
|
|
466
|
+
},
|
|
467
|
+
entries: [{
|
|
468
|
+
_id: false,
|
|
469
|
+
entryId: {
|
|
470
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
471
|
+
required: true
|
|
472
|
+
},
|
|
473
|
+
stamps: {
|
|
474
|
+
type: Number,
|
|
475
|
+
required: true,
|
|
476
|
+
min: 1
|
|
477
|
+
}
|
|
478
|
+
}],
|
|
479
|
+
stampsConsumed: {
|
|
480
|
+
type: Number,
|
|
481
|
+
required: true,
|
|
482
|
+
min: 1
|
|
483
|
+
},
|
|
484
|
+
status: {
|
|
485
|
+
type: String,
|
|
486
|
+
enum: STAMP_AWARD_STATUSES,
|
|
487
|
+
required: true
|
|
488
|
+
},
|
|
489
|
+
reward: {
|
|
490
|
+
kind: {
|
|
491
|
+
type: String,
|
|
492
|
+
enum: STAMP_REWARD_KINDS,
|
|
493
|
+
required: true
|
|
494
|
+
},
|
|
495
|
+
points: { type: Number },
|
|
496
|
+
pointTransactionId: { type: mongoose.Schema.Types.ObjectId },
|
|
497
|
+
voucherProgramId: { type: String },
|
|
498
|
+
voucherId: { type: String },
|
|
499
|
+
code: { type: String }
|
|
500
|
+
},
|
|
501
|
+
awardedAt: {
|
|
502
|
+
type: Date,
|
|
503
|
+
required: true
|
|
504
|
+
},
|
|
505
|
+
issuedAt: { type: Date }
|
|
506
|
+
}, { timestamps: true });
|
|
507
|
+
schema.index({
|
|
508
|
+
stampProgramId: 1,
|
|
509
|
+
memberId: 1,
|
|
510
|
+
sequence: 1
|
|
511
|
+
}, {
|
|
512
|
+
name: "stamp_award_sequence",
|
|
513
|
+
unique: true
|
|
514
|
+
});
|
|
515
|
+
schema.index({ awardKey: 1 }, {
|
|
516
|
+
name: "stamp_award_key",
|
|
517
|
+
unique: true
|
|
518
|
+
});
|
|
519
|
+
return schema;
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
522
|
+
//#region src/models/schemas/stamp-entry.schema.ts
|
|
523
|
+
function createStampEntrySchema() {
|
|
524
|
+
const schema = new mongoose.Schema({
|
|
525
|
+
stampProgramId: {
|
|
526
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
527
|
+
required: true,
|
|
528
|
+
ref: "LoyaltyStampProgram"
|
|
529
|
+
},
|
|
530
|
+
memberId: {
|
|
531
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
532
|
+
required: true,
|
|
533
|
+
ref: "LoyaltyMember"
|
|
534
|
+
},
|
|
535
|
+
orderRef: {
|
|
536
|
+
type: String,
|
|
537
|
+
required: true
|
|
538
|
+
},
|
|
539
|
+
stamps: {
|
|
540
|
+
type: Number,
|
|
541
|
+
required: true,
|
|
542
|
+
min: 1
|
|
543
|
+
},
|
|
544
|
+
consumed: {
|
|
545
|
+
type: Number,
|
|
546
|
+
default: 0,
|
|
547
|
+
min: 0
|
|
548
|
+
},
|
|
549
|
+
earnedAt: {
|
|
550
|
+
type: Date,
|
|
551
|
+
required: true
|
|
552
|
+
},
|
|
553
|
+
expiresAt: { type: Date },
|
|
554
|
+
businessDay: {
|
|
555
|
+
type: String,
|
|
556
|
+
required: true
|
|
557
|
+
},
|
|
558
|
+
visitKey: { type: String },
|
|
559
|
+
branchId: { type: String },
|
|
560
|
+
reversedAt: { type: Date },
|
|
561
|
+
reversedStamps: {
|
|
562
|
+
type: Number,
|
|
563
|
+
min: 0
|
|
564
|
+
}
|
|
565
|
+
}, { timestamps: true });
|
|
566
|
+
schema.index({
|
|
567
|
+
stampProgramId: 1,
|
|
568
|
+
memberId: 1,
|
|
569
|
+
orderRef: 1
|
|
570
|
+
}, {
|
|
571
|
+
name: "stamp_order",
|
|
572
|
+
unique: true
|
|
573
|
+
});
|
|
574
|
+
schema.index({
|
|
575
|
+
stampProgramId: 1,
|
|
576
|
+
memberId: 1,
|
|
577
|
+
visitKey: 1
|
|
578
|
+
}, {
|
|
579
|
+
name: "stamp_visit_day",
|
|
580
|
+
unique: true,
|
|
581
|
+
partialFilterExpression: { visitKey: { $gte: "" } }
|
|
582
|
+
});
|
|
583
|
+
schema.index({ orderRef: 1 });
|
|
584
|
+
return schema;
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/models/schemas/stamp-program.schema.ts
|
|
588
|
+
/** Allow-lists default to ABSENT ("any"), never `[]` — an empty list is refused at write. */
|
|
589
|
+
const allowList = {
|
|
590
|
+
type: [String],
|
|
591
|
+
default: void 0
|
|
592
|
+
};
|
|
593
|
+
function createStampProgramSchema() {
|
|
594
|
+
const schema = new mongoose.Schema({
|
|
595
|
+
programId: {
|
|
596
|
+
type: String,
|
|
597
|
+
default: "default"
|
|
598
|
+
},
|
|
599
|
+
name: {
|
|
600
|
+
type: String,
|
|
601
|
+
required: true
|
|
602
|
+
},
|
|
603
|
+
description: { type: String },
|
|
604
|
+
status: {
|
|
605
|
+
type: String,
|
|
606
|
+
enum: STAMP_PROGRAM_STATUSES,
|
|
607
|
+
default: "active"
|
|
608
|
+
},
|
|
609
|
+
earn: {
|
|
610
|
+
unit: {
|
|
611
|
+
type: String,
|
|
612
|
+
enum: STAMP_EARN_UNITS,
|
|
613
|
+
required: true
|
|
614
|
+
},
|
|
615
|
+
categoryIds: allowList,
|
|
616
|
+
productIds: allowList,
|
|
617
|
+
perMinor: {
|
|
618
|
+
type: Number,
|
|
619
|
+
min: 1
|
|
620
|
+
},
|
|
621
|
+
minOrderMinor: {
|
|
622
|
+
type: Number,
|
|
623
|
+
min: 0
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
stampsRequired: {
|
|
627
|
+
type: Number,
|
|
628
|
+
required: true,
|
|
629
|
+
min: 1
|
|
630
|
+
},
|
|
631
|
+
stampValidityDays: {
|
|
632
|
+
type: Number,
|
|
633
|
+
min: 1
|
|
634
|
+
},
|
|
635
|
+
startsAt: { type: Date },
|
|
636
|
+
endsAt: { type: Date },
|
|
637
|
+
reward: {
|
|
638
|
+
kind: {
|
|
639
|
+
type: String,
|
|
640
|
+
enum: STAMP_REWARD_KINDS,
|
|
641
|
+
required: true
|
|
642
|
+
},
|
|
643
|
+
points: {
|
|
644
|
+
type: Number,
|
|
645
|
+
min: 1
|
|
646
|
+
},
|
|
647
|
+
voucherProgramId: { type: String }
|
|
648
|
+
},
|
|
649
|
+
maxRewardsPerMember: {
|
|
650
|
+
type: Number,
|
|
651
|
+
min: 1
|
|
652
|
+
},
|
|
653
|
+
branchIds: allowList,
|
|
654
|
+
metadata: { type: mongoose.Schema.Types.Mixed }
|
|
655
|
+
}, { timestamps: true });
|
|
656
|
+
schema.index({
|
|
657
|
+
programId: 1,
|
|
658
|
+
status: 1
|
|
659
|
+
});
|
|
660
|
+
return schema;
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
424
663
|
//#region src/models/schemas/tier-definition.schema.ts
|
|
425
664
|
function createTierDefinitionSchema() {
|
|
426
665
|
const schema = new mongoose.Schema({
|
|
@@ -486,7 +725,10 @@ const DEFAULT_COLLECTIONS = {
|
|
|
486
725
|
EarningRule: "loyalty_earning_rules",
|
|
487
726
|
TierDefinition: "loyalty_tier_definitions",
|
|
488
727
|
Redemption: "loyalty_redemptions",
|
|
489
|
-
Referral: "loyalty_referrals"
|
|
728
|
+
Referral: "loyalty_referrals",
|
|
729
|
+
StampProgram: "loyalty_stamp_programs",
|
|
730
|
+
StampEntry: "loyalty_stamp_entries",
|
|
731
|
+
StampAward: "loyalty_stamp_awards"
|
|
490
732
|
};
|
|
491
733
|
/** The registered Mongoose model names — stable, independent of `collectionPrefix`. */
|
|
492
734
|
const MODEL_NAMES = {
|
|
@@ -495,7 +737,10 @@ const MODEL_NAMES = {
|
|
|
495
737
|
EarningRule: "LoyaltyEarningRule",
|
|
496
738
|
TierDefinition: "LoyaltyTierDefinition",
|
|
497
739
|
Redemption: "LoyaltyRedemption",
|
|
498
|
-
Referral: "LoyaltyReferral"
|
|
740
|
+
Referral: "LoyaltyReferral",
|
|
741
|
+
StampProgram: "LoyaltyStampProgram",
|
|
742
|
+
StampEntry: "LoyaltyStampEntry",
|
|
743
|
+
StampAward: "LoyaltyStampAward"
|
|
499
744
|
};
|
|
500
745
|
const ENTITIES = [
|
|
501
746
|
{
|
|
@@ -539,6 +784,27 @@ const ENTITIES = [
|
|
|
539
784
|
collection: "Referral",
|
|
540
785
|
indexKey: "referral",
|
|
541
786
|
factory: createReferralSchema
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
key: "StampProgram",
|
|
790
|
+
modelName: MODEL_NAMES.StampProgram,
|
|
791
|
+
collection: "StampProgram",
|
|
792
|
+
indexKey: "stampProgram",
|
|
793
|
+
factory: createStampProgramSchema
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
key: "StampEntry",
|
|
797
|
+
modelName: MODEL_NAMES.StampEntry,
|
|
798
|
+
collection: "StampEntry",
|
|
799
|
+
indexKey: "stampEntry",
|
|
800
|
+
factory: createStampEntrySchema
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
key: "StampAward",
|
|
804
|
+
modelName: MODEL_NAMES.StampAward,
|
|
805
|
+
collection: "StampAward",
|
|
806
|
+
indexKey: "stampAward",
|
|
807
|
+
factory: createStampAwardSchema
|
|
542
808
|
}
|
|
543
809
|
];
|
|
544
810
|
/**
|
|
@@ -581,7 +847,10 @@ function defineLoyaltyModels(config) {
|
|
|
581
847
|
EarningRule: models.get(MODEL_NAMES.EarningRule),
|
|
582
848
|
TierDefinition: models.get(MODEL_NAMES.TierDefinition),
|
|
583
849
|
Redemption: models.get(MODEL_NAMES.Redemption),
|
|
584
|
-
Referral: models.get(MODEL_NAMES.Referral)
|
|
850
|
+
Referral: models.get(MODEL_NAMES.Referral),
|
|
851
|
+
StampProgram: models.get(MODEL_NAMES.StampProgram),
|
|
852
|
+
StampEntry: models.get(MODEL_NAMES.StampEntry),
|
|
853
|
+
StampAward: models.get(MODEL_NAMES.StampAward)
|
|
585
854
|
})
|
|
586
855
|
});
|
|
587
856
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as NotificationAdapter } from "../notification.port-wY4IKimz.mjs";
|
|
2
2
|
//#region src/notifications/index.d.ts
|
|
3
|
-
interface NotificationsAdapterConfig {
|
|
3
|
+
export interface NotificationsAdapterConfig {
|
|
4
4
|
service: {
|
|
5
5
|
send: (payload: unknown) => Promise<unknown>;
|
|
6
6
|
};
|
|
@@ -15,6 +15,5 @@ interface NotificationsAdapterConfig {
|
|
|
15
15
|
channels?: Record<string, string[]>;
|
|
16
16
|
skip?: string[];
|
|
17
17
|
}
|
|
18
|
-
declare function createNotificationsAdapter(config: NotificationsAdapterConfig): NotificationAdapter;
|
|
19
|
-
//#endregion
|
|
20
|
-
export { NotificationsAdapterConfig, createNotificationsAdapter };
|
|
18
|
+
export declare function createNotificationsAdapter(config: NotificationsAdapterConfig): NotificationAdapter;
|
|
19
|
+
//#endregion
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CreateRepositoriesInput, EarningRuleRepository, LoyaltyRepositories, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
|
|
1
|
+
import { _ as PointTransactionRepository, a as StampProgramRepository, g as EarningRuleRepository, i as StampRepository, l as RedemptionRepository, n as LoyaltyRepositories, o as StampAwardRepository, r as createRepositories, s as ReferralRepository, t as CreateRepositoriesInput, v as MemberRepository, y as TierDefinitionRepository } from "../index-BvyzKU7R.mjs";
|
|
2
|
+
export { CreateRepositoriesInput, EarningRuleRepository, LoyaltyRepositories, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, StampAwardRepository, StampProgramRepository, StampRepository, TierDefinitionRepository, createRepositories };
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { EarningRuleRepository, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
|
|
1
|
+
export { g as EarningRuleRepository, p as MemberRepository, f as PointTransactionRepository, d as RedemptionRepository, u as ReferralRepository, i as StampAwardRepository, r as StampProgramRepository, a as StampRepository, n as TierDefinitionRepository, t as createRepositories } from "../repositories-ER68JmUo.mjs";
|