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.
@@ -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
- status: String,
106
- reason: String,
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
- flashSale: {
16
- isActive: { type: Boolean, default: false },
17
- flashSaleId: { type: mongoose.Schema.Types.ObjectId, ref: 'FlashSale' },
18
- flashPrice: { type: Number, default: 0 },
19
- flashStock: { type: Number, default: 0 },
20
- originalPrice: { type: Number, default: 0 },
21
- endsAt: { type: Date, default: null },
22
- startsAt: { type: Date, default: null }
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
- logisticsType: {
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, // Change from Date to 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, // ADDED in frontend, already exists here
73
+ manufacturerAddress: String,
77
74
  countryOfOrigin: String,
78
- netQuantity: { type: String, default: null }, // ADDED (String to accommodate units like '500g')
79
- packagingType: { type: String, default: null }, // ADDED
80
- weight: { // For shipping calculations
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: { // For shipping calculations
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 }, // ADDED in frontend, already exists here (changed to allow null)
90
- shelfLife: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
91
- batchNumber: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
92
- gstRate: { type: Number, default: null }, // Changed to allow null if needed
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 }, // ADDED
95
- sellerGSTIN: { type: String, default: null }, // ADDED
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 }, // Default added
99
- duration: { type: String, default: null }, // Default added
95
+ hasWarranty: { type: Boolean, default: false },
96
+ duration: { type: String, default: null },
100
97
  },
101
- fssaiLicenseNo: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
102
- bisCertification: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
103
- importerName: { type: String, default: null }, // Default added
104
- importerAddress: { type: String, default: null }, // Default added
105
- importerGSTIN: { type: String, default: null }, // Default added
106
- eWasteCompliance: { type: Boolean, default: false }, // Default added
107
- recyclablePackaging: { type: Boolean, default: false }, // Default added
108
- hazardousMaterials: { type: String, default: null }, // Default added
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
- type: Boolean,
130
- default: false
131
- },
132
- returnDays: {
133
- type: Number,
134
- min: 0,
135
- default: null
136
- },
137
- size: {
138
- type: String,
139
- default: null
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
- },ratingSummary: {
145
- averageRating: { type: Number, default: 0 },
146
- totalRatings: { type: Number, default: 0 },
147
- ratingDistribution: {
148
- 1: { type: Number, default: 0 },
149
- 2: { type: Number, default: 0 },
150
- 3: { type: Number, default: 0 },
151
- 4: { type: Number, default: 0 },
152
- 5: { type: Number, default: 0 }
153
- }
154
- },
155
- totalReviews: { type: Number, default: 0 }
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
- // match: [/^[A-Za-z0-9-]{6,}$/, 'SKU must be at least 6 alphanumeric characters']
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
- // ProductListingSchema.index({ sellerId: 1, sku: 1 }, { unique: true });
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
-
@@ -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
- requireAutoFollow: {
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
- // Show Schema
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
- sellerId: { // <--- REMOVE or comment out this line
31
- type: mongoose.Schema.Types.ObjectId,
32
- ref: "sellers",
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' // Dynamic reference
86
+ refPath: 'hostModel'
39
87
  },
40
- hostModel: { // Type of host
88
+ hostModel: {
41
89
  type: String,
42
90
  required: true,
43
- enum: ['sellers', 'dropshippers'] // Allowed host types
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 // Add index for better query performance
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, // URL or file path
124
+ type: String,
79
125
  default: null,
80
126
  },
81
127
  previewVideo: {
82
- type: String, // URL or file path
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, // URL for the live stream
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: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
117
- productPrice: { type: Number, min: 0, default: null }, // Price set by host for this show
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: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
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
- showSchema.pre('save', function (next) {
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;
@@ -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
- // Sub-schema for manual stock updates
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
- quantity: {
27
+ // ❌ REMOVED: totalReserved, flashSaleReservations
28
+ lowStockThreshold: {
32
29
  type: Number,
33
- min: 0,
34
- default: 0,
30
+ default: 10
35
31
  },
36
- // History for manual stock adjustments
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
- min: 0,
59
- default: 0
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
- // Pre-save hook to calculate totalReserved
66
- StockSchema.pre('save', function(next) {
67
- if (this.isModified('flashSaleReservations')) {
68
- this.totalReserved = this.flashSaleReservations.reduce((total, reservation) => {
69
- return total + (['reserved', 'active'].includes(reservation.status) ? reservation.quantity : 0);
70
- }, 0);
71
- }
72
- next();
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
- // Virtual for available quantity
76
- StockSchema.virtual('availableQuantity').get(function() {
77
- return Math.max(0, this.quantity - this.totalReserved);
78
- });
98
+ // // Indexes
99
+ // stockSchema.index({ productListingId: 1 });
100
+ // stockSchema.index({ isInStock: 1 });
101
+ // stockSchema.index({ quantity: 1 });
79
102
 
80
- // Ensure virtuals are included in JSON output
81
- StockSchema.set('toJSON', { virtuals: true });
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;
@@ -7,20 +7,22 @@ import Seller from "./seller.model.js";
7
7
  const UserSchema = new mongoose.Schema(
8
8
  {
9
9
  devices: [
10
- {
11
- deviceId: { type: String, required: true },
12
- fcmToken: { type: String, required: true },
13
- platform: {
14
- type: String,
15
- enum: ["web", "android", "ios", "mobile-web"],
16
- required: true,
17
- },
18
- lastLogin: { type: Date, default: Date.now },
19
- lastLogout: { type: Date },
20
- isActive: { type: Boolean, default: true },
21
- _id: false,
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" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flykup_model_production",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "private": false,