import { Holiday, HolidayType } from '@models/Holiday'; import { Op } from 'sequelize'; import logger from '@utils/logger'; import dayjs from 'dayjs'; export class HolidayService { /** * Get all holidays within a date range */ async getHolidaysInRange(startDate: Date | string, endDate: Date | string): Promise { try { const holidays = await Holiday.findAll({ where: { holidayDate: { [Op.between]: [dayjs(startDate).format('YYYY-MM-DD'), dayjs(endDate).format('YYYY-MM-DD')] }, isActive: true }, attributes: ['holidayDate'], raw: true }); return holidays.map((h: any) => h.holidayDate || h.holiday_date); } catch (error) { logger.error('[Holiday Service] Error fetching holidays:', error); return []; } } /** * Check if a specific date is a holiday */ async isHoliday(date: Date | string): Promise { try { const dateStr = dayjs(date).format('YYYY-MM-DD'); const holiday = await Holiday.findOne({ where: { holidayDate: dateStr, isActive: true } }); return !!holiday; } catch (error) { logger.error('[Holiday Service] Error checking holiday:', error); return false; } } /** * Check if a date is a working day (not weekend or holiday) */ async isWorkingDay(date: Date | string): Promise { const day = dayjs(date); const dayOfWeek = day.day(); // 0 = Sunday, 6 = Saturday // Check if weekend if (dayOfWeek === 0 || dayOfWeek === 6) { return false; } // Check if holiday const isHol = await this.isHoliday(date); return !isHol; } /** * Add a new holiday */ async createHoliday(holidayData: { holidayDate: string; holidayName: string; description?: string; holidayType?: HolidayType; isRecurring?: boolean; recurrenceRule?: string; appliesToDepartments?: string[]; appliesToLocations?: string[]; createdBy: string; }): Promise { try { const holiday = await Holiday.create({ ...holidayData, isActive: true } as any); logger.info(`[Holiday Service] Holiday created: ${holidayData.holidayName} on ${holidayData.holidayDate}`); return holiday; } catch (error) { logger.error('[Holiday Service] Error creating holiday:', error); throw error; } } /** * Update a holiday */ async updateHoliday(holidayId: string, updates: any, updatedBy: string): Promise { try { const holiday = await Holiday.findByPk(holidayId); if (!holiday) { throw new Error('Holiday not found'); } await holiday.update({ ...updates, updatedBy, updatedAt: new Date() }); logger.info(`[Holiday Service] Holiday updated: ${holidayId}`); return holiday; } catch (error) { logger.error('[Holiday Service] Error updating holiday:', error); throw error; } } /** * Delete (deactivate) a holiday */ async deleteHoliday(holidayId: string): Promise { try { await Holiday.update( { isActive: false }, { where: { holidayId } } ); logger.info(`[Holiday Service] Holiday deactivated: ${holidayId}`); return true; } catch (error) { logger.error('[Holiday Service] Error deleting holiday:', error); throw error; } } /** * Get all active holidays */ async getAllActiveHolidays(year?: number): Promise { try { const whereClause: any = { isActive: true }; if (year) { const startDate = `${year}-01-01`; const endDate = `${year}-12-31`; whereClause.holidayDate = { [Op.between]: [startDate, endDate] }; } const holidays = await Holiday.findAll({ where: whereClause, order: [['holidayDate', 'ASC']] }); return holidays; } catch (error) { logger.error('[Holiday Service] Error fetching holidays:', error); return []; } } /** * Get holidays by year for calendar view */ async getHolidayCalendar(year: number): Promise { try { const startDate = `${year}-01-01`; const endDate = `${year}-12-31`; const holidays = await Holiday.findAll({ where: { holidayDate: { [Op.between]: [startDate, endDate] }, isActive: true }, order: [['holidayDate', 'ASC']] }); return holidays.map((h: any) => ({ date: h.holidayDate || h.holiday_date, name: h.holidayName || h.holiday_name, description: h.description, type: h.holidayType || h.holiday_type, isRecurring: h.isRecurring || h.is_recurring })); } catch (error) { logger.error('[Holiday Service] Error fetching holiday calendar:', error); return []; } } /** * Import multiple holidays (bulk upload) */ async bulkImportHolidays(holidays: any[], createdBy: string): Promise<{ success: number; failed: number }> { let success = 0; let failed = 0; for (const holiday of holidays) { try { await this.createHoliday({ ...holiday, createdBy }); success++; } catch (error) { failed++; logger.error(`[Holiday Service] Failed to import holiday: ${holiday.holidayName}`, error); } } logger.info(`[Holiday Service] Bulk import complete: ${success} success, ${failed} failed`); return { success, failed }; } } export const holidayService = new HolidayService();