flykup_model_production 1.0.13 → 1.0.15
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/models/notification.model.js +16 -8
- package/models/productListing.model.js +82 -106
- package/models/shows.model.js +421 -26
- package/models/stock.model.js +78 -58
- package/models/user.model.js +28 -16
- package/package.json +1 -1
|
@@ -21,7 +21,11 @@ const notificationSchema = new mongoose.Schema(
|
|
|
21
21
|
'seller_broadcast',
|
|
22
22
|
'live_stream_start',
|
|
23
23
|
'seller_order_update',
|
|
24
|
-
'new_show_scheduled'
|
|
24
|
+
'new_show_scheduled',
|
|
25
|
+
'cohost_invite',
|
|
26
|
+
'cohost_accepted',
|
|
27
|
+
'cohost_rejected',
|
|
28
|
+
'cohost_join_live'
|
|
25
29
|
],
|
|
26
30
|
required: true,
|
|
27
31
|
},
|
|
@@ -101,14 +105,18 @@ const notificationSchema = new mongoose.Schema(
|
|
|
101
105
|
type: String,
|
|
102
106
|
},
|
|
103
107
|
},
|
|
108
|
+
// metadata: {
|
|
109
|
+
// status: String,
|
|
110
|
+
// reason: String,
|
|
111
|
+
// orderId: mongoose.Schema.Types.ObjectId,
|
|
112
|
+
// requestId: mongoose.Schema.Types.ObjectId,
|
|
113
|
+
// customerName: String,
|
|
114
|
+
// sellerName: String,
|
|
115
|
+
// customerProfileURL: String,
|
|
116
|
+
// },
|
|
104
117
|
metadata: {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
orderId: mongoose.Schema.Types.ObjectId,
|
|
108
|
-
requestId: mongoose.Schema.Types.ObjectId,
|
|
109
|
-
customerName: String,
|
|
110
|
-
sellerName: String,
|
|
111
|
-
customerProfileURL: String,
|
|
118
|
+
type: mongoose.Schema.Types.Mixed, // This allows any JSON object
|
|
119
|
+
default: {} // Default to empty object
|
|
112
120
|
},
|
|
113
121
|
},
|
|
114
122
|
{
|
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// backend/models/ProductListing.js (or similar path)
|
|
6
1
|
import mongoose from "mongoose";
|
|
7
2
|
const { Schema } = mongoose;
|
|
8
3
|
|
|
9
4
|
const ProductListingSchema = new Schema(
|
|
10
5
|
{
|
|
11
6
|
sellerId: { type: Schema.Types.ObjectId, ref: "sellers" },
|
|
12
|
-
stockId: { type: Schema.Types.ObjectId, ref: "stocks" },
|
|
7
|
+
stockId: { type: Schema.Types.ObjectId, ref: "stocks" }, // ✅ KEEP THIS - for main inventory management
|
|
13
8
|
title: String,
|
|
14
9
|
description: String,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
flashSale: {
|
|
11
|
+
isActive: { type: Boolean, default: false },
|
|
12
|
+
flashSaleId: { type: mongoose.Schema.Types.ObjectId, ref: 'FlashSale' },
|
|
13
|
+
flashPrice: { type: Number, default: 0 },
|
|
14
|
+
// ❌ REMOVED: flashStock field - now using main quantity
|
|
15
|
+
originalPrice: { type: Number, default: 0 },
|
|
16
|
+
endsAt: { type: Date, default: null },
|
|
17
|
+
startsAt: { type: Date, default: null }
|
|
18
|
+
},
|
|
19
|
+
reserveForLive: {
|
|
20
|
+
type: Boolean,
|
|
21
|
+
default: false
|
|
22
|
+
},
|
|
24
23
|
sku: {
|
|
25
24
|
type: String,
|
|
26
25
|
required: true,
|
|
@@ -54,7 +53,7 @@ const ProductListingSchema = new Schema(
|
|
|
54
53
|
min: 0,
|
|
55
54
|
default: null,
|
|
56
55
|
},
|
|
57
|
-
|
|
56
|
+
logisticsType: {
|
|
58
57
|
type: String,
|
|
59
58
|
enum: ["flykupLogistics", "selfShipment"],
|
|
60
59
|
default: null,
|
|
@@ -65,47 +64,45 @@ const ProductListingSchema = new Schema(
|
|
|
65
64
|
default: null,
|
|
66
65
|
},
|
|
67
66
|
estimatedDeliveryDate: {
|
|
68
|
-
type: Number,
|
|
67
|
+
type: Number,
|
|
69
68
|
min: 0,
|
|
70
69
|
default: null,
|
|
71
70
|
},
|
|
72
|
-
|
|
73
|
-
// --- ADDED/UPDATED FIELDS FROM FRONTEND ---
|
|
74
|
-
brand: { type: String, default: null }, // ADDED
|
|
71
|
+
brand: { type: String, default: null },
|
|
75
72
|
manufacturer: String,
|
|
76
|
-
manufacturerAddress: String,
|
|
73
|
+
manufacturerAddress: String,
|
|
77
74
|
countryOfOrigin: String,
|
|
78
|
-
netQuantity: { type: String, default: null },
|
|
79
|
-
packagingType: { type: String, default: null },
|
|
80
|
-
weight: {
|
|
75
|
+
netQuantity: { type: String, default: null },
|
|
76
|
+
packagingType: { type: String, default: null },
|
|
77
|
+
weight: {
|
|
81
78
|
value: { type: Number, default: null },
|
|
82
79
|
unit: { type: String, default: null },
|
|
83
80
|
},
|
|
84
|
-
dimensions: {
|
|
81
|
+
dimensions: {
|
|
85
82
|
length: { type: Number, default: null },
|
|
86
83
|
width: { type: Number, default: null },
|
|
87
84
|
height: { type: Number, default: null },
|
|
88
85
|
},
|
|
89
|
-
expiryDate: { type: Date, default: null },
|
|
90
|
-
shelfLife: { type: String, default: null },
|
|
91
|
-
batchNumber: { type: String, default: null },
|
|
92
|
-
gstRate: { type: Number, default: null },
|
|
86
|
+
expiryDate: { type: Date, default: null },
|
|
87
|
+
shelfLife: { type: String, default: null },
|
|
88
|
+
batchNumber: { type: String, default: null },
|
|
89
|
+
gstRate: { type: Number, default: null },
|
|
93
90
|
sellerName: String,
|
|
94
|
-
sellerContact: { type: String, default: null },
|
|
95
|
-
sellerGSTIN: { type: String, default: null },
|
|
91
|
+
sellerContact: { type: String, default: null },
|
|
92
|
+
sellerGSTIN: { type: String, default: null },
|
|
96
93
|
returnPolicy: [String],
|
|
97
94
|
warranty: {
|
|
98
|
-
hasWarranty: { type: Boolean, default: false },
|
|
99
|
-
duration: { type: String, default: null },
|
|
95
|
+
hasWarranty: { type: Boolean, default: false },
|
|
96
|
+
duration: { type: String, default: null },
|
|
100
97
|
},
|
|
101
|
-
fssaiLicenseNo: { type: String, default: null },
|
|
102
|
-
bisCertification: { type: String, default: null },
|
|
103
|
-
importerName: { type: String, default: null },
|
|
104
|
-
importerAddress: { type: String, default: null },
|
|
105
|
-
importerGSTIN: { type: String, default: null },
|
|
106
|
-
eWasteCompliance: { type: Boolean, default: false },
|
|
107
|
-
recyclablePackaging: { type: Boolean, default: false },
|
|
108
|
-
hazardousMaterials: { type: String, default: null },
|
|
98
|
+
fssaiLicenseNo: { type: String, default: null },
|
|
99
|
+
bisCertification: { type: String, default: null },
|
|
100
|
+
importerName: { type: String, default: null },
|
|
101
|
+
importerAddress: { type: String, default: null },
|
|
102
|
+
importerGSTIN: { type: String, default: null },
|
|
103
|
+
eWasteCompliance: { type: Boolean, default: false },
|
|
104
|
+
recyclablePackaging: { type: Boolean, default: false },
|
|
105
|
+
hazardousMaterials: { type: String, default: null },
|
|
109
106
|
allowDropshipping: {
|
|
110
107
|
type: Boolean,
|
|
111
108
|
default: false,
|
|
@@ -114,65 +111,47 @@ const ProductListingSchema = new Schema(
|
|
|
114
111
|
commissionRate: {
|
|
115
112
|
type: Number,
|
|
116
113
|
min: [0, 'Commission rate cannot be negative.'],
|
|
117
|
-
// max: [100, 'Commission rate cannot exceed 100%.'], // Max was 25, changed to 100 based on frontend
|
|
118
114
|
max: [100, 'Commission rate cannot exceed 100%.'],
|
|
119
115
|
default: null,
|
|
120
|
-
// Removed Mongoose-level required validation dependent on allowDropshipping
|
|
121
|
-
// Let application logic handle this if needed, or adjust validator
|
|
122
|
-
// required: [
|
|
123
|
-
// function () { return this.allowDropshipping === true; },
|
|
124
|
-
// 'Commission rate is required when allowing dropshipping.'
|
|
125
|
-
// ],
|
|
126
|
-
// validate: { ... } // Keep or remove validation as needed
|
|
127
116
|
},
|
|
128
117
|
hasReturn: {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
132
|
-
returnDays: {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
},
|
|
137
|
-
size: {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
},
|
|
118
|
+
type: Boolean,
|
|
119
|
+
default: false
|
|
120
|
+
},
|
|
121
|
+
returnDays: {
|
|
122
|
+
type: Number,
|
|
123
|
+
min: 0,
|
|
124
|
+
default: null
|
|
125
|
+
},
|
|
126
|
+
size: {
|
|
127
|
+
type: String,
|
|
128
|
+
default: null
|
|
129
|
+
},
|
|
141
130
|
isActive: {
|
|
142
131
|
type: Boolean,
|
|
143
132
|
default: true,
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
133
|
+
},
|
|
134
|
+
ratingSummary: {
|
|
135
|
+
averageRating: { type: Number, default: 0 },
|
|
136
|
+
totalRatings: { type: Number, default: 0 },
|
|
137
|
+
ratingDistribution: {
|
|
138
|
+
1: { type: Number, default: 0 },
|
|
139
|
+
2: { type: Number, default: 0 },
|
|
140
|
+
3: { type: Number, default: 0 },
|
|
141
|
+
4: { type: Number, default: 0 },
|
|
142
|
+
5: { type: Number, default: 0 }
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
totalReviews: { type: Number, default: 0 }
|
|
156
146
|
},
|
|
157
147
|
{ timestamps: true }
|
|
158
148
|
);
|
|
159
|
-
ProductListingSchema.index({ title: 'text', description: 'text', category: 'text' });
|
|
160
149
|
|
|
150
|
+
ProductListingSchema.index({ title: 'text', description: 'text', category: 'text' });
|
|
161
151
|
|
|
162
|
-
// Safe export to prevent OverwriteModelError
|
|
163
152
|
const ProductListing = mongoose.models.productlistings || mongoose.model("productlistings", ProductListingSchema);
|
|
164
153
|
export default ProductListing;
|
|
165
154
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
155
|
// // backend/models/ProductListing.js (or similar path)
|
|
177
156
|
// import mongoose from "mongoose";
|
|
178
157
|
// const { Schema } = mongoose;
|
|
@@ -192,25 +171,11 @@ export default ProductListing;
|
|
|
192
171
|
// endsAt: { type: Date, default: null },
|
|
193
172
|
// startsAt: { type: Date, default: null }
|
|
194
173
|
// },
|
|
195
|
-
|
|
196
|
-
// //new fields added
|
|
197
|
-
|
|
198
|
-
// returnPolicy: {
|
|
199
|
-
// hasReturn: { type: Boolean, default: false },
|
|
200
|
-
// returnType: {
|
|
201
|
-
// type: [String],
|
|
202
|
-
// enum: ['refund', 'replacement'],
|
|
203
|
-
// default: []
|
|
204
|
-
// },
|
|
205
|
-
// returnDays: { type: Number, min: 0, default: null },
|
|
206
|
-
// terms: [String] // Keep existing terms for backward compatibility
|
|
207
|
-
// },
|
|
208
174
|
// sku: {
|
|
209
175
|
// type: String,
|
|
210
176
|
// required: true,
|
|
211
|
-
//
|
|
177
|
+
// index: true
|
|
212
178
|
// },
|
|
213
|
-
// // ----------end of new fields added
|
|
214
179
|
// images: [
|
|
215
180
|
// {
|
|
216
181
|
// key: { type: String, maxLength: 255, default: null },
|
|
@@ -239,7 +204,22 @@ export default ProductListing;
|
|
|
239
204
|
// min: 0,
|
|
240
205
|
// default: null,
|
|
241
206
|
// },
|
|
242
|
-
|
|
207
|
+
// logisticsType: {
|
|
208
|
+
// type: String,
|
|
209
|
+
// enum: ["flykupLogistics", "selfShipment"],
|
|
210
|
+
// default: null,
|
|
211
|
+
// },
|
|
212
|
+
// deliveryCharge: {
|
|
213
|
+
// type: Number,
|
|
214
|
+
// min: 0,
|
|
215
|
+
// default: null,
|
|
216
|
+
// },
|
|
217
|
+
// estimatedDeliveryDate: {
|
|
218
|
+
// type: Number, // Change from Date to Number
|
|
219
|
+
// min: 0,
|
|
220
|
+
// default: null,
|
|
221
|
+
// },
|
|
222
|
+
|
|
243
223
|
// // --- ADDED/UPDATED FIELDS FROM FRONTEND ---
|
|
244
224
|
// brand: { type: String, default: null }, // ADDED
|
|
245
225
|
// manufacturer: String,
|
|
@@ -324,13 +304,10 @@ export default ProductListing;
|
|
|
324
304
|
// },
|
|
325
305
|
// totalReviews: { type: Number, default: 0 }
|
|
326
306
|
// },
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
307
|
// { timestamps: true }
|
|
331
308
|
// );
|
|
332
309
|
// ProductListingSchema.index({ title: 'text', description: 'text', category: 'text' });
|
|
333
|
-
|
|
310
|
+
|
|
334
311
|
|
|
335
312
|
// // Safe export to prevent OverwriteModelError
|
|
336
313
|
// const ProductListing = mongoose.models.productlistings || mongoose.model("productlistings", ProductListingSchema);
|
|
@@ -339,4 +316,3 @@ export default ProductListing;
|
|
|
339
316
|
|
|
340
317
|
|
|
341
318
|
|
|
342
|
-
|
package/models/shows.model.js
CHANGED
|
@@ -6,43 +6,90 @@ const giveawayProductSchema = new Schema({
|
|
|
6
6
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings", required: true },
|
|
7
7
|
productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
8
8
|
productTitle: { type: String },
|
|
9
|
-
|
|
10
|
-
// BGA Integration Fields
|
|
11
9
|
giveawayObjectId: { type: Schema.Types.ObjectId, required: true, index: true },
|
|
12
10
|
giveawayStatus: {
|
|
13
11
|
type: String,
|
|
14
12
|
enum: ['preparing', 'ready', 'active', 'ended', 'failed'],
|
|
15
13
|
default: 'preparing'
|
|
16
14
|
},
|
|
17
|
-
|
|
15
|
+
requireAutoFollow: {
|
|
18
16
|
type: Boolean,
|
|
19
17
|
default: false
|
|
20
18
|
},
|
|
21
|
-
|
|
22
19
|
activatedAt: { type: Date, default: null },
|
|
23
20
|
createdAt: { type: Date, default: Date.now }
|
|
24
21
|
});
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
const liveFlashSaleSchema = new Schema({
|
|
24
|
+
_id: false,
|
|
25
|
+
productId: {
|
|
26
|
+
type: Schema.Types.ObjectId,
|
|
27
|
+
ref: "productlistings",
|
|
28
|
+
required: true
|
|
29
|
+
},
|
|
30
|
+
stockId: {
|
|
31
|
+
type: Schema.Types.ObjectId,
|
|
32
|
+
ref: "stocks",
|
|
33
|
+
required: true
|
|
34
|
+
},
|
|
35
|
+
flashPrice: {
|
|
36
|
+
type: Number,
|
|
37
|
+
required: true,
|
|
38
|
+
min: 1
|
|
39
|
+
},
|
|
40
|
+
duration: {
|
|
41
|
+
type: Number,
|
|
42
|
+
required: true,
|
|
43
|
+
enum: [10, 20, 30, 40, 50, 60, 240]
|
|
44
|
+
},
|
|
45
|
+
initialStock: {
|
|
46
|
+
type: Number,
|
|
47
|
+
required: true,
|
|
48
|
+
min: 1
|
|
49
|
+
},
|
|
50
|
+
currentStock: {
|
|
51
|
+
type: Number,
|
|
52
|
+
required: true,
|
|
53
|
+
min: 0
|
|
54
|
+
},
|
|
55
|
+
sold: {
|
|
56
|
+
type: Number,
|
|
57
|
+
default: 0
|
|
58
|
+
},
|
|
59
|
+
status: {
|
|
60
|
+
type: String,
|
|
61
|
+
enum: ['scheduled', 'active', 'ended', 'cancelled'],
|
|
62
|
+
default: 'scheduled'
|
|
63
|
+
},
|
|
64
|
+
startTime: Date,
|
|
65
|
+
endTime: Date,
|
|
66
|
+
flashSaleId: {
|
|
67
|
+
type: Schema.Types.ObjectId,
|
|
68
|
+
ref: 'FlashSale'
|
|
69
|
+
},
|
|
70
|
+
createdAt: {
|
|
71
|
+
type: Date,
|
|
72
|
+
default: Date.now
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Show Schema with FIXED validation
|
|
27
77
|
const showSchema = new Schema(
|
|
28
78
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// === Host Information ===
|
|
35
|
-
host: { // Host (Seller or Dropshipper)
|
|
79
|
+
sellerId: {
|
|
80
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
81
|
+
ref: "sellers",
|
|
82
|
+
},
|
|
83
|
+
host: {
|
|
36
84
|
type: mongoose.Schema.Types.ObjectId,
|
|
37
85
|
required: true,
|
|
38
|
-
refPath: 'hostModel'
|
|
86
|
+
refPath: 'hostModel'
|
|
39
87
|
},
|
|
40
|
-
hostModel: {
|
|
88
|
+
hostModel: {
|
|
41
89
|
type: String,
|
|
42
90
|
required: true,
|
|
43
|
-
enum: ['sellers', 'dropshippers']
|
|
91
|
+
enum: ['sellers', 'dropshippers']
|
|
44
92
|
},
|
|
45
|
-
|
|
46
93
|
title: {
|
|
47
94
|
type: String,
|
|
48
95
|
required: true,
|
|
@@ -55,7 +102,7 @@ const showSchema = new Schema(
|
|
|
55
102
|
},
|
|
56
103
|
scheduledAt: {
|
|
57
104
|
type: Date,
|
|
58
|
-
index: true
|
|
105
|
+
index: true
|
|
59
106
|
},
|
|
60
107
|
category: {
|
|
61
108
|
type: String,
|
|
@@ -73,13 +120,12 @@ const showSchema = new Schema(
|
|
|
73
120
|
type: [String],
|
|
74
121
|
default: [],
|
|
75
122
|
},
|
|
76
|
-
|
|
77
123
|
thumbnailImage: {
|
|
78
|
-
type: String,
|
|
124
|
+
type: String,
|
|
79
125
|
default: null,
|
|
80
126
|
},
|
|
81
127
|
previewVideo: {
|
|
82
|
-
type: String,
|
|
128
|
+
type: String,
|
|
83
129
|
default: null,
|
|
84
130
|
},
|
|
85
131
|
language: {
|
|
@@ -90,9 +136,19 @@ const showSchema = new Schema(
|
|
|
90
136
|
default: false,
|
|
91
137
|
},
|
|
92
138
|
streamUrl: {
|
|
93
|
-
type: String,
|
|
139
|
+
type: String,
|
|
94
140
|
default: null,
|
|
95
141
|
},
|
|
142
|
+
orientation: {
|
|
143
|
+
type: String,
|
|
144
|
+
enum: ['horizontal', 'vertical'],
|
|
145
|
+
default: 'horizontal'
|
|
146
|
+
},
|
|
147
|
+
layout: {
|
|
148
|
+
type: String,
|
|
149
|
+
enum: ['side-by-side', 'top-bottom'],
|
|
150
|
+
default: 'side-by-side'
|
|
151
|
+
},
|
|
96
152
|
showStatus: {
|
|
97
153
|
type: String,
|
|
98
154
|
enum: ['created', 'live', 'cancelled', 'ended'],
|
|
@@ -108,27 +164,56 @@ const showSchema = new Schema(
|
|
|
108
164
|
default: [],
|
|
109
165
|
},
|
|
110
166
|
|
|
167
|
+
// ✅ FIXED: Better validation for buyNowProducts
|
|
111
168
|
buyNowProducts: {
|
|
112
169
|
type: [
|
|
113
170
|
{
|
|
114
171
|
_id: false,
|
|
115
172
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
116
|
-
productOwnerSellerId: {
|
|
117
|
-
|
|
173
|
+
productOwnerSellerId: {
|
|
174
|
+
type: Schema.Types.ObjectId,
|
|
175
|
+
ref: 'sellers',
|
|
176
|
+
required: function() {
|
|
177
|
+
// Only require if productId exists
|
|
178
|
+
return this.productId != null;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
productPrice: { type: Number, min: 0, default: null },
|
|
118
182
|
},
|
|
119
183
|
],
|
|
120
184
|
default: [],
|
|
185
|
+
validate: {
|
|
186
|
+
validator: function(products) {
|
|
187
|
+
// Custom validation to ensure productOwnerSellerId exists when productId exists
|
|
188
|
+
return products.every(product =>
|
|
189
|
+
!product.productId || (product.productId && product.productOwnerSellerId)
|
|
190
|
+
);
|
|
191
|
+
},
|
|
192
|
+
message: 'productOwnerSellerId is required when productId is provided'
|
|
193
|
+
}
|
|
121
194
|
},
|
|
195
|
+
|
|
122
196
|
auctionProducts: {
|
|
123
197
|
type: [
|
|
124
198
|
{
|
|
125
199
|
_id: false,
|
|
126
200
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
127
|
-
productOwnerSellerId: {
|
|
201
|
+
productOwnerSellerId: {
|
|
202
|
+
type: Schema.Types.ObjectId,
|
|
203
|
+
ref: 'sellers',
|
|
204
|
+
required: function() {
|
|
205
|
+
return this.productId != null;
|
|
206
|
+
}
|
|
207
|
+
},
|
|
128
208
|
startingPrice: { type: Number, min: 0, default: null },
|
|
129
209
|
reservedPrice: { type: Number, min: 0, default: null },
|
|
130
210
|
auctionNumber: {
|
|
131
211
|
type: Number,
|
|
212
|
+
},
|
|
213
|
+
auctionObjectId: {
|
|
214
|
+
type: Schema.Types.ObjectId,
|
|
215
|
+
required: true,
|
|
216
|
+
index: true
|
|
132
217
|
}
|
|
133
218
|
},
|
|
134
219
|
],
|
|
@@ -140,6 +225,16 @@ const showSchema = new Schema(
|
|
|
140
225
|
default: []
|
|
141
226
|
},
|
|
142
227
|
|
|
228
|
+
liveFlashSales: {
|
|
229
|
+
type: [liveFlashSaleSchema],
|
|
230
|
+
default: []
|
|
231
|
+
},
|
|
232
|
+
currentFlashSale: {
|
|
233
|
+
type: Schema.Types.ObjectId,
|
|
234
|
+
ref: 'FlashSale',
|
|
235
|
+
default: null
|
|
236
|
+
},
|
|
237
|
+
|
|
143
238
|
enabledProductTypes: {
|
|
144
239
|
buyNow: { type: Boolean, default: false },
|
|
145
240
|
auction: { type: Boolean, default: false },
|
|
@@ -153,9 +248,39 @@ const showSchema = new Schema(
|
|
|
153
248
|
{ timestamps: true }
|
|
154
249
|
);
|
|
155
250
|
|
|
156
|
-
|
|
251
|
+
// ✅ ADD THIS PRE-SAVE MIDDLEWARE TO CLEAN INVALID DATA
|
|
252
|
+
showSchema.pre('save', function(next) {
|
|
157
253
|
const doc = this;
|
|
158
254
|
|
|
255
|
+
// Clean up buyNowProducts - remove items without required fields
|
|
256
|
+
if (doc.buyNowProducts && Array.isArray(doc.buyNowProducts)) {
|
|
257
|
+
doc.buyNowProducts = doc.buyNowProducts.filter(product =>
|
|
258
|
+
product &&
|
|
259
|
+
product.productId &&
|
|
260
|
+
product.productOwnerSellerId
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Clean up auctionProducts
|
|
265
|
+
if (doc.auctionProducts && Array.isArray(doc.auctionProducts)) {
|
|
266
|
+
doc.auctionProducts = doc.auctionProducts.filter(product =>
|
|
267
|
+
product &&
|
|
268
|
+
product.productId &&
|
|
269
|
+
product.productOwnerSellerId &&
|
|
270
|
+
product.auctionObjectId
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Clean up giveawayProducts
|
|
275
|
+
if (doc.giveawayProducts && Array.isArray(doc.giveawayProducts)) {
|
|
276
|
+
doc.giveawayProducts = doc.giveawayProducts.filter(product =>
|
|
277
|
+
product &&
|
|
278
|
+
product.productId &&
|
|
279
|
+
product.productOwnerSellerId &&
|
|
280
|
+
product.giveawayObjectId
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
159
284
|
// Set liveDrop based on product types
|
|
160
285
|
const hasBuyNow = doc.buyNowProducts && doc.buyNowProducts.length > 0;
|
|
161
286
|
const hasAuction = doc.auctionProducts && doc.auctionProducts.length > 0;
|
|
@@ -170,6 +295,276 @@ showSchema.pre('save', function (next) {
|
|
|
170
295
|
next();
|
|
171
296
|
});
|
|
172
297
|
|
|
298
|
+
// ✅ ADD THIS PRE-VALIDATION MIDDLEWARE FOR EXTRA SAFETY
|
|
299
|
+
showSchema.pre('validate', function(next) {
|
|
300
|
+
// Ensure all arrays exist (prevent undefined errors)
|
|
301
|
+
if (!this.buyNowProducts) this.buyNowProducts = [];
|
|
302
|
+
if (!this.auctionProducts) this.auctionProducts = [];
|
|
303
|
+
if (!this.giveawayProducts) this.giveawayProducts = [];
|
|
304
|
+
if (!this.liveFlashSales) this.liveFlashSales = [];
|
|
305
|
+
|
|
306
|
+
next();
|
|
307
|
+
});
|
|
308
|
+
|
|
173
309
|
const Show = mongoose.model("shows", showSchema);
|
|
310
|
+
export default Show;
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
// import mongoose from "mongoose";
|
|
315
|
+
// const { Schema } = mongoose;
|
|
316
|
+
|
|
317
|
+
// const giveawayProductSchema = new Schema({
|
|
318
|
+
// _id: false,
|
|
319
|
+
// productId: { type: Schema.Types.ObjectId, ref: "productlistings", required: true },
|
|
320
|
+
// productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
321
|
+
// productTitle: { type: String },
|
|
322
|
+
// giveawayObjectId: { type: Schema.Types.ObjectId, required: true, index: true },
|
|
323
|
+
// giveawayStatus: {
|
|
324
|
+
// type: String,
|
|
325
|
+
// enum: ['preparing', 'ready', 'active', 'ended', 'failed'],
|
|
326
|
+
// default: 'preparing'
|
|
327
|
+
// },
|
|
328
|
+
// requireAutoFollow: {
|
|
329
|
+
// type: Boolean,
|
|
330
|
+
// default: false
|
|
331
|
+
// },
|
|
332
|
+
// activatedAt: { type: Date, default: null },
|
|
333
|
+
// createdAt: { type: Date, default: Date.now }
|
|
334
|
+
// });
|
|
335
|
+
|
|
336
|
+
// // Live Flash Sale Schema
|
|
337
|
+
// const liveFlashSaleSchema = new Schema({
|
|
338
|
+
// _id: false,
|
|
339
|
+
// productId: {
|
|
340
|
+
// type: Schema.Types.ObjectId,
|
|
341
|
+
// ref: "productlistings",
|
|
342
|
+
// required: true
|
|
343
|
+
// },
|
|
344
|
+
// stockId: {
|
|
345
|
+
// type: Schema.Types.ObjectId,
|
|
346
|
+
// ref: "stocks",
|
|
347
|
+
// required: true
|
|
348
|
+
// },
|
|
349
|
+
// flashPrice: {
|
|
350
|
+
// type: Number,
|
|
351
|
+
// required: true,
|
|
352
|
+
// min: 1
|
|
353
|
+
// },
|
|
354
|
+
// duration: {
|
|
355
|
+
// type: Number,
|
|
356
|
+
// required: true,
|
|
357
|
+
// enum: [10, 20, 30, 40, 50, 60,240]
|
|
358
|
+
// },
|
|
359
|
+
// initialStock: {
|
|
360
|
+
// type: Number,
|
|
361
|
+
// required: true,
|
|
362
|
+
// min: 1
|
|
363
|
+
// },
|
|
364
|
+
// currentStock: {
|
|
365
|
+
// type: Number,
|
|
366
|
+
// required: true,
|
|
367
|
+
// min: 0
|
|
368
|
+
// },
|
|
369
|
+
// sold: {
|
|
370
|
+
// type: Number,
|
|
371
|
+
// default: 0
|
|
372
|
+
// },
|
|
373
|
+
// status: {
|
|
374
|
+
// type: String,
|
|
375
|
+
// enum: ['scheduled', 'active', 'ended', 'cancelled'],
|
|
376
|
+
// default: 'scheduled'
|
|
377
|
+
// },
|
|
378
|
+
// startTime: Date,
|
|
379
|
+
// endTime: Date,
|
|
380
|
+
// flashSaleId: {
|
|
381
|
+
// type: Schema.Types.ObjectId,
|
|
382
|
+
// ref: 'FlashSale'
|
|
383
|
+
// },
|
|
384
|
+
// createdAt: {
|
|
385
|
+
// type: Date,
|
|
386
|
+
// default: Date.now
|
|
387
|
+
// }
|
|
388
|
+
// });
|
|
389
|
+
// // Show Schema
|
|
390
|
+
// const showSchema = new Schema(
|
|
391
|
+
// {
|
|
392
|
+
// sellerId: {
|
|
393
|
+
// type: mongoose.Schema.Types.ObjectId,
|
|
394
|
+
// ref: "sellers",
|
|
395
|
+
// },
|
|
396
|
+
// // === Host Information ===
|
|
397
|
+
// host: {
|
|
398
|
+
// type: mongoose.Schema.Types.ObjectId,
|
|
399
|
+
// required: true,
|
|
400
|
+
// refPath: 'hostModel'
|
|
401
|
+
// },
|
|
402
|
+
// hostModel: {
|
|
403
|
+
// type: String,
|
|
404
|
+
// required: true,
|
|
405
|
+
// enum: ['sellers', 'dropshippers']
|
|
406
|
+
// },
|
|
407
|
+
|
|
408
|
+
// title: {
|
|
409
|
+
// type: String,
|
|
410
|
+
// required: true,
|
|
411
|
+
// },
|
|
412
|
+
// date: {
|
|
413
|
+
// type: Date,
|
|
414
|
+
// },
|
|
415
|
+
// time: {
|
|
416
|
+
// type: String,
|
|
417
|
+
// },
|
|
418
|
+
// scheduledAt: {
|
|
419
|
+
// type: Date,
|
|
420
|
+
// index: true
|
|
421
|
+
// },
|
|
422
|
+
// category: {
|
|
423
|
+
// type: String,
|
|
424
|
+
// },
|
|
425
|
+
// subCategory: {
|
|
426
|
+
// type: String,
|
|
427
|
+
// default: '',
|
|
428
|
+
// },
|
|
429
|
+
// liveStreamId: { type: String, default: null },
|
|
430
|
+
// streamName: {
|
|
431
|
+
// type: String,
|
|
432
|
+
// default: ''
|
|
433
|
+
// },
|
|
434
|
+
// tags: {
|
|
435
|
+
// type: [String],
|
|
436
|
+
// default: [],
|
|
437
|
+
// },
|
|
438
|
+
|
|
439
|
+
// thumbnailImage: {
|
|
440
|
+
// type: String,
|
|
441
|
+
// default: null,
|
|
442
|
+
// },
|
|
443
|
+
// previewVideo: {
|
|
444
|
+
// type: String,
|
|
445
|
+
// default: null,
|
|
446
|
+
// },
|
|
447
|
+
// language: {
|
|
448
|
+
// type: String,
|
|
449
|
+
// },
|
|
450
|
+
// isLive: {
|
|
451
|
+
// type: Boolean,
|
|
452
|
+
// default: false,
|
|
453
|
+
// },
|
|
454
|
+
// streamUrl: {
|
|
455
|
+
// type: String,
|
|
456
|
+
// default: null,
|
|
457
|
+
// },
|
|
458
|
+
|
|
459
|
+
// // === Stream Layout Configuration ===
|
|
460
|
+
// orientation: {
|
|
461
|
+
// type: String,
|
|
462
|
+
// enum: ['horizontal', 'vertical'],
|
|
463
|
+
// default: 'horizontal'
|
|
464
|
+
// },
|
|
465
|
+
// layout: {
|
|
466
|
+
// type: String,
|
|
467
|
+
// enum: ['side-by-side', 'top-bottom'],
|
|
468
|
+
// default: 'side-by-side'
|
|
469
|
+
// },
|
|
470
|
+
|
|
471
|
+
// showStatus: {
|
|
472
|
+
// type: String,
|
|
473
|
+
// enum: ['created', 'live', 'cancelled', 'ended'],
|
|
474
|
+
// default: 'created'
|
|
475
|
+
// },
|
|
476
|
+
// likes: {
|
|
477
|
+
// type: Number,
|
|
478
|
+
// default: 0,
|
|
479
|
+
// },
|
|
480
|
+
// likedBy: {
|
|
481
|
+
// type: [mongoose.Schema.Types.ObjectId],
|
|
482
|
+
// ref: 'users',
|
|
483
|
+
// default: [],
|
|
484
|
+
// },
|
|
485
|
+
|
|
486
|
+
// buyNowProducts: {
|
|
487
|
+
// type: [
|
|
488
|
+
// {
|
|
489
|
+
// _id: false,
|
|
490
|
+
// productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
491
|
+
// productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
492
|
+
// productPrice: { type: Number, min: 0, default: null },
|
|
493
|
+
// },
|
|
494
|
+
// ],
|
|
495
|
+
// default: [],
|
|
496
|
+
// },
|
|
497
|
+
// auctionProducts: {
|
|
498
|
+
// type: [
|
|
499
|
+
// {
|
|
500
|
+
// _id: false,
|
|
501
|
+
// productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
502
|
+
// productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
503
|
+
// startingPrice: { type: Number, min: 0, default: null },
|
|
504
|
+
// reservedPrice: { type: Number, min: 0, default: null },
|
|
505
|
+
// auctionNumber: {
|
|
506
|
+
// type: Number,
|
|
507
|
+
// },
|
|
508
|
+
// // BGA Integration - Reference to BGA auction document
|
|
509
|
+
// auctionObjectId: {
|
|
510
|
+
// type: Schema.Types.ObjectId,
|
|
511
|
+
// required: true,
|
|
512
|
+
// index: true
|
|
513
|
+
// }
|
|
514
|
+
// },
|
|
515
|
+
// ],
|
|
516
|
+
// default: [],
|
|
517
|
+
// },
|
|
518
|
+
|
|
519
|
+
// giveawayProducts: {
|
|
520
|
+
// type: [giveawayProductSchema],
|
|
521
|
+
// default: []
|
|
522
|
+
// },
|
|
523
|
+
|
|
524
|
+
// // === LIVE FLASH SALE FIELDS (ADD THESE) ===
|
|
525
|
+
// liveFlashSales: {
|
|
526
|
+
// type: [liveFlashSaleSchema],
|
|
527
|
+
// default: [] // This ensures it's always an array
|
|
528
|
+
// },
|
|
529
|
+
// currentFlashSale: {
|
|
530
|
+
// type: Schema.Types.ObjectId,
|
|
531
|
+
// ref: 'FlashSale',
|
|
532
|
+
// default: null
|
|
533
|
+
// },
|
|
534
|
+
|
|
535
|
+
// enabledProductTypes: {
|
|
536
|
+
// buyNow: { type: Boolean, default: false },
|
|
537
|
+
// auction: { type: Boolean, default: false },
|
|
538
|
+
// giveaway: { type: Boolean, default: false }
|
|
539
|
+
// },
|
|
540
|
+
// notes: {
|
|
541
|
+
// type: String,
|
|
542
|
+
// default: ''
|
|
543
|
+
// },
|
|
544
|
+
// },
|
|
545
|
+
// { timestamps: true }
|
|
546
|
+
// );
|
|
547
|
+
|
|
548
|
+
// showSchema.pre('save', function (next) {
|
|
549
|
+
// const doc = this;
|
|
550
|
+
|
|
551
|
+
// // Set liveDrop based on product types
|
|
552
|
+
// const hasBuyNow = doc.buyNowProducts && doc.buyNowProducts.length > 0;
|
|
553
|
+
// const hasAuction = doc.auctionProducts && doc.auctionProducts.length > 0;
|
|
554
|
+
// const hasGiveaway = doc.giveawayProducts && doc.giveawayProducts.length > 0;
|
|
555
|
+
// doc.liveDrop = hasBuyNow && hasAuction && hasGiveaway;
|
|
556
|
+
|
|
557
|
+
// // Ensure coHost is null if hasCoHost is false
|
|
558
|
+
// if (!doc.hasCoHost) {
|
|
559
|
+
// doc.coHost = null;
|
|
560
|
+
// }
|
|
561
|
+
|
|
562
|
+
// next();
|
|
563
|
+
// });
|
|
564
|
+
|
|
565
|
+
// const Show = mongoose.model("shows", showSchema);
|
|
566
|
+
|
|
567
|
+
// export default Show;
|
|
568
|
+
|
|
569
|
+
|
|
174
570
|
|
|
175
|
-
export default Show;
|
package/models/stock.model.js
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// models/Stock.js
|
|
1
|
+
// models/Stock.js - Simplified
|
|
3
2
|
import mongoose from "mongoose";
|
|
4
3
|
const { Schema } = mongoose;
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const StockUpdateHistorySchema = new Schema({
|
|
8
|
-
change: { type: Number, required: true }, // e.g., +50 or -10
|
|
9
|
-
previousQuantity: { type: Number, required: true },
|
|
10
|
-
newQuantity: { type: Number, required: true },
|
|
11
|
-
reason: { type: String, default: "Manual update by seller" },
|
|
12
|
-
updatedAt: { type: Date, default: Date.now }
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const StockSchema = new Schema(
|
|
5
|
+
const stockSchema = new mongoose.Schema(
|
|
17
6
|
{
|
|
18
7
|
sellerId: {
|
|
19
8
|
type: Schema.Types.ObjectId,
|
|
@@ -22,64 +11,95 @@ const StockSchema = new Schema(
|
|
|
22
11
|
productListingId: {
|
|
23
12
|
type: Schema.Types.ObjectId,
|
|
24
13
|
ref: "productlistings",
|
|
14
|
+
required: true,
|
|
15
|
+
unique: true
|
|
16
|
+
},
|
|
17
|
+
quantity: {
|
|
18
|
+
type: Number,
|
|
19
|
+
default: 0,
|
|
20
|
+
min: 0
|
|
25
21
|
},
|
|
26
22
|
title: String,
|
|
27
23
|
images: [{
|
|
28
24
|
key: String,
|
|
29
25
|
url: String
|
|
30
26
|
}],
|
|
31
|
-
|
|
27
|
+
// ❌ REMOVED: totalReserved, flashSaleReservations
|
|
28
|
+
lowStockThreshold: {
|
|
32
29
|
type: Number,
|
|
33
|
-
|
|
34
|
-
default: 0,
|
|
30
|
+
default: 10
|
|
35
31
|
},
|
|
36
|
-
|
|
37
|
-
stockUpdateHistory: [StockUpdateHistorySchema],
|
|
38
|
-
|
|
39
|
-
// Track reservations for flash sales
|
|
40
|
-
flashSaleReservations: [{
|
|
41
|
-
flashSaleId: {
|
|
42
|
-
type: Schema.Types.ObjectId,
|
|
43
|
-
ref: "FlashSale",
|
|
44
|
-
required: true
|
|
45
|
-
},
|
|
46
|
-
quantity: { type: Number, min: 0, default: 0 },
|
|
47
|
-
status: {
|
|
48
|
-
type: String,
|
|
49
|
-
enum: ['reserved', 'active', 'released', 'sold', 'partially_sold'],
|
|
50
|
-
default: 'reserved'
|
|
51
|
-
},
|
|
52
|
-
reservedAt: { type: Date, default: Date.now },
|
|
53
|
-
releasedAt: Date,
|
|
54
|
-
soldQuantity: { type: Number, default: 0 }
|
|
55
|
-
}],
|
|
56
|
-
totalReserved: {
|
|
32
|
+
reorderQuantity: {
|
|
57
33
|
type: Number,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
34
|
+
default: 50
|
|
35
|
+
},
|
|
36
|
+
isInStock: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: true
|
|
39
|
+
},
|
|
40
|
+
lastRestocked: {
|
|
41
|
+
type: Date,
|
|
42
|
+
default: null
|
|
43
|
+
},
|
|
44
|
+
stockUpdateHistory: [{
|
|
45
|
+
change: Number,
|
|
46
|
+
previousQuantity: Number,
|
|
47
|
+
newQuantity: Number,
|
|
48
|
+
reason: String,
|
|
49
|
+
updatedAt: { type: Date, default: Date.now }
|
|
50
|
+
}]
|
|
61
51
|
},
|
|
62
52
|
{ timestamps: true }
|
|
63
53
|
);
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
55
|
+
const Stock = mongoose.models.stocks || mongoose.model("stocks", stockSchema);
|
|
56
|
+
export default Stock;
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// // models/Stock.js - Simplified version without flash sale reservations
|
|
60
|
+
// import mongoose from "mongoose";
|
|
61
|
+
// const { Schema } = mongoose;
|
|
62
|
+
|
|
63
|
+
// const stockSchema = new mongoose.Schema(
|
|
64
|
+
// {
|
|
65
|
+
// productListingId: {
|
|
66
|
+
// type: Schema.Types.ObjectId,
|
|
67
|
+
// ref: "productlistings",
|
|
68
|
+
// required: true,
|
|
69
|
+
// unique: true
|
|
70
|
+
// },
|
|
71
|
+
// quantity: {
|
|
72
|
+
// type: Number,
|
|
73
|
+
// default: 0,
|
|
74
|
+
// min: 0
|
|
75
|
+
// },
|
|
76
|
+
// // REMOVED: totalReserved field since we don't need reservations
|
|
77
|
+
// // REMOVED: flashSaleReservations array completely
|
|
78
|
+
// lowStockThreshold: {
|
|
79
|
+
// type: Number,
|
|
80
|
+
// default: 10
|
|
81
|
+
// },
|
|
82
|
+
// reorderQuantity: {
|
|
83
|
+
// type: Number,
|
|
84
|
+
// default: 50
|
|
85
|
+
// },
|
|
86
|
+
// isInStock: {
|
|
87
|
+
// type: Boolean,
|
|
88
|
+
// default: true
|
|
89
|
+
// },
|
|
90
|
+
// lastRestocked: {
|
|
91
|
+
// type: Date,
|
|
92
|
+
// default: null
|
|
93
|
+
// }
|
|
94
|
+
// },
|
|
95
|
+
// { timestamps: true }
|
|
96
|
+
// );
|
|
74
97
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
98
|
+
// // Indexes
|
|
99
|
+
// stockSchema.index({ productListingId: 1 });
|
|
100
|
+
// stockSchema.index({ isInStock: 1 });
|
|
101
|
+
// stockSchema.index({ quantity: 1 });
|
|
79
102
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
StockSchema.set('toObject', { virtuals: true });
|
|
103
|
+
// const Stock = mongoose.models.stocks || mongoose.model("stocks", stockSchema);
|
|
104
|
+
// export default Stock;
|
|
83
105
|
|
|
84
|
-
const Stock = mongoose.models.stocks || mongoose.model("stocks", StockSchema);
|
|
85
|
-
export default Stock;
|
package/models/user.model.js
CHANGED
|
@@ -7,20 +7,22 @@ import Seller from "./seller.model.js";
|
|
|
7
7
|
const UserSchema = new mongoose.Schema(
|
|
8
8
|
{
|
|
9
9
|
devices: [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
{
|
|
11
|
+
deviceId: { type: String, required: true },
|
|
12
|
+
fcmToken: { type: String, required: true },
|
|
13
|
+
snsEndpointArn: { type: String, default: null }, // New field for SNS
|
|
14
|
+
platform: {
|
|
15
|
+
type: String,
|
|
16
|
+
enum: ["web", "android", "ios", "mobile-web"],
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
lastLogin: { type: Date, default: Date.now },
|
|
20
|
+
lastLogout: { type: Date },
|
|
21
|
+
isActive: { type: Boolean, default: true },
|
|
22
|
+
_id: false,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
|
|
24
26
|
sellerInfo: { type: mongoose.Schema.Types.ObjectId, ref: "sellers" },
|
|
25
27
|
dropshipperInfo: { type: mongoose.Schema.Types.ObjectId, ref: "dropshippers" },
|
|
26
28
|
categories: { type: [String], default: [], maxLength: 100 },
|
|
@@ -49,7 +51,6 @@ const UserSchema = new mongoose.Schema(
|
|
|
49
51
|
},
|
|
50
52
|
maxLength: 120,
|
|
51
53
|
},
|
|
52
|
-
mobile: { type: String, trim: true, maxLength: 15 },
|
|
53
54
|
isEmailVerified: { type: Boolean, default: false },
|
|
54
55
|
role: {
|
|
55
56
|
type: String,
|
|
@@ -120,6 +121,13 @@ const UserSchema = new mongoose.Schema(
|
|
|
120
121
|
isAddressSelected: { type: Boolean, default: false },
|
|
121
122
|
addressSelectedDate: { type: Date },
|
|
122
123
|
|
|
124
|
+
//Mobile Verification
|
|
125
|
+
mobile: { type: String, trim: true, maxLength: 15, unique: true, sparse: true }, // Make it unique but allow nulls
|
|
126
|
+
isMobileVerified: { type: Boolean, default: false },
|
|
127
|
+
mobileVerificationOTP: { type: String, select: false }, // 'select: false' hides it from default queries
|
|
128
|
+
mobileVerificationOTPExpiry: { type: Date, select: false },
|
|
129
|
+
|
|
130
|
+
|
|
123
131
|
// Payment Mandate Setup (Auto-Payment)
|
|
124
132
|
payuMandate: {
|
|
125
133
|
mandateToken: { type: String, default: null },
|
|
@@ -144,7 +152,11 @@ const UserSchema = new mongoose.Schema(
|
|
|
144
152
|
},
|
|
145
153
|
isAutoPaymentEnabled: { type: Boolean, default: false },
|
|
146
154
|
autoPaymentSetupDate: { type: Date },
|
|
147
|
-
|
|
155
|
+
onboardingStatus: {
|
|
156
|
+
type: String,
|
|
157
|
+
enum: ["pending","profile_setup", "completed"],
|
|
158
|
+
default: "completed" // Default to completed for existing users
|
|
159
|
+
},
|
|
148
160
|
deviceInfo: {
|
|
149
161
|
deviceId: { type: String, default: null },
|
|
150
162
|
appPlatform: { type: String, enum: ["android", "ios", "web"], default: "web" },
|