flykup_model_production 1.0.10 → 1.0.11
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/index.js +4 -2
- package/models/BankVerification.js +10 -0
- package/models/LiveStreamInteraction.model.js +9 -0
- package/models/ProductInteraction.model.js +93 -27
- package/models/ShoppableInteraction.model.js +19 -1
- package/models/chat.model.js +7 -0
- package/models/notification.model.js +4 -0
- package/models/order.modal.js +100 -5
- package/models/orderPayment.model.js +91 -0
- package/models/productListing.model.js +213 -3
- package/models/seller.model.js +5 -1
- package/models/shoppableVideo.model.js +28 -8
- package/models/shows.model.js +14 -166
- package/models/stock.model.js +76 -117
- package/models/user.model.js +0 -2
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import SellerDraft from './models/sellerDraft.model.js';
|
|
|
33
33
|
import AadhaarVerification from './models/AadhaarVerification.js';
|
|
34
34
|
import BankVerification from './models/BankVerification.js';
|
|
35
35
|
import GSTVerification from './models/GSTVerification.js';
|
|
36
|
+
import OrderPayment from './models/orderPayment.model.js';
|
|
36
37
|
let initialized = false;
|
|
37
38
|
|
|
38
39
|
export async function initializeSharedModels({ token, dbUri }) {
|
|
@@ -97,7 +98,7 @@ export const SellerDraftModel = createModelProxy(SellerDraft);
|
|
|
97
98
|
export const AadhaarVerificationModel = createModelProxy(AadhaarVerification);
|
|
98
99
|
export const BankVerificationModel = createModelProxy(BankVerification);
|
|
99
100
|
export const GSTVerificationModel = createModelProxy(GSTVerification);
|
|
100
|
-
|
|
101
|
+
export const OrderPaymentModel = createModelProxy(OrderPayment)
|
|
101
102
|
|
|
102
103
|
export {
|
|
103
104
|
AdminModel as Admin,
|
|
@@ -134,5 +135,6 @@ export {
|
|
|
134
135
|
SellerDraftModel as SellerDraft,
|
|
135
136
|
AadhaarVerificationModel as AadhaarVerification,
|
|
136
137
|
BankVerificationModel as BankVerification,
|
|
137
|
-
GSTVerificationModel as GSTVerification
|
|
138
|
+
GSTVerificationModel as GSTVerification,
|
|
139
|
+
OrderPaymentModel as OrderPayment
|
|
138
140
|
};
|
|
@@ -8,6 +8,16 @@ const bankVerificationSchema = new mongoose.Schema({
|
|
|
8
8
|
required: true,
|
|
9
9
|
unique: true, // Ensures only one bank verification per user
|
|
10
10
|
index: true
|
|
11
|
+
},
|
|
12
|
+
sellerId: {
|
|
13
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
14
|
+
ref: 'sellers',
|
|
15
|
+
|
|
16
|
+
},
|
|
17
|
+
role: {
|
|
18
|
+
type: String,
|
|
19
|
+
enum: ['user', 'seller', 'admin'],
|
|
20
|
+
|
|
11
21
|
},
|
|
12
22
|
accountNumber: {
|
|
13
23
|
type: String,
|
|
@@ -69,6 +69,15 @@ const liveStreamInteractionSchema = new mongoose.Schema({
|
|
|
69
69
|
type: mongoose.Schema.Types.ObjectId,
|
|
70
70
|
ref: 'productlistings'
|
|
71
71
|
}],
|
|
72
|
+
auctionBids: [{
|
|
73
|
+
productId: { type: mongoose.Schema.Types.ObjectId, ref: 'productlistings' },
|
|
74
|
+
bidAmount: { type: Number, required: true },
|
|
75
|
+
bidTime: { type: Date, default: Date.now }
|
|
76
|
+
}],
|
|
77
|
+
giveawayEntries: [{
|
|
78
|
+
productId: { type: mongoose.Schema.Types.ObjectId, ref: 'productlistings' },
|
|
79
|
+
entryTime: { type: Date, default: Date.now }
|
|
80
|
+
}]
|
|
72
81
|
}, {
|
|
73
82
|
timestamps: true
|
|
74
83
|
});
|
|
@@ -1,34 +1,53 @@
|
|
|
1
1
|
import mongoose, { Schema, model } from 'mongoose';
|
|
2
2
|
|
|
3
3
|
const productInteractionSchema = new mongoose.Schema({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
message: '{VALUE} is not an integer value'
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
review: {
|
|
29
|
-
type: String,
|
|
30
|
-
maxlength: 500
|
|
4
|
+
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
|
|
5
|
+
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true },
|
|
6
|
+
seller: { type: mongoose.Schema.Types.ObjectId, ref: 'Seller', required: true },
|
|
7
|
+
location: {
|
|
8
|
+
city: String,
|
|
9
|
+
region: String,
|
|
10
|
+
country: String
|
|
11
|
+
},
|
|
12
|
+
platform: { type: String, enum: ['web', 'mobile', 'unknown'], default: 'mobile' },
|
|
13
|
+
device: { type: String, enum: ['mobile', 'desktop', 'tablet', 'other'] },
|
|
14
|
+
browser: String,
|
|
15
|
+
os: String,
|
|
16
|
+
ip: { type: String, required: true },
|
|
17
|
+
isIndianRegion: { type: Boolean, default: false },
|
|
18
|
+
rating: {
|
|
19
|
+
type: Number,
|
|
20
|
+
min: 1,
|
|
21
|
+
max: 5,
|
|
22
|
+
validate: {
|
|
23
|
+
validator: Number.isInteger,
|
|
24
|
+
message: '{VALUE} is not an integer value'
|
|
31
25
|
}
|
|
26
|
+
},
|
|
27
|
+
review: {
|
|
28
|
+
type: String,
|
|
29
|
+
maxlength: 500
|
|
30
|
+
},
|
|
31
|
+
viewCount: { type: Number, default: 1 },
|
|
32
|
+
// NEW: Trust signals tracking
|
|
33
|
+
trustSignals: {
|
|
34
|
+
reviewViews: { type: Number, default: 0 }, // How many times user viewed reviews
|
|
35
|
+
reviewViewDuration: { type: Number, default: 0 }, // Total time spent on reviews (seconds)
|
|
36
|
+
comparisonViews: { type: Number, default: 0 }, // How many times user compared with other products
|
|
37
|
+
priceCheckCount: { type: Number, default: 0 }, // How many times user checked pricing
|
|
38
|
+
lastTrustSignalAt: { type: Date } // When the last trust signal was recorded
|
|
39
|
+
},
|
|
40
|
+
// NEW: Engagement status for sellers
|
|
41
|
+
engagementStatus: {
|
|
42
|
+
type: String,
|
|
43
|
+
enum: ['new', 'interested', 'hesitant', 'contacted', 'converted'],
|
|
44
|
+
default: 'new'
|
|
45
|
+
},
|
|
46
|
+
sellerNotes: [{
|
|
47
|
+
note: String,
|
|
48
|
+
createdAt: { type: Date, default: Date.now },
|
|
49
|
+
updatedAt: { type: Date, default: Date.now }
|
|
50
|
+
}]
|
|
32
51
|
}, { timestamps: true });
|
|
33
52
|
|
|
34
53
|
// Ensures a user's view is counted only once per product
|
|
@@ -40,3 +59,50 @@ productInteractionSchema.index({ createdAt: -1 });
|
|
|
40
59
|
// Safe export to prevent OverwriteModelError
|
|
41
60
|
const ProductInteraction = mongoose.models.ProductInteraction || mongoose.model('ProductInteraction', productInteractionSchema);
|
|
42
61
|
export default ProductInteraction;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
// import mongoose, { Schema, model } from 'mongoose';
|
|
67
|
+
|
|
68
|
+
// const productInteractionSchema = new mongoose.Schema({
|
|
69
|
+
// product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
|
|
70
|
+
// user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true },
|
|
71
|
+
// seller: { type: mongoose.Schema.Types.ObjectId, ref: 'Seller', required: true },
|
|
72
|
+
// location: {
|
|
73
|
+
// city: String,
|
|
74
|
+
// region: String,
|
|
75
|
+
// country: String
|
|
76
|
+
// },
|
|
77
|
+
// // --- NEW FIELD ---
|
|
78
|
+
// platform: { type: String, enum: ['web', 'mobile', 'unknown'], default: 'mobile' },
|
|
79
|
+
// device: { type: String, enum: ['mobile', 'desktop', 'tablet', 'other'] },
|
|
80
|
+
// browser: String,
|
|
81
|
+
// os: String,
|
|
82
|
+
// ip: { type: String, required: true },
|
|
83
|
+
// isIndianRegion: { type: Boolean, default: false },
|
|
84
|
+
// rating: {
|
|
85
|
+
// type: Number,
|
|
86
|
+
// min: 1,
|
|
87
|
+
// max: 5,
|
|
88
|
+
// validate: {
|
|
89
|
+
// validator: Number.isInteger,
|
|
90
|
+
// message: '{VALUE} is not an integer value'
|
|
91
|
+
// }
|
|
92
|
+
// },
|
|
93
|
+
// review: {
|
|
94
|
+
// type: String,
|
|
95
|
+
// maxlength: 500
|
|
96
|
+
// },
|
|
97
|
+
// viewCount: { type: Number, default: 1 }
|
|
98
|
+
// }, { timestamps: true });
|
|
99
|
+
|
|
100
|
+
// // Ensures a user's view is counted only once per product
|
|
101
|
+
// productInteractionSchema.index({ product: 1, user: 1 }, { unique: true, partialFilterExpression: { user: { $exists: true } } });
|
|
102
|
+
|
|
103
|
+
// // Additional indexes for performance
|
|
104
|
+
// productInteractionSchema.index({ createdAt: -1 });
|
|
105
|
+
|
|
106
|
+
// // Safe export to prevent OverwriteModelError
|
|
107
|
+
// const ProductInteraction = mongoose.models.ProductInteraction || mongoose.model('ProductInteraction', productInteractionSchema);
|
|
108
|
+
// export default ProductInteraction;
|
|
@@ -61,7 +61,22 @@ const shoppableVideoInteractionSchema = new mongoose.Schema({
|
|
|
61
61
|
productsClicked: [{ // Unique list of products clicked by this user for this video
|
|
62
62
|
type: mongoose.Schema.Types.ObjectId,
|
|
63
63
|
ref: 'productlistings'
|
|
64
|
-
}]
|
|
64
|
+
}],
|
|
65
|
+
maxSecondReached: {
|
|
66
|
+
type: Number,
|
|
67
|
+
default: 0
|
|
68
|
+
},
|
|
69
|
+
rewatchCount: {
|
|
70
|
+
type: Number,
|
|
71
|
+
default: 0
|
|
72
|
+
},
|
|
73
|
+
watchSessions: [{
|
|
74
|
+
startedAt: Date,
|
|
75
|
+
endedAt: Date,
|
|
76
|
+
duration: Number,
|
|
77
|
+
completionPercentage: Number,
|
|
78
|
+
maxSecond: Number
|
|
79
|
+
}],
|
|
65
80
|
}, {
|
|
66
81
|
timestamps: true
|
|
67
82
|
});
|
|
@@ -71,6 +86,9 @@ shoppableVideoInteractionSchema.index({ video: 1, user: 1 }, {
|
|
|
71
86
|
unique: true,
|
|
72
87
|
partialFilterExpression: { user: { $exists: true } }
|
|
73
88
|
});
|
|
89
|
+
shoppableVideoInteractionSchema.index({ video: 1, maxSecondReached: 1 });
|
|
90
|
+
shoppableVideoInteractionSchema.index({ video: 1, rewatchCount: 1 });
|
|
91
|
+
shoppableVideoInteractionSchema.index({ host: 1, user: 1 });
|
|
74
92
|
|
|
75
93
|
// Ensures a unique interaction record per anonymous session per video.
|
|
76
94
|
shoppableVideoInteractionSchema.index({ video: 1, sessionIdentifier: 1 }, {
|
package/models/chat.model.js
CHANGED
|
@@ -191,6 +191,13 @@ const ChatMessageSchema = new mongoose.Schema({
|
|
|
191
191
|
required: true
|
|
192
192
|
},
|
|
193
193
|
|
|
194
|
+
source: {
|
|
195
|
+
type: String,
|
|
196
|
+
enum: ['chat', 'product', 'shoppable', 'liveVideo'],
|
|
197
|
+
default: 'chat'
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
|
|
194
201
|
// Message content
|
|
195
202
|
messageType: {
|
|
196
203
|
type: String,
|
package/models/order.modal.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
|
|
1
2
|
// order.modal.js
|
|
2
|
-
import mongoose
|
|
3
|
+
import mongoose from 'mongoose';
|
|
3
4
|
import { nanoid } from 'nanoid';
|
|
4
5
|
|
|
5
6
|
const orderSchema = new mongoose.Schema(
|
|
@@ -8,7 +9,7 @@ const orderSchema = new mongoose.Schema(
|
|
|
8
9
|
type: String,
|
|
9
10
|
required: true,
|
|
10
11
|
unique: true,
|
|
11
|
-
default: () => `ORD-${nanoid(8)}`
|
|
12
|
+
default: () => `FLY-ORD-${nanoid(8)}`
|
|
12
13
|
},
|
|
13
14
|
userId: {
|
|
14
15
|
type: mongoose.Schema.Types.ObjectId,
|
|
@@ -48,7 +49,15 @@ const orderSchema = new mongoose.Schema(
|
|
|
48
49
|
type: String,
|
|
49
50
|
enum: ['CGST+SGST', 'IGST',"NON-GST"],
|
|
50
51
|
required: true
|
|
51
|
-
}
|
|
52
|
+
},
|
|
53
|
+
isFlashSale: {
|
|
54
|
+
type: Boolean,
|
|
55
|
+
default: false
|
|
56
|
+
},
|
|
57
|
+
flashPrice: {
|
|
58
|
+
type: Number,
|
|
59
|
+
default: null // Store the specific flash price used for this item
|
|
60
|
+
}
|
|
52
61
|
}],
|
|
53
62
|
totalBaseAmount: {
|
|
54
63
|
type: Number,
|
|
@@ -100,6 +109,77 @@ const orderSchema = new mongoose.Schema(
|
|
|
100
109
|
default: null
|
|
101
110
|
}
|
|
102
111
|
},
|
|
112
|
+
logisticsDetails: {
|
|
113
|
+
shipmentMethod: {
|
|
114
|
+
type: String,
|
|
115
|
+
enum: ['self_shipment', 'flykup_logistics'],
|
|
116
|
+
default: 'flykup_logistics'
|
|
117
|
+
},
|
|
118
|
+
awbNumber: String,
|
|
119
|
+
carrier: String,
|
|
120
|
+
carrierId: String,
|
|
121
|
+
trackingNumber: String,
|
|
122
|
+
trackingUrl: String,
|
|
123
|
+
expectedDeliveryDate: Date,
|
|
124
|
+
shipmentBookedAt: Date,
|
|
125
|
+
logisticsResponse: mongoose.Schema.Types.Mixed,
|
|
126
|
+
shipmentBookingStatus: {
|
|
127
|
+
type: String,
|
|
128
|
+
enum: ['pending', 'booked', 'failed', 'cancelled', 'cancellation_failed'], // Add the new value here
|
|
129
|
+
default: 'pending'
|
|
130
|
+
},
|
|
131
|
+
shipmentBookingError: String,
|
|
132
|
+
multipleSellers: {
|
|
133
|
+
type: Boolean,
|
|
134
|
+
default: false
|
|
135
|
+
},
|
|
136
|
+
selectedCourier: {
|
|
137
|
+
courierId: String,
|
|
138
|
+
courierName: String,
|
|
139
|
+
deliveryCharges: Number,
|
|
140
|
+
estimatedDays: Number,
|
|
141
|
+
deliveryDate: String
|
|
142
|
+
},
|
|
143
|
+
cancellationAttemptedAt: Date,
|
|
144
|
+
cancellationResult: {
|
|
145
|
+
success: Boolean,
|
|
146
|
+
message: String,
|
|
147
|
+
error: String,
|
|
148
|
+
responseData: mongoose.Schema.Types.Mixed
|
|
149
|
+
},
|
|
150
|
+
cancellationEligibility: {
|
|
151
|
+
eligible: Boolean,
|
|
152
|
+
reason: String
|
|
153
|
+
},
|
|
154
|
+
tracking: {
|
|
155
|
+
lastSyncedAt: Date,
|
|
156
|
+
currentStatus: String,
|
|
157
|
+
statusCode: String,
|
|
158
|
+
progressPercentage: Number,
|
|
159
|
+
currentLocation: String,
|
|
160
|
+
estimatedDelivery: Date,
|
|
161
|
+
trackingTimeline: [{
|
|
162
|
+
date: Date,
|
|
163
|
+
status: String,
|
|
164
|
+
location: String,
|
|
165
|
+
icon: String
|
|
166
|
+
}]
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
packageDetails: {
|
|
170
|
+
totalWeight: Number,
|
|
171
|
+
weightUnit: {
|
|
172
|
+
type: String,
|
|
173
|
+
default: 'grams'
|
|
174
|
+
},
|
|
175
|
+
dimensions: {
|
|
176
|
+
length: Number,
|
|
177
|
+
width: Number,
|
|
178
|
+
height: Number
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
volumetricWeight: Number
|
|
182
|
+
},
|
|
103
183
|
pickupAddresses: [{
|
|
104
184
|
sellerUserId: {
|
|
105
185
|
type: mongoose.Schema.Types.ObjectId,
|
|
@@ -136,7 +216,7 @@ const orderSchema = new mongoose.Schema(
|
|
|
136
216
|
}],
|
|
137
217
|
paymentMethod: {
|
|
138
218
|
type: String,
|
|
139
|
-
enum: ['CARD', 'UPI', 'NETBANKING', 'WALLET', 'COD', "PENDING_PAYMENT", "CASHFREE", "Online"],
|
|
219
|
+
enum: ['CARD', 'UPI', 'NETBANKING', 'WALLET', 'COD', "PENDING_PAYMENT", "CASHFREE","RAZORPAY", "Online"],
|
|
140
220
|
required: true
|
|
141
221
|
},
|
|
142
222
|
paymentStatus: {
|
|
@@ -168,7 +248,7 @@ const orderSchema = new mongoose.Schema(
|
|
|
168
248
|
},
|
|
169
249
|
sourceType: {
|
|
170
250
|
type: String,
|
|
171
|
-
enum: ['static', 'shoppable_video', 'livestream', 'auction'],
|
|
251
|
+
enum: ['static', 'shoppable_video', 'livestream', 'auction', 'flash_sale', 'giveaway'],
|
|
172
252
|
required: true
|
|
173
253
|
},
|
|
174
254
|
sourceRefId: String,
|
|
@@ -254,6 +334,19 @@ cancelSource: {
|
|
|
254
334
|
enum: ['user', 'seller', 'system'],
|
|
255
335
|
default: 'user'
|
|
256
336
|
},
|
|
337
|
+
flashSaleItems: [{
|
|
338
|
+
productId: {
|
|
339
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
340
|
+
ref: 'productlistings'
|
|
341
|
+
},
|
|
342
|
+
flashSaleId: {
|
|
343
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
344
|
+
ref: 'FlashSale'
|
|
345
|
+
},
|
|
346
|
+
quantity: Number,
|
|
347
|
+
flashPrice: Number,
|
|
348
|
+
originalPrice: Number
|
|
349
|
+
}],
|
|
257
350
|
payment: {
|
|
258
351
|
type: mongoose.Schema.Types.ObjectId,
|
|
259
352
|
ref: 'orderPayment'
|
|
@@ -273,10 +366,12 @@ payment: {
|
|
|
273
366
|
},
|
|
274
367
|
invoiceError: String
|
|
275
368
|
},
|
|
369
|
+
|
|
276
370
|
{ timestamps: true }
|
|
277
371
|
);
|
|
278
372
|
|
|
279
373
|
|
|
280
374
|
|
|
375
|
+
|
|
281
376
|
const Order = mongoose.models.Order || mongoose.model('Order', orderSchema);
|
|
282
377
|
export default Order;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const paymentSchema = new mongoose.Schema({
|
|
4
|
+
orderId: {
|
|
5
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
6
|
+
ref: 'Order',
|
|
7
|
+
required: true
|
|
8
|
+
},
|
|
9
|
+
userId: {
|
|
10
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
11
|
+
ref: 'users',
|
|
12
|
+
required: true
|
|
13
|
+
},
|
|
14
|
+
paymentGateway: {
|
|
15
|
+
type: String,
|
|
16
|
+
enum: ['RAZORPAY', 'CASHFREE'],
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// Razorpay-specific fields
|
|
21
|
+
razorpayOrderId: {
|
|
22
|
+
type: String,
|
|
23
|
+
// ✅ This field is now required only if the gateway is RAZORPAY
|
|
24
|
+
required: function() {
|
|
25
|
+
return this.paymentGateway === 'RAZORPAY';
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
razorpayPaymentId: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: null
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Cashfree-specific fields
|
|
34
|
+
cfPaymentSessionId: {
|
|
35
|
+
type: String,
|
|
36
|
+
// ✅ This field is now required only if the gateway is CASHFREE
|
|
37
|
+
required: function() {
|
|
38
|
+
return this.paymentGateway === 'CASHFREE';
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
cfOrderId: {
|
|
42
|
+
type: String,
|
|
43
|
+
// ✅ This field is now required only if the gateway is CASHFREE
|
|
44
|
+
required: function() {
|
|
45
|
+
return this.paymentGateway === 'CASHFREE';
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
amount: {
|
|
50
|
+
type: Number,
|
|
51
|
+
required: true
|
|
52
|
+
},
|
|
53
|
+
currency: {
|
|
54
|
+
type: String,
|
|
55
|
+
default: 'INR'
|
|
56
|
+
},
|
|
57
|
+
status: {
|
|
58
|
+
type: String,
|
|
59
|
+
enum: ['PENDING', 'SUCCESS', 'FAILED', 'REFUNDED'],
|
|
60
|
+
default: 'PENDING'
|
|
61
|
+
},
|
|
62
|
+
paymentMethod: String,
|
|
63
|
+
transactionTime: Date,
|
|
64
|
+
sellerPayments: [{
|
|
65
|
+
sellerId: {
|
|
66
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
67
|
+
ref: 'users',
|
|
68
|
+
required: true
|
|
69
|
+
},
|
|
70
|
+
amount: Number,
|
|
71
|
+
settlementStatus: {
|
|
72
|
+
type: String,
|
|
73
|
+
enum: ['PENDING', 'SETTLED','FAILED'],
|
|
74
|
+
default: 'PENDING'
|
|
75
|
+
}
|
|
76
|
+
}]
|
|
77
|
+
}, {
|
|
78
|
+
timestamps: true
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Create indexes for better performance
|
|
82
|
+
paymentSchema.index({ razorpayOrderId: 1 });
|
|
83
|
+
paymentSchema.index({ cfOrderId: 1 });
|
|
84
|
+
paymentSchema.index({ orderId: 1 });
|
|
85
|
+
|
|
86
|
+
// ❌ The pre-save middleware is no longer needed and should be removed.
|
|
87
|
+
|
|
88
|
+
const OrderPayment =
|
|
89
|
+
mongoose.models.OrderPayment || mongoose.model('orderPayment', paymentSchema);
|
|
90
|
+
|
|
91
|
+
export default OrderPayment;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
// backend/models/ProductListing.js (or similar path)
|
|
2
6
|
import mongoose from "mongoose";
|
|
3
7
|
const { Schema } = mongoose;
|
|
@@ -8,6 +12,20 @@ const ProductListingSchema = new Schema(
|
|
|
8
12
|
stockId: { type: Schema.Types.ObjectId, ref: "stocks" },
|
|
9
13
|
title: String,
|
|
10
14
|
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
|
+
},
|
|
24
|
+
sku: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: true,
|
|
27
|
+
index: true
|
|
28
|
+
},
|
|
11
29
|
images: [
|
|
12
30
|
{
|
|
13
31
|
key: { type: String, maxLength: 255, default: null },
|
|
@@ -36,7 +54,22 @@ const ProductListingSchema = new Schema(
|
|
|
36
54
|
min: 0,
|
|
37
55
|
default: null,
|
|
38
56
|
},
|
|
39
|
-
|
|
57
|
+
logisticsType: {
|
|
58
|
+
type: String,
|
|
59
|
+
enum: ["flykupLogistics", "selfShipment"],
|
|
60
|
+
default: null,
|
|
61
|
+
},
|
|
62
|
+
deliveryCharge: {
|
|
63
|
+
type: Number,
|
|
64
|
+
min: 0,
|
|
65
|
+
default: null,
|
|
66
|
+
},
|
|
67
|
+
estimatedDeliveryDate: {
|
|
68
|
+
type: Number, // Change from Date to Number
|
|
69
|
+
min: 0,
|
|
70
|
+
default: null,
|
|
71
|
+
},
|
|
72
|
+
|
|
40
73
|
// --- ADDED/UPDATED FIELDS FROM FRONTEND ---
|
|
41
74
|
brand: { type: String, default: null }, // ADDED
|
|
42
75
|
manufacturer: String,
|
|
@@ -123,10 +156,187 @@ totalReviews: { type: Number, default: 0 }
|
|
|
123
156
|
},
|
|
124
157
|
{ timestamps: true }
|
|
125
158
|
);
|
|
159
|
+
ProductListingSchema.index({ title: 'text', description: 'text', category: 'text' });
|
|
126
160
|
|
|
127
|
-
// Optional: Ensure strict mode is not preventing fields if you intended flexibility (default is true)
|
|
128
|
-
// ProductListingSchema.set('strict', false); // Use with caution
|
|
129
161
|
|
|
130
162
|
// Safe export to prevent OverwriteModelError
|
|
131
163
|
const ProductListing = mongoose.models.productlistings || mongoose.model("productlistings", ProductListingSchema);
|
|
132
164
|
export default ProductListing;
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
// // backend/models/ProductListing.js (or similar path)
|
|
177
|
+
// import mongoose from "mongoose";
|
|
178
|
+
// const { Schema } = mongoose;
|
|
179
|
+
|
|
180
|
+
// const ProductListingSchema = new Schema(
|
|
181
|
+
// {
|
|
182
|
+
// sellerId: { type: Schema.Types.ObjectId, ref: "sellers" },
|
|
183
|
+
// stockId: { type: Schema.Types.ObjectId, ref: "stocks" },
|
|
184
|
+
// title: String,
|
|
185
|
+
// description: String,
|
|
186
|
+
// flashSale: {
|
|
187
|
+
// isActive: { type: Boolean, default: false },
|
|
188
|
+
// flashSaleId: { type: mongoose.Schema.Types.ObjectId, ref: 'FlashSale' },
|
|
189
|
+
// flashPrice: { type: Number, default: 0 },
|
|
190
|
+
// flashStock: { type: Number, default: 0 },
|
|
191
|
+
// originalPrice: { type: Number, default: 0 },
|
|
192
|
+
// endsAt: { type: Date, default: null },
|
|
193
|
+
// startsAt: { type: Date, default: null }
|
|
194
|
+
// },
|
|
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
|
+
// sku: {
|
|
209
|
+
// type: String,
|
|
210
|
+
// required: true,
|
|
211
|
+
// match: [/^[A-Za-z0-9-]{6,}$/, 'SKU must be at least 6 alphanumeric characters']
|
|
212
|
+
// },
|
|
213
|
+
// // ----------end of new fields added
|
|
214
|
+
// images: [
|
|
215
|
+
// {
|
|
216
|
+
// key: { type: String, maxLength: 255, default: null },
|
|
217
|
+
// }
|
|
218
|
+
// ],
|
|
219
|
+
// category: String,
|
|
220
|
+
// subcategory: String,
|
|
221
|
+
// hsnNo: String,
|
|
222
|
+
// MRP: {
|
|
223
|
+
// type: Number,
|
|
224
|
+
// min: 0,
|
|
225
|
+
// default: null,
|
|
226
|
+
// },
|
|
227
|
+
// productPrice: {
|
|
228
|
+
// type: Number,
|
|
229
|
+
// min: 0,
|
|
230
|
+
// default: null,
|
|
231
|
+
// },
|
|
232
|
+
// startingPrice: {
|
|
233
|
+
// type: Number,
|
|
234
|
+
// min: 0,
|
|
235
|
+
// default: null,
|
|
236
|
+
// },
|
|
237
|
+
// reservedPrice: {
|
|
238
|
+
// type: Number,
|
|
239
|
+
// min: 0,
|
|
240
|
+
// default: null,
|
|
241
|
+
// },
|
|
242
|
+
|
|
243
|
+
// // --- ADDED/UPDATED FIELDS FROM FRONTEND ---
|
|
244
|
+
// brand: { type: String, default: null }, // ADDED
|
|
245
|
+
// manufacturer: String,
|
|
246
|
+
// manufacturerAddress: String, // ADDED in frontend, already exists here
|
|
247
|
+
// countryOfOrigin: String,
|
|
248
|
+
// netQuantity: { type: String, default: null }, // ADDED (String to accommodate units like '500g')
|
|
249
|
+
// packagingType: { type: String, default: null }, // ADDED
|
|
250
|
+
// weight: { // For shipping calculations
|
|
251
|
+
// value: { type: Number, default: null },
|
|
252
|
+
// unit: { type: String, default: null },
|
|
253
|
+
// },
|
|
254
|
+
// dimensions: { // For shipping calculations
|
|
255
|
+
// length: { type: Number, default: null },
|
|
256
|
+
// width: { type: Number, default: null },
|
|
257
|
+
// height: { type: Number, default: null },
|
|
258
|
+
// },
|
|
259
|
+
// expiryDate: { type: Date, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
260
|
+
// shelfLife: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
261
|
+
// batchNumber: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
262
|
+
// gstRate: { type: Number, default: null }, // Changed to allow null if needed
|
|
263
|
+
// sellerName: String,
|
|
264
|
+
// sellerContact: { type: String, default: null }, // ADDED
|
|
265
|
+
// sellerGSTIN: { type: String, default: null }, // ADDED
|
|
266
|
+
// returnPolicy: [String],
|
|
267
|
+
// warranty: {
|
|
268
|
+
// hasWarranty: { type: Boolean, default: false }, // Default added
|
|
269
|
+
// duration: { type: String, default: null }, // Default added
|
|
270
|
+
// },
|
|
271
|
+
// fssaiLicenseNo: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
272
|
+
// bisCertification: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
273
|
+
// importerName: { type: String, default: null }, // Default added
|
|
274
|
+
// importerAddress: { type: String, default: null }, // Default added
|
|
275
|
+
// importerGSTIN: { type: String, default: null }, // Default added
|
|
276
|
+
// eWasteCompliance: { type: Boolean, default: false }, // Default added
|
|
277
|
+
// recyclablePackaging: { type: Boolean, default: false }, // Default added
|
|
278
|
+
// hazardousMaterials: { type: String, default: null }, // Default added
|
|
279
|
+
// allowDropshipping: {
|
|
280
|
+
// type: Boolean,
|
|
281
|
+
// default: false,
|
|
282
|
+
// index: true,
|
|
283
|
+
// },
|
|
284
|
+
// commissionRate: {
|
|
285
|
+
// type: Number,
|
|
286
|
+
// min: [0, 'Commission rate cannot be negative.'],
|
|
287
|
+
// // max: [100, 'Commission rate cannot exceed 100%.'], // Max was 25, changed to 100 based on frontend
|
|
288
|
+
// max: [100, 'Commission rate cannot exceed 100%.'],
|
|
289
|
+
// default: null,
|
|
290
|
+
// // Removed Mongoose-level required validation dependent on allowDropshipping
|
|
291
|
+
// // Let application logic handle this if needed, or adjust validator
|
|
292
|
+
// // required: [
|
|
293
|
+
// // function () { return this.allowDropshipping === true; },
|
|
294
|
+
// // 'Commission rate is required when allowing dropshipping.'
|
|
295
|
+
// // ],
|
|
296
|
+
// // validate: { ... } // Keep or remove validation as needed
|
|
297
|
+
// },
|
|
298
|
+
// hasReturn: {
|
|
299
|
+
// type: Boolean,
|
|
300
|
+
// default: false
|
|
301
|
+
// },
|
|
302
|
+
// returnDays: {
|
|
303
|
+
// type: Number,
|
|
304
|
+
// min: 0,
|
|
305
|
+
// default: null
|
|
306
|
+
// },
|
|
307
|
+
// size: {
|
|
308
|
+
// type: String,
|
|
309
|
+
// default: null
|
|
310
|
+
// },
|
|
311
|
+
// isActive: {
|
|
312
|
+
// type: Boolean,
|
|
313
|
+
// default: true,
|
|
314
|
+
// },ratingSummary: {
|
|
315
|
+
// averageRating: { type: Number, default: 0 },
|
|
316
|
+
// totalRatings: { type: Number, default: 0 },
|
|
317
|
+
// ratingDistribution: {
|
|
318
|
+
// 1: { type: Number, default: 0 },
|
|
319
|
+
// 2: { type: Number, default: 0 },
|
|
320
|
+
// 3: { type: Number, default: 0 },
|
|
321
|
+
// 4: { type: Number, default: 0 },
|
|
322
|
+
// 5: { type: Number, default: 0 }
|
|
323
|
+
// }
|
|
324
|
+
// },
|
|
325
|
+
// totalReviews: { type: Number, default: 0 }
|
|
326
|
+
// },
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
// { timestamps: true }
|
|
331
|
+
// );
|
|
332
|
+
// ProductListingSchema.index({ title: 'text', description: 'text', category: 'text' });
|
|
333
|
+
// ProductListingSchema.index({ sellerId: 1, sku: 1 }, { unique: true });
|
|
334
|
+
|
|
335
|
+
// // Safe export to prevent OverwriteModelError
|
|
336
|
+
// const ProductListing = mongoose.models.productlistings || mongoose.model("productlistings", ProductListingSchema);
|
|
337
|
+
// export default ProductListing;
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
package/models/seller.model.js
CHANGED
|
@@ -113,6 +113,8 @@ const SellerSchema = new mongoose.Schema(
|
|
|
113
113
|
fssaiCertificateFileName: {
|
|
114
114
|
type: String
|
|
115
115
|
},
|
|
116
|
+
bisCertificateFileName: { type: String, default: null }, // New BIS certificate
|
|
117
|
+
qualityCertificateFileName: { type: String, default: null }, // New Quality certificate
|
|
116
118
|
productCatalog: {
|
|
117
119
|
link: { type: String },
|
|
118
120
|
file: { type: String }, // for old sellers
|
|
@@ -289,7 +291,8 @@ const SellerSchema = new mongoose.Schema(
|
|
|
289
291
|
start: { type: String, default: '09:00' },
|
|
290
292
|
end: { type: String, default: '18:00' }
|
|
291
293
|
}
|
|
292
|
-
}
|
|
294
|
+
},
|
|
295
|
+
|
|
293
296
|
},
|
|
294
297
|
{ timestamps: true, strict: true }
|
|
295
298
|
);
|
|
@@ -308,5 +311,6 @@ SellerSchema.index({
|
|
|
308
311
|
},
|
|
309
312
|
name: "seller_text_index"
|
|
310
313
|
});
|
|
314
|
+
|
|
311
315
|
const Seller = mongoose.models.sellers || mongoose.model("sellers", SellerSchema);
|
|
312
316
|
export default Seller;
|
|
@@ -37,8 +37,8 @@ const ShoppableVideoSchema = new Schema(
|
|
|
37
37
|
required: false,
|
|
38
38
|
trim: true,
|
|
39
39
|
},
|
|
40
|
-
category: { type: String, required:
|
|
41
|
-
subcategory: { type: String, required:
|
|
40
|
+
category: { type: String, required: false, trim: false},
|
|
41
|
+
subcategory: { type: String, required: false, trim: false },
|
|
42
42
|
// thumbnailURL might be set initially or updated by the processing service
|
|
43
43
|
isThumbnailEnabled: {
|
|
44
44
|
type: Boolean,
|
|
@@ -48,7 +48,7 @@ const ShoppableVideoSchema = new Schema(
|
|
|
48
48
|
type: String,
|
|
49
49
|
default: null
|
|
50
50
|
},
|
|
51
|
-
isProductsAvailable : { type : Boolean, required : true, default :
|
|
51
|
+
isProductsAvailable : { type : Boolean, required : true, default : false },
|
|
52
52
|
productsListed: {
|
|
53
53
|
type: [
|
|
54
54
|
{
|
|
@@ -167,11 +167,31 @@ const ShoppableVideoSchema = new Schema(
|
|
|
167
167
|
type: Number,
|
|
168
168
|
default: 0
|
|
169
169
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
// Admin blocking fields
|
|
171
|
+
isBlocked: {
|
|
172
|
+
type: Boolean,
|
|
173
|
+
default: false,
|
|
174
|
+
index: true // For efficient queries of blocked videos
|
|
175
|
+
},
|
|
176
|
+
blockedBy: {
|
|
177
|
+
type: Schema.Types.ObjectId,
|
|
178
|
+
ref: 'Admin',
|
|
179
|
+
default: null
|
|
180
|
+
},
|
|
181
|
+
blockReason: {
|
|
182
|
+
type: String,
|
|
183
|
+
default: null,
|
|
184
|
+
trim: true
|
|
185
|
+
},
|
|
186
|
+
blockedAt: {
|
|
187
|
+
type: Date,
|
|
188
|
+
default: null
|
|
189
|
+
},
|
|
190
|
+
isEligibleToPlay: {
|
|
191
|
+
type: Boolean,
|
|
192
|
+
default: false, // Default to true, can be set to false if video is not eligible for playback
|
|
193
|
+
index: true // Index for quick lookups
|
|
194
|
+
}
|
|
175
195
|
},
|
|
176
196
|
{ timestamps: true }
|
|
177
197
|
);
|
package/models/shows.model.js
CHANGED
|
@@ -1,99 +1,6 @@
|
|
|
1
1
|
import mongoose from "mongoose";
|
|
2
2
|
const { Schema } = mongoose;
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
// Comment Schema
|
|
6
|
-
const commentSchema = new Schema({
|
|
7
|
-
user: {
|
|
8
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
9
|
-
ref: "users", // Reference to the User model
|
|
10
|
-
},
|
|
11
|
-
text: {
|
|
12
|
-
type: String,
|
|
13
|
-
},
|
|
14
|
-
timeStamp: {
|
|
15
|
-
type: Date,
|
|
16
|
-
default: Date.now,
|
|
17
|
-
},
|
|
18
|
-
createdAt: {
|
|
19
|
-
type: Date,
|
|
20
|
-
default: Date.now,
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const bidSchema = new mongoose.Schema({
|
|
25
|
-
user: {
|
|
26
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
27
|
-
ref: "users", // Reference to the User model
|
|
28
|
-
required: true,
|
|
29
|
-
},
|
|
30
|
-
amount: {
|
|
31
|
-
type: Number,
|
|
32
|
-
required: true,
|
|
33
|
-
},
|
|
34
|
-
createdAt: {
|
|
35
|
-
type: Date,
|
|
36
|
-
default: Date.now,
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Auction Schema
|
|
41
|
-
const auctionSchema = new mongoose.Schema(
|
|
42
|
-
{
|
|
43
|
-
streamId: {
|
|
44
|
-
type: String,
|
|
45
|
-
required: true,
|
|
46
|
-
},
|
|
47
|
-
product: {
|
|
48
|
-
type: String,
|
|
49
|
-
},
|
|
50
|
-
auctionType: {
|
|
51
|
-
type: String, // 'default' or other types
|
|
52
|
-
},
|
|
53
|
-
startingBid: {
|
|
54
|
-
type: Number,
|
|
55
|
-
},
|
|
56
|
-
increment: {
|
|
57
|
-
type: Number,
|
|
58
|
-
default: 500,
|
|
59
|
-
},
|
|
60
|
-
currentHighestBid: {
|
|
61
|
-
type: Number,
|
|
62
|
-
default: 0,
|
|
63
|
-
},
|
|
64
|
-
nextBid1: {
|
|
65
|
-
type: Number,
|
|
66
|
-
default: 0,
|
|
67
|
-
},
|
|
68
|
-
nextBid2: {
|
|
69
|
-
type: Number,
|
|
70
|
-
default: 0,
|
|
71
|
-
},
|
|
72
|
-
highestBidder: {
|
|
73
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
74
|
-
ref: "users", // Store reference to the highest bidder
|
|
75
|
-
default: null,
|
|
76
|
-
},
|
|
77
|
-
bidderWon: {
|
|
78
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
79
|
-
ref: "users", // Store reference to the highest bidder
|
|
80
|
-
default: null,
|
|
81
|
-
},
|
|
82
|
-
bids: [bidSchema], // Array of bids
|
|
83
|
-
endsAt: {
|
|
84
|
-
type: Date,
|
|
85
|
-
},
|
|
86
|
-
isActive: {
|
|
87
|
-
type: Boolean,
|
|
88
|
-
default: true,
|
|
89
|
-
},
|
|
90
|
-
uniqueStreamId: {
|
|
91
|
-
type: String,
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
{ timestamps: true }
|
|
95
|
-
);
|
|
96
|
-
|
|
97
4
|
const giveawayProductSchema = new Schema({
|
|
98
5
|
_id: false,
|
|
99
6
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings", required: true },
|
|
@@ -119,22 +26,22 @@ const giveawayProductSchema = new Schema({
|
|
|
119
26
|
// Show Schema
|
|
120
27
|
const showSchema = new Schema(
|
|
121
28
|
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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)
|
|
128
36
|
type: mongoose.Schema.Types.ObjectId,
|
|
129
37
|
required: true,
|
|
130
38
|
refPath: 'hostModel' // Dynamic reference
|
|
131
39
|
},
|
|
132
|
-
hostModel: { //
|
|
40
|
+
hostModel: { // Type of host
|
|
133
41
|
type: String,
|
|
134
42
|
required: true,
|
|
135
43
|
enum: ['sellers', 'dropshippers'] // Allowed host types
|
|
136
44
|
},
|
|
137
|
-
// === MODIFIED END: Host Information ===
|
|
138
45
|
|
|
139
46
|
title: {
|
|
140
47
|
type: String,
|
|
@@ -191,7 +98,6 @@ const showSchema = new Schema(
|
|
|
191
98
|
enum: ['created', 'live', 'cancelled', 'ended'],
|
|
192
99
|
default: 'created'
|
|
193
100
|
},
|
|
194
|
-
comments: [commentSchema], // Array of comments
|
|
195
101
|
likes: {
|
|
196
102
|
type: Number,
|
|
197
103
|
default: 0,
|
|
@@ -201,14 +107,12 @@ const showSchema = new Schema(
|
|
|
201
107
|
ref: 'users',
|
|
202
108
|
default: [],
|
|
203
109
|
},
|
|
204
|
-
|
|
205
|
-
currentAuction: auctionSchema,
|
|
110
|
+
|
|
206
111
|
buyNowProducts: {
|
|
207
112
|
type: [
|
|
208
113
|
{
|
|
209
114
|
_id: false,
|
|
210
115
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
211
|
-
// --- ADDED productOwnerSellerId ---
|
|
212
116
|
productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
213
117
|
productPrice: { type: Number, min: 0, default: null }, // Price set by host for this show
|
|
214
118
|
},
|
|
@@ -220,64 +124,10 @@ const showSchema = new Schema(
|
|
|
220
124
|
{
|
|
221
125
|
_id: false,
|
|
222
126
|
productId: { type: Schema.Types.ObjectId, ref: "productlistings" },
|
|
223
|
-
// --- ADDED productOwnerSellerId ---
|
|
224
127
|
productOwnerSellerId: { type: Schema.Types.ObjectId, ref: 'sellers', required: true },
|
|
225
128
|
startingPrice: { type: Number, min: 0, default: null },
|
|
226
129
|
reservedPrice: { type: Number, min: 0, default: null },
|
|
227
|
-
|
|
228
|
-
type: Boolean,
|
|
229
|
-
default: false
|
|
230
|
-
},
|
|
231
|
-
streamId: {
|
|
232
|
-
type: String,
|
|
233
|
-
},
|
|
234
|
-
// product: {
|
|
235
|
-
// type: String,
|
|
236
|
-
// },
|
|
237
|
-
auctionType: {
|
|
238
|
-
type: String, // 'default' or other types
|
|
239
|
-
},
|
|
240
|
-
startingBid: {
|
|
241
|
-
type: Number,
|
|
242
|
-
},
|
|
243
|
-
increment: {
|
|
244
|
-
type: Number,
|
|
245
|
-
default: 5,
|
|
246
|
-
},
|
|
247
|
-
currentHighestBid: {
|
|
248
|
-
type: Number,
|
|
249
|
-
default: 0,
|
|
250
|
-
},
|
|
251
|
-
nextBid1: {
|
|
252
|
-
type: Number,
|
|
253
|
-
default: 0,
|
|
254
|
-
},
|
|
255
|
-
nextBid2: {
|
|
256
|
-
type: Number,
|
|
257
|
-
default: 0,
|
|
258
|
-
},
|
|
259
|
-
highestBidder: {
|
|
260
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
261
|
-
ref: "users", // Store reference to the highest bidder
|
|
262
|
-
default: null,
|
|
263
|
-
},
|
|
264
|
-
bidderWon: {
|
|
265
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
266
|
-
ref: "users", // Store reference to the highest bidder
|
|
267
|
-
default: null,
|
|
268
|
-
},
|
|
269
|
-
bids: [bidSchema], // Array of bids
|
|
270
|
-
endsAt: {
|
|
271
|
-
type: Date,
|
|
272
|
-
},
|
|
273
|
-
isActive: {
|
|
274
|
-
type: Boolean,
|
|
275
|
-
default: true,
|
|
276
|
-
},
|
|
277
|
-
uniqueStreamId: {
|
|
278
|
-
type: String,
|
|
279
|
-
},
|
|
280
|
-
auctionNumber: { // <-- New field in auctionProducts sub-schema
|
|
130
|
+
auctionNumber: {
|
|
281
131
|
type: Number,
|
|
282
132
|
}
|
|
283
133
|
},
|
|
@@ -291,16 +141,14 @@ const showSchema = new Schema(
|
|
|
291
141
|
},
|
|
292
142
|
|
|
293
143
|
enabledProductTypes: {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
,
|
|
144
|
+
buyNow: { type: Boolean, default: false },
|
|
145
|
+
auction: { type: Boolean, default: false },
|
|
146
|
+
giveaway: { type: Boolean, default: false }
|
|
147
|
+
},
|
|
299
148
|
notes: {
|
|
300
149
|
type: String,
|
|
301
150
|
default: ''
|
|
302
151
|
},
|
|
303
|
-
// giveaways: [giveawaySchema], // Array of giveaways
|
|
304
152
|
},
|
|
305
153
|
{ timestamps: true }
|
|
306
154
|
);
|
package/models/stock.model.js
CHANGED
|
@@ -1,126 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
// models/Stock.js
|
|
1
3
|
import mongoose from "mongoose";
|
|
2
|
-
const { Schema
|
|
4
|
+
const { Schema } = mongoose;
|
|
5
|
+
|
|
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
|
+
|
|
3
15
|
|
|
4
16
|
const StockSchema = new Schema(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
productListingId: {
|
|
11
|
-
type: Schema.Types.ObjectId,
|
|
12
|
-
ref: "productlistings",
|
|
13
|
-
},
|
|
14
|
-
title: String,
|
|
15
|
-
quantity: {
|
|
16
|
-
type: Number,
|
|
17
|
-
min: 0,
|
|
18
|
-
default: null,
|
|
19
|
-
},
|
|
20
|
-
images: [
|
|
21
|
-
{
|
|
22
|
-
key: { type: String, maxLength: 255, default: null },
|
|
23
|
-
blobName: { type: String, maxLength: 255, default: null },
|
|
24
|
-
azureUrl: { type: String, maxLength: 1024, default: null },
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
mfgDate: {
|
|
28
|
-
type: String,
|
|
29
|
-
default: null,
|
|
30
|
-
},
|
|
31
|
-
expDate: {
|
|
32
|
-
type: String,
|
|
33
|
-
default: null,
|
|
34
|
-
},
|
|
17
|
+
{
|
|
18
|
+
sellerId: {
|
|
19
|
+
type: Schema.Types.ObjectId,
|
|
20
|
+
ref: "sellers",
|
|
35
21
|
},
|
|
36
|
-
|
|
22
|
+
productListingId: {
|
|
23
|
+
type: Schema.Types.ObjectId,
|
|
24
|
+
ref: "productlistings",
|
|
25
|
+
},
|
|
26
|
+
title: String,
|
|
27
|
+
images: [{
|
|
28
|
+
key: String,
|
|
29
|
+
url: String
|
|
30
|
+
}],
|
|
31
|
+
quantity: {
|
|
32
|
+
type: Number,
|
|
33
|
+
min: 0,
|
|
34
|
+
default: 0,
|
|
35
|
+
},
|
|
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: {
|
|
57
|
+
type: Number,
|
|
58
|
+
min: 0,
|
|
59
|
+
default: 0
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{ timestamps: true }
|
|
37
63
|
);
|
|
38
64
|
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
});
|
|
41
74
|
|
|
75
|
+
// Virtual for available quantity
|
|
76
|
+
StockSchema.virtual('availableQuantity').get(function() {
|
|
77
|
+
return Math.max(0, this.quantity - this.totalReserved);
|
|
78
|
+
});
|
|
42
79
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// const { Schema, model } = mongoose;
|
|
47
|
-
|
|
48
|
-
// const StockSchema = new Schema(
|
|
49
|
-
// {
|
|
50
|
-
// sellerId: {
|
|
51
|
-
// type: Schema.Types.ObjectId,
|
|
52
|
-
// ref: "sellers",
|
|
53
|
-
// },
|
|
54
|
-
// productListingId: {
|
|
55
|
-
// type: Schema.Types.ObjectId,
|
|
56
|
-
// ref: "productlistings",
|
|
57
|
-
// },
|
|
58
|
-
// title: String,
|
|
59
|
-
// quantity: {
|
|
60
|
-
// type: Number,
|
|
61
|
-
// min: 0,
|
|
62
|
-
// default: 0,
|
|
63
|
-
// },
|
|
64
|
-
// // Track reservations for current and upcoming flash sales
|
|
65
|
-
// flashSaleReservations: [{
|
|
66
|
-
// flashSaleId: {
|
|
67
|
-
// type: Schema.Types.ObjectId,
|
|
68
|
-
// ref: "FlashSale",
|
|
69
|
-
// required: true
|
|
70
|
-
// },
|
|
71
|
-
// productId: {
|
|
72
|
-
// type: Schema.Types.ObjectId,
|
|
73
|
-
// ref: "productlistings",
|
|
74
|
-
// required: true
|
|
75
|
-
// },
|
|
76
|
-
// quantity: {
|
|
77
|
-
// type: Number,
|
|
78
|
-
// min: 0,
|
|
79
|
-
// default: 0
|
|
80
|
-
// },
|
|
81
|
-
// status: {
|
|
82
|
-
// type: String,
|
|
83
|
-
// enum: ['reserved', 'active', 'released', 'sold', 'partially_sold'],
|
|
84
|
-
// default: 'reserved'
|
|
85
|
-
// },
|
|
86
|
-
// reservedAt: {
|
|
87
|
-
// type: Date,
|
|
88
|
-
// default: Date.now
|
|
89
|
-
// },
|
|
90
|
-
// releasedAt: Date,
|
|
91
|
-
// soldQuantity: {
|
|
92
|
-
// type: Number,
|
|
93
|
-
// default: 0
|
|
94
|
-
// }
|
|
95
|
-
// }],
|
|
96
|
-
// // Total reserved quantity (calculated field)
|
|
97
|
-
// totalReserved: {
|
|
98
|
-
// type: Number,
|
|
99
|
-
// min: 0,
|
|
100
|
-
// default: 0
|
|
101
|
-
// }
|
|
102
|
-
// },
|
|
103
|
-
// { timestamps: true }
|
|
104
|
-
// );
|
|
105
|
-
|
|
106
|
-
// // Update totalReserved when flashSaleReservations change
|
|
107
|
-
// StockSchema.pre('save', function(next) {
|
|
108
|
-
// if (this.isModified('flashSaleReservations')) {
|
|
109
|
-
// this.totalReserved = this.flashSaleReservations.reduce((total, reservation) => {
|
|
110
|
-
// return total + (reservation.status === 'reserved' || reservation.status === 'active' ? reservation.quantity : 0);
|
|
111
|
-
// }, 0);
|
|
112
|
-
// }
|
|
113
|
-
// next();
|
|
114
|
-
// });
|
|
80
|
+
// Ensure virtuals are included in JSON output
|
|
81
|
+
StockSchema.set('toJSON', { virtuals: true });
|
|
82
|
+
StockSchema.set('toObject', { virtuals: true });
|
|
115
83
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// return Math.max(0, this.quantity - this.totalReserved);
|
|
119
|
-
// });
|
|
120
|
-
|
|
121
|
-
// // Ensure virtuals are included in toJSON output
|
|
122
|
-
// StockSchema.set('toJSON', { virtuals: true });
|
|
123
|
-
// StockSchema.set('toObject', { virtuals: true });
|
|
124
|
-
|
|
125
|
-
// const Stock = mongoose.models.stocks || mongoose.model("stocks", StockSchema);
|
|
126
|
-
// export default Stock;
|
|
84
|
+
const Stock = mongoose.models.stocks || mongoose.model("stocks", StockSchema);
|
|
85
|
+
export default Stock;
|
package/models/user.model.js
CHANGED
|
@@ -93,7 +93,6 @@ const UserSchema = new mongoose.Schema(
|
|
|
93
93
|
},
|
|
94
94
|
],
|
|
95
95
|
filledNewSellerForm: { type: Boolean, default: false },
|
|
96
|
-
|
|
97
96
|
// --- Verification Flow Fields ---
|
|
98
97
|
verificationFlowStatus: {
|
|
99
98
|
type: String,
|
|
@@ -181,7 +180,6 @@ UserSchema.index({
|
|
|
181
180
|
name: "user_text_index"
|
|
182
181
|
});
|
|
183
182
|
|
|
184
|
-
|
|
185
183
|
UserSchema.methods.comparePassword = async function (loginPassword) {
|
|
186
184
|
if (!this.password) return false;
|
|
187
185
|
return bcrypt.compare(loginPassword, this.password);
|