tt-entities 0.0.53 → 0.0.55

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.
@@ -3,6 +3,7 @@ import {
3
3
  } from 'sequelize-typescript';
4
4
  import { User } from './user.entity';
5
5
  import { Order } from './order.entity';
6
+ import { Store } from './store.entity';
6
7
 
7
8
  export enum TransactionType {
8
9
  DEBIT = 'debit', // money leaves user balance
@@ -10,12 +11,12 @@ export enum TransactionType {
10
11
  }
11
12
 
12
13
  export enum TransactionCategory {
13
- ORDER_PAYMENT = 'order_payment', // DEBIT: paid for order
14
+ ORDER_PAYMENT = 'order_payment', // DEBIT: paid for order
14
15
  ORDER_REFUND = 'order_refund', // CREDIT: refund received
15
- GIFT_SENT = 'gift_sent', // DEBIT: sent gift to someone
16
+ GIFT_SENT = 'gift_sent', // DEBIT: sent gift to someone
16
17
  GIFT_RECEIVED = 'gift_received', // CREDIT: received a gift
17
18
  BALANCE_TOP_UP = 'balance_top_up', // CREDIT: topped up wallet
18
- BALANCE_WITHDRAWAL = 'balance_withdrawal', // DEBIT: withdrew balance
19
+ BALANCE_WITHDRAWAL = 'balance_withdrawal', // DEBIT: withdrew balance
19
20
  COUPON_CREDIT = 'coupon_credit', // CREDIT: coupon cashback
20
21
  ADJUSTMENT = 'adjustment', // DEBIT or CREDIT: manual admin adjustment
21
22
  CASHBACK = 'cashback', // CREDIT: loyalty cashback
@@ -27,6 +28,15 @@ export class UserTransaction extends Model {
27
28
  @Column({ allowNull: false })
28
29
  declare userId: number;
29
30
 
31
+ /**
32
+ * The store this transaction belongs to.
33
+ * NULL only for legacy rows migrated before this column existed.
34
+ * All new transactions MUST have a storeId so the balance is store-scoped.
35
+ */
36
+ @ForeignKey(() => Store)
37
+ @Column({ allowNull: true })
38
+ declare storeId?: number;
39
+
30
40
  @Column({
31
41
  type: DataType.ENUM(...Object.values(TransactionType)),
32
42
  allowNull: false,
@@ -43,27 +53,28 @@ export class UserTransaction extends Model {
43
53
  @Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
44
54
  declare amount: number;
45
55
 
46
- // Running balance AFTER this transaction
56
+ /**
57
+ * Running balance for this (userId, storeId) pair AFTER this transaction.
58
+ * Previously this mirrored User.walletBalance which was global — now it
59
+ * correctly reflects the per-store balance.
60
+ */
47
61
  @Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
48
62
  declare balanceAfter: number;
49
63
 
50
- // Currency of the transaction
64
+ // Currency must always match the store's currency
51
65
  @Column({ allowNull: false, defaultValue: 'KWD' })
52
66
  declare currency: string;
53
67
 
54
- // Bilingual description shown in the app
55
68
  @Column({ allowNull: false })
56
69
  declare descriptionEn: string;
57
70
 
58
71
  @Column({ allowNull: false })
59
72
  declare descriptionAr: string;
60
73
 
61
- // Reference to the related order (if applicable)
62
74
  @ForeignKey(() => Order)
63
75
  @Column({ allowNull: true })
64
76
  declare orderId?: number;
65
77
 
66
- // External reference (payment gateway transaction ID, etc.)
67
78
  @Column({ allowNull: true })
68
79
  declare externalReference?: string;
69
80
 
@@ -71,11 +82,13 @@ export class UserTransaction extends Model {
71
82
  @Column({ allowNull: false, defaultValue: 'completed' })
72
83
  declare status: string;
73
84
 
74
- // ── Associations ──────────────────────────────────────────────────────────────
75
-
85
+ // ── Associations ──────────────────────────────────────────────────────────
76
86
  @BelongsTo(() => User)
77
87
  declare user: User;
78
88
 
89
+ @BelongsTo(() => Store)
90
+ declare store?: Store;
91
+
79
92
  @BelongsTo(() => Order)
80
93
  declare order?: Order;
81
94
  }
@@ -0,0 +1,50 @@
1
+ import {
2
+ Model,
3
+ Table,
4
+ Column,
5
+ ForeignKey,
6
+ BelongsTo,
7
+ DataType,
8
+ Unique,
9
+ } from 'sequelize-typescript';
10
+ import { User } from './user.entity';
11
+ import { Store } from './store.entity';
12
+
13
+ /**
14
+ * UserWalletBalance — per-user, per-store wallet balance.
15
+ *
16
+ * WHY: A user may shop in multiple stores (Kuwait, Saudi, UAE...) each with
17
+ * their own currency. Wallet credits from a Saudi store refund can only be
18
+ * spent in that same store. Mixing them into a single `walletBalance` on User
19
+ * would mean 30 KWD + 120 SAR = meaningless 150.
20
+ *
21
+ * One row per (userId, storeId) pair. Created on first credit if it doesn't
22
+ * exist yet (upsert pattern).
23
+ */
24
+ @Table
25
+ export class UserWalletBalance extends Model {
26
+ @ForeignKey(() => User)
27
+ @Column({ allowNull: false })
28
+ declare userId: number;
29
+
30
+ @ForeignKey(() => Store)
31
+ @Column({ allowNull: false })
32
+ declare storeId: number;
33
+
34
+ @Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
35
+ declare balance: number;
36
+
37
+ /**
38
+ * Denormalized from Store.currency — avoids a join every time we need to
39
+ * show the currency symbol. Updated automatically when Store currency changes
40
+ * (rare, but handled in WalletService.credit / WalletService.debit).
41
+ */
42
+ @Column({ allowNull: false, defaultValue: 'KWD' })
43
+ declare currency: string;
44
+
45
+ @BelongsTo(() => User)
46
+ declare user: User;
47
+
48
+ @BelongsTo(() => Store)
49
+ declare store: Store;
50
+ }
@@ -28,6 +28,9 @@ export class User extends Model {
28
28
  @Column
29
29
  declare password: string;
30
30
 
31
+ @Column({ allowNull: true, defaultValue: null })
32
+ declare legacyPasswordHash?: string;
33
+
31
34
  @Column({
32
35
  type: DataType.ENUM(...Object.values(Status)),
33
36
  defaultValue: Status.ACTIVE,
@@ -80,6 +80,7 @@ import { RunSheetOrder } from './entities/run-sheet-order.entity';
80
80
  import { Refund } from './entities/refund.entity';
81
81
  import { PurchaseOrderReceipt } from './entities/purchase-order-receipt.entity';
82
82
  import { PurchaseOrderReceiptItem } from './entities/purchase-order-receipt-item.entity';
83
+ import { UserWalletBalance } from './entities/user-wallet-balance.entity';
83
84
 
84
85
  // =============================================================================
85
86
  // EXPORTS
@@ -113,6 +114,8 @@ export {
113
114
  } from './entities/run-sheet-order.entity';
114
115
 
115
116
  export { Refund, RefundMethod, RefundStatus } from './entities/refund.entity';
117
+ export { UserWalletBalance } from './entities/user-wallet-balance.entity';
118
+
116
119
 
117
120
 
118
121
  // ─── Catalogue ────────────────────────────────────────────────────────────────
@@ -229,6 +232,8 @@ export function getDbModels(): ModelCtor[] {
229
232
  UserAddress,
230
233
  UserFavorite,
231
234
  UserFavoriteCatalog,
235
+ UserTransaction,
236
+ UserWalletBalance,
232
237
 
233
238
  // Catalogue
234
239
  Category,
@@ -289,7 +294,6 @@ export function getDbModels(): ModelCtor[] {
289
294
  OrderItem,
290
295
  OrderItemBundleSelection,
291
296
  OrderStatusHistory,
292
- UserTransaction,
293
297
  PurchaseOrderOrderItem,
294
298
 
295
299
  // Banners
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tt-entities",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "description": "Tatayab entities library",
5
5
  "main": "dist/libs/tatayab-entities-library/index.js",
6
6
  "types": "dist/libs/tatayab-entities-library/index.d.ts",