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.

139 lines (119 loc) 5.14 kB
import { models } from '../../mongoDB/index.js'; /** * Shared Product Availability Service * Contains logic for checking product availability and stock status. */ export class SharedProductAvailabilityService { /** * Check equipment availability * @param {string} id - Equipment ID * @param {Date|string} startDate - Start date * @param {Date|string} endDate - End date * @returns {Promise<Object>} Availability result { available: boolean, reason: string } */ static async checkEquipmentAvailability(id, startDate, endDate) { try { const equipment = await models.Equipment.findById(id); if (!equipment) { return { available: false, reason: 'Equipment not found' }; } if (!equipment.isActive) { return { available: false, reason: 'Equipment is inactive' }; } if (equipment.inventory.availableUnits <= 0) { return { available: false, reason: 'No units available' }; } // Check for booking conflicts const conflicts = await models.Booking.countDocuments({ product: id, status: { $in: ['confirmed', 'preparing', 'in_progress'] }, $or: [ { 'typeSpecificData.rentalPeriod.startDate': { $lt: new Date(endDate) }, 'typeSpecificData.rentalPeriod.endDate': { $gt: new Date(startDate) } } ] }); if (conflicts > 0) { return { available: false, reason: 'Equipment is booked for this period' }; } // Check maintenance schedule const isInMaintenance = equipment.availabilityRules?.maintenanceSchedule?.some(schedule => { const maintenanceStart = new Date(schedule.startDate); const maintenanceEnd = new Date(schedule.endDate); const requestStart = new Date(startDate); const requestEnd = new Date(endDate); return (maintenanceStart <= requestEnd && maintenanceEnd >= requestStart); }); if (isInMaintenance) { return { available: false, reason: 'Equipment is under maintenance' }; } // Check blackout dates const hasBlackoutConflict = equipment.availabilityRules?.blackoutDates?.some(blackoutDate => { const blackout = new Date(blackoutDate); const requestStart = new Date(startDate); const requestEnd = new Date(endDate); return (blackout >= requestStart && blackout <= requestEnd); }); if (hasBlackoutConflict) { return { available: false, reason: 'Equipment is unavailable due to blackout period' }; } return { available: true }; } catch (error) { console.error('Error in checkEquipmentAvailability:', error); throw new Error(`Failed to check equipment availability: ${error.message}`); } } /** * Check if product is in stock * @param {Object} product - Product object * @param {string} productType - Product type * @param {number} quantity - Required quantity * @returns {boolean} Is in stock */ static isInStock(product, productType, quantity = 1) { switch (productType) { case 'minimart': return (product.inventory?.stockQuantity || 0) >= quantity; case 'makeover_session': case 'studio_session': return product.isActive !== false; // Services are always "in stock" if active case 'equipment': default: return (product.inventory?.availableUnits || 0) >= quantity; } } /** * Validate product for cart addition * @param {Object} product - Product object * @param {string} productType - Product type * @param {number} quantity - Required quantity * @returns {Object} Validation result { isValid, errors } */ static validateProductForCart(product, productType, quantity = 1) { const errors = []; if (!product) { errors.push('Product not found'); return { isValid: false, errors }; } if (!this.isInStock(product, productType, quantity)) { errors.push('Product is out of stock'); } if (quantity < 1) { errors.push('Invalid quantity'); } // Type-specific validations switch (productType) { case 'makeover_session': case 'studio_session': if (!product.isActive) { errors.push('Service is not available'); } break; } return { isValid: errors.length === 0, errors }; } }