tt-entities 0.0.32 → 0.0.34
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/dist/libs/tatayab-entities-library/entities/order-item-bundle-selection.entity.d.ts +17 -0
- package/dist/libs/tatayab-entities-library/entities/order-item-bundle-selection.entity.js +70 -0
- package/dist/libs/tatayab-entities-library/entities/order-item-bundle-selection.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/order-item.entity.d.ts +50 -0
- package/dist/libs/tatayab-entities-library/entities/order-item.entity.js +153 -0
- package/dist/libs/tatayab-entities-library/entities/order-item.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/order-status-history.entity.d.ts +12 -0
- package/dist/libs/tatayab-entities-library/entities/order-status-history.entity.js +54 -0
- package/dist/libs/tatayab-entities-library/entities/order-status-history.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/order-status.entity.d.ts +37 -0
- package/dist/libs/tatayab-entities-library/entities/order-status.entity.js +94 -0
- package/dist/libs/tatayab-entities-library/entities/order-status.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/order.entity.d.ts +57 -0
- package/dist/libs/tatayab-entities-library/entities/order.entity.js +219 -0
- package/dist/libs/tatayab-entities-library/entities/order.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/purchase-order-order-item.entity.d.ts +16 -0
- package/dist/libs/tatayab-entities-library/entities/purchase-order-order-item.entity.js +64 -0
- package/dist/libs/tatayab-entities-library/entities/purchase-order-order-item.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/user-transaction.entity.d.ts +33 -0
- package/dist/libs/tatayab-entities-library/entities/user-transaction.entity.js +99 -0
- package/dist/libs/tatayab-entities-library/entities/user-transaction.entity.js.map +1 -0
- package/dist/libs/tatayab-entities-library/entities/user.entity.d.ts +7 -0
- package/dist/libs/tatayab-entities-library/entities/user.entity.js +19 -0
- package/dist/libs/tatayab-entities-library/entities/user.entity.js.map +1 -1
- package/dist/libs/tatayab-entities-library/index.d.ts +7 -0
- package/dist/libs/tatayab-entities-library/index.js +34 -1
- package/dist/libs/tatayab-entities-library/index.js.map +1 -1
- package/dist/src/main.js +876 -2
- package/libs/tatayab-entities-library/src/entities/order-item-bundle-selection.entity.ts +38 -0
- package/libs/tatayab-entities-library/src/entities/order-item.entity.ts +132 -0
- package/libs/tatayab-entities-library/src/entities/order-status-history.entity.ts +34 -0
- package/libs/tatayab-entities-library/src/entities/order-status.entity.ts +70 -0
- package/libs/tatayab-entities-library/src/entities/order.entity.ts +175 -0
- package/libs/tatayab-entities-library/src/entities/purchase-order-order-item.entity.ts +38 -0
- package/libs/tatayab-entities-library/src/entities/user-transaction.entity.ts +81 -0
- package/libs/tatayab-entities-library/src/entities/user.entity.ts +15 -0
- package/libs/tatayab-entities-library/src/index.ts +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Table, Column, Model, ForeignKey, BelongsTo } from 'sequelize-typescript';
|
|
2
|
+
import { OrderItem } from './order-item.entity';
|
|
3
|
+
import { Product } from './product.entity';
|
|
4
|
+
import { ProductVariant } from './product-variant.entity';
|
|
5
|
+
|
|
6
|
+
@Table
|
|
7
|
+
export class OrderItemBundleSelection extends Model {
|
|
8
|
+
@ForeignKey(() => OrderItem)
|
|
9
|
+
@Column({ allowNull: false })
|
|
10
|
+
declare orderItemId: number;
|
|
11
|
+
|
|
12
|
+
@ForeignKey(() => Product)
|
|
13
|
+
@Column({ allowNull: false })
|
|
14
|
+
declare productId: number;
|
|
15
|
+
|
|
16
|
+
@ForeignKey(() => ProductVariant)
|
|
17
|
+
@Column({ allowNull: true })
|
|
18
|
+
declare productVariantId?: number;
|
|
19
|
+
|
|
20
|
+
@Column({ allowNull: true })
|
|
21
|
+
declare productNameEn?: string;
|
|
22
|
+
|
|
23
|
+
@Column({ allowNull: true })
|
|
24
|
+
declare productNameAr?: string;
|
|
25
|
+
|
|
26
|
+
@Column({ allowNull: true })
|
|
27
|
+
declare variantLabelEn?: string;
|
|
28
|
+
|
|
29
|
+
@Column({ allowNull: true })
|
|
30
|
+
declare variantLabelAr?: string;
|
|
31
|
+
|
|
32
|
+
@Column({ allowNull: false, defaultValue: 0 })
|
|
33
|
+
declare sortOrder: number;
|
|
34
|
+
|
|
35
|
+
@BelongsTo(() => OrderItem) declare orderItem: OrderItem;
|
|
36
|
+
@BelongsTo(() => Product) declare product: Product;
|
|
37
|
+
@BelongsTo(() => ProductVariant) declare variant?: ProductVariant;
|
|
38
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table,
|
|
3
|
+
Column,
|
|
4
|
+
Model,
|
|
5
|
+
ForeignKey,
|
|
6
|
+
BelongsTo,
|
|
7
|
+
HasMany,
|
|
8
|
+
HasOne,
|
|
9
|
+
DataType,
|
|
10
|
+
} from 'sequelize-typescript';
|
|
11
|
+
import { Order } from './order.entity';
|
|
12
|
+
import { Product } from './product.entity';
|
|
13
|
+
import { ProductVariant } from './product-variant.entity';
|
|
14
|
+
import { Bundle } from './bundle.entity';
|
|
15
|
+
import { OrderItemBundleSelection } from './order-item-bundle-selection.entity';
|
|
16
|
+
import { PurchaseOrderOrderItem } from './purchase-order-order-item.entity';
|
|
17
|
+
|
|
18
|
+
export enum OrderItemType {
|
|
19
|
+
PRODUCT = 'product',
|
|
20
|
+
BUNDLE = 'bundle',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum OrderItemFulfillmentStatus {
|
|
24
|
+
PENDING = 'pending',
|
|
25
|
+
AWAITING_STOCK = 'awaiting_stock', // needs vendor PO
|
|
26
|
+
PO_LINKED = 'po_linked', // PO created and linked
|
|
27
|
+
PO_RECEIVED = 'po_received', // PO received for this item
|
|
28
|
+
STOCK_RESERVED = 'stock_reserved', // physical stock locked in inventory
|
|
29
|
+
PACKED = 'packed',
|
|
30
|
+
SHIPPED = 'shipped',
|
|
31
|
+
DELIVERED = 'delivered',
|
|
32
|
+
CANCELLED = 'cancelled',
|
|
33
|
+
RETURN_REQUESTED = 'return_requested',
|
|
34
|
+
RETURNED = 'returned',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Table
|
|
38
|
+
export class OrderItem extends Model {
|
|
39
|
+
@ForeignKey(() => Order)
|
|
40
|
+
@Column({ allowNull: false })
|
|
41
|
+
declare orderId: number;
|
|
42
|
+
|
|
43
|
+
@Column({
|
|
44
|
+
type: DataType.ENUM(...Object.values(OrderItemType)),
|
|
45
|
+
allowNull: false,
|
|
46
|
+
defaultValue: OrderItemType.PRODUCT,
|
|
47
|
+
})
|
|
48
|
+
declare itemType: OrderItemType;
|
|
49
|
+
|
|
50
|
+
// ── Product fields ────────────────────────────────────────────────────────────
|
|
51
|
+
@ForeignKey(() => Product)
|
|
52
|
+
@Column({ allowNull: true })
|
|
53
|
+
declare productId?: number;
|
|
54
|
+
|
|
55
|
+
@ForeignKey(() => ProductVariant)
|
|
56
|
+
@Column({ allowNull: true })
|
|
57
|
+
declare productVariantId?: number;
|
|
58
|
+
|
|
59
|
+
// ── Bundle fields ─────────────────────────────────────────────────────────────
|
|
60
|
+
@ForeignKey(() => Bundle)
|
|
61
|
+
@Column({ allowNull: true })
|
|
62
|
+
declare bundleId?: number;
|
|
63
|
+
|
|
64
|
+
@Column({ allowNull: true })
|
|
65
|
+
declare composedSku?: string;
|
|
66
|
+
|
|
67
|
+
// ── Snapshots at order time ───────────────────────────────────────────────────
|
|
68
|
+
@Column({ allowNull: true })
|
|
69
|
+
declare productNameEn?: string;
|
|
70
|
+
|
|
71
|
+
@Column({ allowNull: true })
|
|
72
|
+
declare productNameAr?: string;
|
|
73
|
+
|
|
74
|
+
@Column({ allowNull: true })
|
|
75
|
+
declare sku?: string;
|
|
76
|
+
|
|
77
|
+
@Column({ allowNull: true })
|
|
78
|
+
declare variantLabelEn?: string;
|
|
79
|
+
|
|
80
|
+
@Column({ allowNull: true })
|
|
81
|
+
declare variantLabelAr?: string;
|
|
82
|
+
|
|
83
|
+
@Column({ allowNull: true })
|
|
84
|
+
declare productImage?: string;
|
|
85
|
+
|
|
86
|
+
// ── Pricing ───────────────────────────────────────────────────────────────────
|
|
87
|
+
@Column({ allowNull: false, defaultValue: 1 })
|
|
88
|
+
declare quantity: number;
|
|
89
|
+
|
|
90
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
|
|
91
|
+
declare unitPrice: number;
|
|
92
|
+
|
|
93
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
|
|
94
|
+
declare lineTotal: number;
|
|
95
|
+
|
|
96
|
+
// ── Fulfillment assignment ────────────────────────────────────────────────────
|
|
97
|
+
// Which inventory is responsible for this item
|
|
98
|
+
@Column({ allowNull: true })
|
|
99
|
+
declare fulfillmentInventoryId?: number;
|
|
100
|
+
|
|
101
|
+
// Shipment group — items with the same group ship together from the same inventory
|
|
102
|
+
// Usually '1' for single-inventory orders; '2' if a second inventory is needed
|
|
103
|
+
@Column({ allowNull: false, defaultValue: 1 })
|
|
104
|
+
declare shipmentGroup: number;
|
|
105
|
+
|
|
106
|
+
@Column({
|
|
107
|
+
type: DataType.ENUM(...Object.values(OrderItemFulfillmentStatus)),
|
|
108
|
+
allowNull: false,
|
|
109
|
+
defaultValue: OrderItemFulfillmentStatus.PENDING,
|
|
110
|
+
})
|
|
111
|
+
declare fulfillmentStatus: OrderItemFulfillmentStatus;
|
|
112
|
+
|
|
113
|
+
// ── Associations ──────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
@BelongsTo(() => Order)
|
|
116
|
+
declare order: Order;
|
|
117
|
+
|
|
118
|
+
@BelongsTo(() => Product)
|
|
119
|
+
declare product?: Product;
|
|
120
|
+
|
|
121
|
+
@BelongsTo(() => ProductVariant)
|
|
122
|
+
declare variant?: ProductVariant;
|
|
123
|
+
|
|
124
|
+
@BelongsTo(() => Bundle)
|
|
125
|
+
declare bundle?: Bundle;
|
|
126
|
+
|
|
127
|
+
@HasMany(() => OrderItemBundleSelection)
|
|
128
|
+
declare bundleSelections: OrderItemBundleSelection[];
|
|
129
|
+
|
|
130
|
+
@HasOne(() => PurchaseOrderOrderItem)
|
|
131
|
+
declare poLink?: PurchaseOrderOrderItem;
|
|
132
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table, Column, Model, ForeignKey, BelongsTo, DataType,
|
|
3
|
+
} from 'sequelize-typescript';
|
|
4
|
+
import { Order } from './order.entity';
|
|
5
|
+
|
|
6
|
+
@Table
|
|
7
|
+
export class OrderStatusHistory extends Model {
|
|
8
|
+
@ForeignKey(() => Order)
|
|
9
|
+
@Column({ allowNull: false })
|
|
10
|
+
declare orderId: number;
|
|
11
|
+
|
|
12
|
+
@Column({ allowNull: true })
|
|
13
|
+
declare fromStatus?: string;
|
|
14
|
+
|
|
15
|
+
@Column({ allowNull: false })
|
|
16
|
+
declare toStatus: string;
|
|
17
|
+
|
|
18
|
+
// Bilingual label snapshots so history is readable even if status labels change
|
|
19
|
+
@Column({ allowNull: true })
|
|
20
|
+
declare toLabelEn?: string;
|
|
21
|
+
|
|
22
|
+
@Column({ allowNull: true })
|
|
23
|
+
declare toLabelAr?: string;
|
|
24
|
+
|
|
25
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
26
|
+
declare note?: string;
|
|
27
|
+
|
|
28
|
+
// Who made the change: 'system', 'user', or 'admin:<id>'
|
|
29
|
+
@Column({ allowNull: true })
|
|
30
|
+
declare changedBy?: string;
|
|
31
|
+
|
|
32
|
+
@BelongsTo(() => Order)
|
|
33
|
+
declare order: Order;
|
|
34
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table, Column, Model, HasMany, DataType,
|
|
3
|
+
} from 'sequelize-typescript';
|
|
4
|
+
import { Order } from './order.entity';
|
|
5
|
+
|
|
6
|
+
// All possible order statuses in the system
|
|
7
|
+
export enum OrderStatusCode {
|
|
8
|
+
PENDING_PAYMENT = 'pending_payment',
|
|
9
|
+
PAID = 'paid',
|
|
10
|
+
PROCESSING = 'processing',
|
|
11
|
+
AWAITING_FULFILLMENT = 'awaiting_fulfillment',
|
|
12
|
+
PENDING_VENDOR = 'pending_vendor',
|
|
13
|
+
VENDOR_ORDERED = 'vendor_ordered',
|
|
14
|
+
STOCK_RECEIVED = 'stock_received',
|
|
15
|
+
PACKED = 'packed',
|
|
16
|
+
SHIPPED = 'shipped',
|
|
17
|
+
OUT_FOR_DELIVERY = 'out_for_delivery',
|
|
18
|
+
DELIVERED = 'delivered',
|
|
19
|
+
COMPLETED = 'completed',
|
|
20
|
+
CANCELLED = 'cancelled',
|
|
21
|
+
RETURN_REQUESTED = 'return_requested',
|
|
22
|
+
RETURN_APPROVED = 'return_approved',
|
|
23
|
+
RETURNED = 'returned',
|
|
24
|
+
REFUNDED = 'refunded',
|
|
25
|
+
FAILED = 'failed',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Table
|
|
29
|
+
export class OrderStatus extends Model {
|
|
30
|
+
@Column({ allowNull: false, unique: true })
|
|
31
|
+
declare code: string;
|
|
32
|
+
|
|
33
|
+
@Column({ allowNull: false })
|
|
34
|
+
declare labelEn: string;
|
|
35
|
+
|
|
36
|
+
@Column({ allowNull: false })
|
|
37
|
+
declare labelAr: string;
|
|
38
|
+
|
|
39
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
40
|
+
declare descriptionEn?: string;
|
|
41
|
+
|
|
42
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
43
|
+
declare descriptionAr?: string;
|
|
44
|
+
|
|
45
|
+
@Column({ allowNull: true })
|
|
46
|
+
declare color?: string;
|
|
47
|
+
|
|
48
|
+
@Column({ allowNull: true })
|
|
49
|
+
declare icon?: string;
|
|
50
|
+
|
|
51
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
52
|
+
declare nextStatuses?: string;
|
|
53
|
+
|
|
54
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
55
|
+
declare prevStatuses?: string;
|
|
56
|
+
|
|
57
|
+
@Column({ allowNull: false, defaultValue: false })
|
|
58
|
+
declare isTerminal: boolean;
|
|
59
|
+
|
|
60
|
+
@Column({ allowNull: false, defaultValue: true })
|
|
61
|
+
declare isVisibleToUser: boolean;
|
|
62
|
+
|
|
63
|
+
@Column({ allowNull: false, defaultValue: 0 })
|
|
64
|
+
declare sortOrder: number;
|
|
65
|
+
|
|
66
|
+
// ─── Associations ─────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
@HasMany(() => Order, { foreignKey: 'statusId' })
|
|
69
|
+
declare orders: Order[];
|
|
70
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table, Column, Model, ForeignKey, BelongsTo,
|
|
3
|
+
HasMany, DataType,
|
|
4
|
+
} from 'sequelize-typescript';
|
|
5
|
+
import { User } from './user.entity';
|
|
6
|
+
import { Store } from './store.entity';
|
|
7
|
+
import { Country } from './country.entity';
|
|
8
|
+
import { OrderStatus } from './order-status.entity';
|
|
9
|
+
import { OrderItem } from './order-item.entity';
|
|
10
|
+
import { OrderStatusHistory } from './order-status-history.entity';
|
|
11
|
+
import { UserTransaction } from './user-transaction.entity';
|
|
12
|
+
|
|
13
|
+
@Table
|
|
14
|
+
export class Order extends Model {
|
|
15
|
+
// ── Identity ──────────────────────────────────────────────────────────────────
|
|
16
|
+
@Column({ allowNull: false, unique: true })
|
|
17
|
+
declare orderNumber: string; // e.g. TAT-2026-00001
|
|
18
|
+
|
|
19
|
+
// Null for guest orders — linked on account creation
|
|
20
|
+
@ForeignKey(() => User)
|
|
21
|
+
@Column({ allowNull: true })
|
|
22
|
+
declare userId?: number;
|
|
23
|
+
|
|
24
|
+
// Guest token for guest orders
|
|
25
|
+
@Column({ allowNull: true })
|
|
26
|
+
declare guestToken?: string;
|
|
27
|
+
|
|
28
|
+
@ForeignKey(() => Store)
|
|
29
|
+
@Column({ allowNull: false })
|
|
30
|
+
declare storeId: number;
|
|
31
|
+
|
|
32
|
+
// ── Status ────────────────────────────────────────────────────────────────────
|
|
33
|
+
// Proper FK to order_statuses so we can eager load with include: [OrderStatus]
|
|
34
|
+
@ForeignKey(() => OrderStatus)
|
|
35
|
+
@Column({ allowNull: false })
|
|
36
|
+
declare statusId: number;
|
|
37
|
+
|
|
38
|
+
// Keep statusCode as a convenience column — synced with statusId at all times
|
|
39
|
+
// Allows filtering by code string without a join: where({ statusCode: 'shipped' })
|
|
40
|
+
@Column({ allowNull: false, defaultValue: 'pending_payment' })
|
|
41
|
+
declare statusCode: string;
|
|
42
|
+
|
|
43
|
+
// ── Delivery address (snapshot at order time) ─────────────────────────────────
|
|
44
|
+
@ForeignKey(() => Country)
|
|
45
|
+
@Column({ allowNull: true })
|
|
46
|
+
declare deliveryCountryId?: number;
|
|
47
|
+
|
|
48
|
+
@Column({ allowNull: true })
|
|
49
|
+
declare deliveryFullName?: string;
|
|
50
|
+
|
|
51
|
+
@Column({ allowNull: true })
|
|
52
|
+
declare deliveryPhoneExt?: string;
|
|
53
|
+
|
|
54
|
+
@Column({ allowNull: true })
|
|
55
|
+
declare deliveryPhone?: string;
|
|
56
|
+
|
|
57
|
+
@Column({ allowNull: true })
|
|
58
|
+
declare deliveryCity?: string;
|
|
59
|
+
|
|
60
|
+
@Column({ allowNull: true })
|
|
61
|
+
declare deliveryBlock?: string;
|
|
62
|
+
|
|
63
|
+
@Column({ allowNull: true })
|
|
64
|
+
declare deliveryStreet?: string;
|
|
65
|
+
|
|
66
|
+
@Column({ allowNull: true })
|
|
67
|
+
declare deliveryBuilding?: string;
|
|
68
|
+
|
|
69
|
+
@Column({ allowNull: true })
|
|
70
|
+
declare deliveryFloor?: string;
|
|
71
|
+
|
|
72
|
+
@Column({ allowNull: true })
|
|
73
|
+
declare deliveryApartment?: string;
|
|
74
|
+
|
|
75
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
76
|
+
declare deliveryAdditionalInfo?: string;
|
|
77
|
+
|
|
78
|
+
@Column({ type: DataType.DECIMAL(9, 6), allowNull: true })
|
|
79
|
+
declare deliveryLatitude?: number;
|
|
80
|
+
|
|
81
|
+
@Column({ type: DataType.DECIMAL(9, 6), allowNull: true })
|
|
82
|
+
declare deliveryLongitude?: number;
|
|
83
|
+
|
|
84
|
+
// ── Financials (all in store currency) ────────────────────────────────────────
|
|
85
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
86
|
+
declare subtotal: number;
|
|
87
|
+
|
|
88
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
89
|
+
declare shippingFee: number;
|
|
90
|
+
|
|
91
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
92
|
+
declare vatAmount: number;
|
|
93
|
+
|
|
94
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
95
|
+
declare discountAmount: number;
|
|
96
|
+
|
|
97
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
98
|
+
declare total: number;
|
|
99
|
+
|
|
100
|
+
// Currency snapshot
|
|
101
|
+
@Column({ allowNull: false })
|
|
102
|
+
declare currency: string;
|
|
103
|
+
|
|
104
|
+
@Column({ allowNull: true })
|
|
105
|
+
declare currencySymbolEn?: string;
|
|
106
|
+
|
|
107
|
+
// ── Coupon ────────────────────────────────────────────────────────────────────
|
|
108
|
+
@Column({ allowNull: true })
|
|
109
|
+
declare couponCode?: string;
|
|
110
|
+
|
|
111
|
+
@Column({ allowNull: true })
|
|
112
|
+
declare couponId?: number;
|
|
113
|
+
|
|
114
|
+
// ── Payment ───────────────────────────────────────────────────────────────────
|
|
115
|
+
@Column({ allowNull: true })
|
|
116
|
+
declare paymentMethodSlug?: string;
|
|
117
|
+
|
|
118
|
+
@Column({ allowNull: true })
|
|
119
|
+
declare paymentStatus?: string; // pending | paid | failed | refunded
|
|
120
|
+
|
|
121
|
+
@Column({ allowNull: true })
|
|
122
|
+
declare paymentReference?: string; // Gateway transaction ID
|
|
123
|
+
|
|
124
|
+
@Column({ type: DataType.DATE, allowNull: true })
|
|
125
|
+
declare paidAt?: Date;
|
|
126
|
+
|
|
127
|
+
// ── Shipping ─────────────────────────────────────────────────────────────────
|
|
128
|
+
@Column({ allowNull: true })
|
|
129
|
+
declare carrierName?: string;
|
|
130
|
+
|
|
131
|
+
@Column({ allowNull: true })
|
|
132
|
+
declare trackingNumber?: string;
|
|
133
|
+
|
|
134
|
+
@Column({ allowNull: true })
|
|
135
|
+
declare trackingUrl?: string;
|
|
136
|
+
|
|
137
|
+
@Column({ type: DataType.DATE, allowNull: true })
|
|
138
|
+
declare estimatedDeliveryDate?: Date;
|
|
139
|
+
|
|
140
|
+
@Column({ type: DataType.DATE, allowNull: true })
|
|
141
|
+
declare shippedAt?: Date;
|
|
142
|
+
|
|
143
|
+
@Column({ type: DataType.DATE, allowNull: true })
|
|
144
|
+
declare deliveredAt?: Date;
|
|
145
|
+
|
|
146
|
+
// ── Notes ─────────────────────────────────────────────────────────────────────
|
|
147
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
148
|
+
declare customerNotes?: string;
|
|
149
|
+
|
|
150
|
+
@Column({ type: DataType.TEXT, allowNull: true })
|
|
151
|
+
declare internalNotes?: string;
|
|
152
|
+
|
|
153
|
+
// ── Associations ──────────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
@BelongsTo(() => User)
|
|
156
|
+
declare user?: User;
|
|
157
|
+
|
|
158
|
+
@BelongsTo(() => Store)
|
|
159
|
+
declare store: Store;
|
|
160
|
+
|
|
161
|
+
@BelongsTo(() => OrderStatus, { foreignKey: 'statusId' })
|
|
162
|
+
declare status: OrderStatus;
|
|
163
|
+
|
|
164
|
+
@BelongsTo(() => Country)
|
|
165
|
+
declare deliveryCountry?: Country;
|
|
166
|
+
|
|
167
|
+
@HasMany(() => OrderItem)
|
|
168
|
+
declare items: OrderItem[];
|
|
169
|
+
|
|
170
|
+
@HasMany(() => OrderStatusHistory)
|
|
171
|
+
declare statusHistory: OrderStatusHistory[];
|
|
172
|
+
|
|
173
|
+
@HasMany(() => UserTransaction)
|
|
174
|
+
declare transactions: UserTransaction[];
|
|
175
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table, Column, Model, ForeignKey, BelongsTo, DataType,
|
|
3
|
+
} from 'sequelize-typescript';
|
|
4
|
+
import { PurchaseOrder } from './purchase-order.entity';
|
|
5
|
+
import { PurchaseOrderItem } from './purchase-order-item.entity';
|
|
6
|
+
import { OrderItem } from './order-item.entity';
|
|
7
|
+
import { Order } from './order.entity';
|
|
8
|
+
|
|
9
|
+
// Junction table: links a PO item to the order item(s) it will fulfill
|
|
10
|
+
// One PO item can cover items from multiple orders
|
|
11
|
+
// One order item is linked to exactly one PO item
|
|
12
|
+
@Table
|
|
13
|
+
export class PurchaseOrderOrderItem extends Model {
|
|
14
|
+
@ForeignKey(() => PurchaseOrder)
|
|
15
|
+
@Column({ allowNull: false })
|
|
16
|
+
declare purchaseOrderId: number;
|
|
17
|
+
|
|
18
|
+
@ForeignKey(() => PurchaseOrderItem)
|
|
19
|
+
@Column({ allowNull: false })
|
|
20
|
+
declare purchaseOrderItemId: number;
|
|
21
|
+
|
|
22
|
+
@ForeignKey(() => Order)
|
|
23
|
+
@Column({ allowNull: false })
|
|
24
|
+
declare orderId: number;
|
|
25
|
+
|
|
26
|
+
@ForeignKey(() => OrderItem)
|
|
27
|
+
@Column({ allowNull: false })
|
|
28
|
+
declare orderItemId: number;
|
|
29
|
+
|
|
30
|
+
// How many units of this PO item are allocated to this order item
|
|
31
|
+
@Column({ allowNull: false, defaultValue: 1 })
|
|
32
|
+
declare allocatedQuantity: number;
|
|
33
|
+
|
|
34
|
+
@BelongsTo(() => PurchaseOrder) declare purchaseOrder: PurchaseOrder;
|
|
35
|
+
@BelongsTo(() => PurchaseOrderItem) declare purchaseOrderItem: PurchaseOrderItem;
|
|
36
|
+
@BelongsTo(() => Order) declare order: Order;
|
|
37
|
+
@BelongsTo(() => OrderItem) declare orderItem: OrderItem;
|
|
38
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Table, Column, Model, ForeignKey, BelongsTo, DataType,
|
|
3
|
+
} from 'sequelize-typescript';
|
|
4
|
+
import { User } from './user.entity';
|
|
5
|
+
import { Order } from './order.entity';
|
|
6
|
+
|
|
7
|
+
export enum TransactionType {
|
|
8
|
+
DEBIT = 'debit', // money leaves user balance
|
|
9
|
+
CREDIT = 'credit', // money enters user balance
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum TransactionCategory {
|
|
13
|
+
ORDER_PAYMENT = 'order_payment', // DEBIT: paid for order
|
|
14
|
+
ORDER_REFUND = 'order_refund', // CREDIT: refund received
|
|
15
|
+
GIFT_SENT = 'gift_sent', // DEBIT: sent gift to someone
|
|
16
|
+
GIFT_RECEIVED = 'gift_received', // CREDIT: received a gift
|
|
17
|
+
BALANCE_TOP_UP = 'balance_top_up', // CREDIT: topped up wallet
|
|
18
|
+
BALANCE_WITHDRAWAL = 'balance_withdrawal', // DEBIT: withdrew balance
|
|
19
|
+
COUPON_CREDIT = 'coupon_credit', // CREDIT: coupon cashback
|
|
20
|
+
ADJUSTMENT = 'adjustment', // DEBIT or CREDIT: manual admin adjustment
|
|
21
|
+
CASHBACK = 'cashback', // CREDIT: loyalty cashback
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Table
|
|
25
|
+
export class UserTransaction extends Model {
|
|
26
|
+
@ForeignKey(() => User)
|
|
27
|
+
@Column({ allowNull: false })
|
|
28
|
+
declare userId: number;
|
|
29
|
+
|
|
30
|
+
@Column({
|
|
31
|
+
type: DataType.ENUM(...Object.values(TransactionType)),
|
|
32
|
+
allowNull: false,
|
|
33
|
+
})
|
|
34
|
+
declare type: TransactionType;
|
|
35
|
+
|
|
36
|
+
@Column({
|
|
37
|
+
type: DataType.ENUM(...Object.values(TransactionCategory)),
|
|
38
|
+
allowNull: false,
|
|
39
|
+
})
|
|
40
|
+
declare category: TransactionCategory;
|
|
41
|
+
|
|
42
|
+
// Transaction amount (always positive — type determines direction)
|
|
43
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
|
|
44
|
+
declare amount: number;
|
|
45
|
+
|
|
46
|
+
// Running balance AFTER this transaction
|
|
47
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
48
|
+
declare balanceAfter: number;
|
|
49
|
+
|
|
50
|
+
// Currency of the transaction
|
|
51
|
+
@Column({ allowNull: false, defaultValue: 'KWD' })
|
|
52
|
+
declare currency: string;
|
|
53
|
+
|
|
54
|
+
// Bilingual description shown in the app
|
|
55
|
+
@Column({ allowNull: false })
|
|
56
|
+
declare descriptionEn: string;
|
|
57
|
+
|
|
58
|
+
@Column({ allowNull: false })
|
|
59
|
+
declare descriptionAr: string;
|
|
60
|
+
|
|
61
|
+
// Reference to the related order (if applicable)
|
|
62
|
+
@ForeignKey(() => Order)
|
|
63
|
+
@Column({ allowNull: true })
|
|
64
|
+
declare orderId?: number;
|
|
65
|
+
|
|
66
|
+
// External reference (payment gateway transaction ID, etc.)
|
|
67
|
+
@Column({ allowNull: true })
|
|
68
|
+
declare externalReference?: string;
|
|
69
|
+
|
|
70
|
+
// Status: pending | completed | failed | reversed
|
|
71
|
+
@Column({ allowNull: false, defaultValue: 'completed' })
|
|
72
|
+
declare status: string;
|
|
73
|
+
|
|
74
|
+
// ── Associations ──────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
@BelongsTo(() => User)
|
|
77
|
+
declare user: User;
|
|
78
|
+
|
|
79
|
+
@BelongsTo(() => Order)
|
|
80
|
+
declare order?: Order;
|
|
81
|
+
}
|
|
@@ -13,6 +13,9 @@ import { Country } from './country.entity';
|
|
|
13
13
|
import { Gender } from '../utils/enums/gender';
|
|
14
14
|
import { UserAddress } from './user-address.entity';
|
|
15
15
|
import { UserFavorite } from './user-favorite.entity';
|
|
16
|
+
import { Cart } from './cart.entity';
|
|
17
|
+
import { Order } from './order.entity';
|
|
18
|
+
import { UserTransaction } from './user-transaction.entity';
|
|
16
19
|
|
|
17
20
|
@Table
|
|
18
21
|
export class User extends Model {
|
|
@@ -83,6 +86,9 @@ export class User extends Model {
|
|
|
83
86
|
})
|
|
84
87
|
declare gender: Gender;
|
|
85
88
|
|
|
89
|
+
@Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
|
|
90
|
+
declare walletBalance: number;
|
|
91
|
+
|
|
86
92
|
@BelongsTo(() => Country)
|
|
87
93
|
declare country: Country;
|
|
88
94
|
|
|
@@ -91,4 +97,13 @@ export class User extends Model {
|
|
|
91
97
|
|
|
92
98
|
@HasMany(() => UserFavorite)
|
|
93
99
|
declare favorites: UserFavorite[];
|
|
100
|
+
|
|
101
|
+
@HasMany(() => Cart)
|
|
102
|
+
declare carts: Cart[];
|
|
103
|
+
|
|
104
|
+
@HasMany(() => Order)
|
|
105
|
+
declare orders: Order[];
|
|
106
|
+
|
|
107
|
+
@HasMany(() => UserTransaction)
|
|
108
|
+
declare transactions: UserTransaction[];
|
|
94
109
|
}
|
|
@@ -64,6 +64,16 @@ import { CouponStore } from './entities/coupon-store.entity';
|
|
|
64
64
|
import { CouponProduct } from './entities/coupon-product.entity';
|
|
65
65
|
import { CouponUser } from './entities/coupon-user.entity';
|
|
66
66
|
|
|
67
|
+
// ─── Orders ───────────────────────────────────────────────────────────────────
|
|
68
|
+
import { OrderStatus } from './entities/order-status.entity';
|
|
69
|
+
import { Order } from './entities/order.entity';
|
|
70
|
+
import { OrderItem } from './entities/order-item.entity';
|
|
71
|
+
import { OrderItemBundleSelection } from './entities/order-item-bundle-selection.entity';
|
|
72
|
+
import { OrderStatusHistory } from './entities/order-status-history.entity';
|
|
73
|
+
import { UserTransaction } from './entities/user-transaction.entity';
|
|
74
|
+
import { PurchaseOrderOrderItem } from './entities/purchase-order-order-item.entity';
|
|
75
|
+
|
|
76
|
+
|
|
67
77
|
// =============================================================================
|
|
68
78
|
// EXPORTS
|
|
69
79
|
// =============================================================================
|
|
@@ -147,6 +157,24 @@ export { CouponStore } from './entities/coupon-store.entity';
|
|
|
147
157
|
export { CouponProduct } from './entities/coupon-product.entity';
|
|
148
158
|
export { CouponUser } from './entities/coupon-user.entity';
|
|
149
159
|
|
|
160
|
+
// ─── Orders ───────────────────────────────────────────────────────────────────
|
|
161
|
+
export { OrderStatus, OrderStatusCode } from './entities/order-status.entity';
|
|
162
|
+
export { Order } from './entities/order.entity';
|
|
163
|
+
export {
|
|
164
|
+
OrderItem,
|
|
165
|
+
OrderItemType,
|
|
166
|
+
OrderItemFulfillmentStatus,
|
|
167
|
+
} from './entities/order-item.entity';
|
|
168
|
+
export { OrderItemBundleSelection } from './entities/order-item-bundle-selection.entity';
|
|
169
|
+
export { OrderStatusHistory } from './entities/order-status-history.entity';
|
|
170
|
+
export {
|
|
171
|
+
UserTransaction,
|
|
172
|
+
TransactionType,
|
|
173
|
+
TransactionCategory,
|
|
174
|
+
} from './entities/user-transaction.entity';
|
|
175
|
+
export { PurchaseOrderOrderItem } from './entities/purchase-order-order-item.entity';
|
|
176
|
+
|
|
177
|
+
|
|
150
178
|
// ─── Enums ────────────────────────────────────────────────────────────────────
|
|
151
179
|
export { OsName } from './utils/enums/osName';
|
|
152
180
|
export { ProductApp } from './utils/enums/productApp';
|
|
@@ -226,5 +254,14 @@ export function getDbModels(): ModelCtor[] {
|
|
|
226
254
|
CouponStore,
|
|
227
255
|
CouponProduct,
|
|
228
256
|
CouponUser,
|
|
257
|
+
|
|
258
|
+
// Orders
|
|
259
|
+
OrderStatus,
|
|
260
|
+
Order,
|
|
261
|
+
OrderItem,
|
|
262
|
+
OrderItemBundleSelection,
|
|
263
|
+
OrderStatusHistory,
|
|
264
|
+
UserTransaction,
|
|
265
|
+
PurchaseOrderOrderItem,
|
|
229
266
|
];
|
|
230
267
|
}
|