UNPKG

@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.

159 lines (142 loc) 4.44 kB
import mongoose from 'mongoose'; const minimartItemSchema = new mongoose.Schema({ // Basic Information name: { type: String, required: [true, 'Item name is required'], trim: true, maxlength: [100, 'Item name must be less than 100 characters'] }, description: { type: String, required: [true, 'Description is required'], maxlength: [1000, 'Description must be less than 1000 characters'] }, shortDescription: { type: String, maxlength: [200, 'Short description must be less than 200 characters'] }, type: { type: String, default: 'mini_mart_sale' }, // Category & Classification category: { type: String, required: [true, 'Category is required'] }, subcategory: { type: String, maxlength: [100, 'Subcategory must be less than 100 characters'] }, // Pricing Information pricing: { salePrice: { type: Number, required: [true, 'Sale price is required'], min: [0, 'Sale price must be positive'] }, costPrice: { type: Number, min: [0, 'Cost price must be positive'] }, compareAtPrice: { type: Number, min: [0, 'Compare at price must be positive'] }, currency: { type: String, default: 'NGN' } }, // Simple Inventory (no complex tracking) inventory: { stockQuantity: { type: Number, required: [true, 'Stock quantity is required'], min: [0, 'Stock quantity cannot be negative'], default: 0 } }, // Product Identification sku: { type: String, unique: true, default: () => 'MINI-' + Date.now() + '-' + Math.random().toString(36).substr(2, 5).toUpperCase() }, barcode: { type: String, unique: true, sparse: true // Allow multiple null values }, // Media & Content images: [String], // Array of Cloudinary image URLs tags: [String], // Status & Features isActive: { type: Boolean, default: true }, isFeatured: { type: Boolean, default: false }, // Admin & Audit createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'Admin' }, lastModifiedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'Admin' }, // Additional Notes notes: String }, { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }); // Virtuals minimartItemSchema.virtual('profitMargin').get(function () { if (this.pricing.costPrice && this.pricing.salePrice) { return ((this.pricing.salePrice - this.pricing.costPrice) / this.pricing.salePrice) * 100; } return 0; }); minimartItemSchema.virtual('isOutOfStock').get(function () { return this.inventory.stockQuantity <= 0; }); // Indexes for efficient queries minimartItemSchema.index({ name: 'text', description: 'text', tags: 'text' }); minimartItemSchema.index({ category: 1 }); minimartItemSchema.index({ isActive: 1 }); minimartItemSchema.index({ isFeatured: 1 }); minimartItemSchema.index({ 'pricing.salePrice': 1 }); // Note: sku and barcode already have unique indexes from schema definition minimartItemSchema.index({ createdAt: -1 }); // Pre-save middleware minimartItemSchema.pre('save', function (next) { // Auto-generate SKU if not provided if (!this.sku) { this.sku = 'MINI-' + Date.now() + '-' + Math.random().toString(36).substr(2, 5).toUpperCase(); } next(); }); // Simple methods minimartItemSchema.methods.adjustStock = function (quantity, operation = 'subtract') { if (operation === 'add') { this.inventory.stockQuantity += quantity; } else { this.inventory.stockQuantity = Math.max(0, this.inventory.stockQuantity - quantity); } return this.save(); }; // Static methods minimartItemSchema.statics.getOutOfStockItems = function () { return this.find({ 'inventory.stockQuantity': { $lte: 0 }, isActive: true }); }; export default mongoose.model('MinimartItem', minimartItemSchema);