@ideal-photography/shared
Version:
Shared MongoDB and utility logic for Ideal Photography PWAs: users, products, services, bookings, orders/cart, galleries, reviews, notifications, campaigns, settings, audit logs, minimart items/orders, and push notification subscriptions.
297 lines (289 loc) • 7.05 kB
JavaScript
import mongoose from 'mongoose';
const equipmentSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0
},
pricing: {
baseRate: {
type: Number,
min: 0,
default: function () { return this.price || 0; }
},
rateType: {
type: String,
enum: ['hourly', 'daily', 'weekly', 'monthly', 'fixed'],
default: 'daily'
},
currency: {
type: String,
default: 'NGN'
},
discounts: [{
type: {
type: String,
enum: ['percentage', 'fixed', 'bulk']
},
value: Number,
minQuantity: Number,
validFrom: Date,
validUntil: Date,
description: String
}],
seasonalPricing: [{
name: String,
multiplier: Number,
startDate: String,
endDate: String,
description: String
}]
},
category: {
type: String,
required: true,
enum: [
'cameras',
'lenses',
'tripods',
'lighting',
'audio',
'screens',
'accessories',
'stabilizers',
'filters',
'memory_cards',
'batteries',
'bags',
'monitors',
'computers',
'projectors',
'microphones',
'stands',
'cables',
'adapters',
'tools',
'cases',
'other'
]
},
subcategory: {
type: String,
required: false // Make optional to match validation
},
shortDescription: {
type: String,
maxlength: 200
},
type: {
type: String,
default: 'equipment_rental'
},
images: [{
url: String,
alt: String,
isPrimary: { type: Boolean, default: false }
}],
isActive: {
type: Boolean,
default: true
},
stock: {
type: Number,
default: 0,
min: 0
},
// Additional fields expected by Admin system
specifications: {
type: Map,
of: mongoose.Schema.Types.Mixed,
default: {}
},
features: [{
type: String,
maxlength: 200
}],
whatsIncluded: [{
type: String,
maxlength: 200
}],
requirements: [{
type: String,
maxlength: 200
}],
usageInstructions: {
type: String,
maxlength: 1000
},
tags: [String],
sku: {
type: String,
unique: true,
sparse: true
},
barcode: String,
isFeatured: {
type: Boolean,
default: false
},
isDigital: {
type: Boolean,
default: false
},
inventory: {
totalUnits: {
type: Number,
min: 1,
default: 1
},
availableUnits: {
type: Number,
min: 0,
default: 1
},
condition: {
type: String,
enum: ['excellent', 'good', 'fair', 'needs_repair'],
default: 'excellent'
},
serialNumbers: [String]
},
availabilityRules: {
minRentalPeriod: {
type: Number,
default: 1
},
maxRentalPeriod: {
type: Number,
default: 168
},
advanceBookingRequired: {
type: Number,
default: 1
}
},
analytics: {
viewCount: {
type: Number,
default: 0
},
purchaseCount: {
type: Number,
default: 0
},
reviewCount: {
type: Number,
default: 0
},
revenue: {
type: Number,
default: 0
},
popularityScore: {
type: Number,
default: 0
},
rentalCount: {
type: Number,
default: 0
},
averageRating: {
type: Number,
default: 0
}
},
relatedProducts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Equipment'
}],
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Admin'
},
lastModifiedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Admin'
},
discount: {
type: {
type: String,
enum: ['percentage', 'fixed'],
default: 'percentage'
},
value: {
type: Number,
default: 0
},
isActive: {
type: Boolean,
default: false
}
},
maintenance: {
serviceHistory: [{
date: Date,
description: String,
cost: Number,
performedBy: String
}]
},
bundleProducts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Equipment'
}],
uuid: {
type: String,
unique: true,
sparse: true
},
metadata: {
type: Map,
of: mongoose.Schema.Types.Mixed
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date,
default: Date.now
}
}, {
timestamps: true
});
// Indexes for performance
equipmentSchema.index({ category: 1, subcategory: 1 });
equipmentSchema.index({ isActive: 1 });
equipmentSchema.index({ name: 'text', description: 'text' });
// Virtual for primary image
equipmentSchema.virtual('primaryImage').get(function () {
const primary = this.images.find(img => img.isPrimary);
return primary || this.images[0] || null;
});
// Method to check if equipment is available
equipmentSchema.methods.isAvailable = function () {
return this.isActive && this.stock > 0;
};
// Method to get display price
equipmentSchema.methods.getDisplayPrice = function () {
return `₦${this.price.toLocaleString()}`;
};
equipmentSchema.pre('save', function (next) {
// Sync legacy stock field with new inventory.availableUnits
if (this.isModified('inventory.availableUnits') && !this.isModified('stock')) {
this.stock = this.inventory.availableUnits;
} else if (this.isModified('stock') && !this.isModified('inventory.availableUnits')) {
this.inventory.availableUnits = this.stock;
}
next();
});
export default mongoose.model('Equipment', equipmentSchema);