let db; function init(database) { db = database; } function getSummary(req, res) { const today = new Date().toISOString().split('T')[0]; const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; const monthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; const queries = { revenue_today: `SELECT COALESCE(SUM(total_price), 0) as revenue FROM bookings WHERE status NOT IN ('отменена','новая') AND checkin_date = ?`, revenue_week: `SELECT COALESCE(SUM(total_price), 0) as revenue FROM bookings WHERE status NOT IN ('отменена','новая') AND checkin_date >= ?`, revenue_month: `SELECT COALESCE(SUM(total_price), 0) as revenue FROM bookings WHERE status NOT IN ('отменена','новая') AND checkin_date >= ?`, checkin_today: `SELECT COUNT(*) as count FROM bookings WHERE status IN ('заселена') AND checkin_date = ?`, checkout_today: `SELECT COUNT(*) as count FROM bookings WHERE status IN ('выехала') AND checkout_date = ?`, new_bookings: `SELECT COUNT(*) as count FROM bookings WHERE status = 'новая'`, active_bookings: `SELECT COUNT(*) as count FROM bookings WHERE status IN ('оплачена','зарезервирована','заселена')`, avg_review: `SELECT COALESCE(AVG(stars), 0) as avg FROM reviews WHERE is_approved = 1`, total_bookings: `SELECT COUNT(*) as total, AVG(julianday(checkout_date) - julianday(checkin_date)) as avg_stay FROM bookings WHERE status NOT IN ('отменена')` }; const results = {}; let completed = 0; const total = Object.keys(queries).length; function finish() { completed++; if (completed < total) return; const now = new Date().toISOString(); const stayingSql = `SELECT COUNT(DISTINCT b.id) as count FROM bookings b WHERE b.status IN ('оплачена','зарезервирована','заселена') AND b.checkin_date <= ? AND b.checkout_date > ?`; db.get(stayingSql, [now, now], (err, staying) => { const stayingCount = (err ? 0 : staying?.count) || 0; db.all(`SELECT r.type, r.rooms_count, COUNT(b.id) as booked FROM rooms r LEFT JOIN bookings b ON b.room_id = r.id AND b.status IN ('новая','оплачена','зарезервирована','заселена') AND b.checkin_date <= ? AND b.checkout_date > ? WHERE r.is_active = 1 GROUP BY r.type, r.rooms_count`, [now, now], (err, occRows) => { const occupancy = { current: 0, by_type: {} }; let totalRooms = 0; let totalBooked = 0; if (occRows) { occRows.forEach(r => { const booked = r.booked || 0; const total = r.rooms_count || 0; occupancy.by_type[r.type] = total > 0 ? Math.round((booked / total) * 100) : 0; totalRooms += total; totalBooked += booked; }); occupancy.current = totalRooms > 0 ? Math.round((totalBooked / totalRooms) * 100) : 0; } res.json({ revenue: { today: results.revenue_today, week: results.revenue_week, month: results.revenue_month }, occupancy: occupancy, bookings: { new: results.new_bookings, active: results.active_bookings, checkin_today: results.checkin_today, checkout_today: results.checkout_today, staying_now: stayingCount, total: results.total_bookings?.total || 0 }, avg_stay_days: Math.round((results.total_bookings?.avg_stay || 0) * 10) / 10, avg_review_stars: Math.round((results.avg_review || 0) * 10) / 10 }); }); }); } Object.entries(queries).forEach(([key, sql]) => { const params = []; if (key === 'revenue_today' || key === 'checkin_today' || key === 'checkout_today') params.push(today); else if (key === 'revenue_week') params.push(weekAgo); else if (key === 'revenue_month') params.push(monthAgo); db.get(sql, params, (err, row) => { if (err) { results[key] = 0; } else if (key.includes('revenue')) results[key] = row.revenue || 0; else if (key === 'total_bookings') results[key] = { total: row.total || 0, avg_stay: row.avg_stay || 0 }; else results[key] = row?.count || row?.avg || 0; finish(); }); }); } function getRevenue(req, res) { const period = req.query.period || 'monthly'; const months = parseInt(req.query.months) || 6; let groupBy; let dateFormat; if (period === 'daily') { groupBy = "date(b.checkin_date)"; dateFormat = 'daily'; } else if (period === 'weekly') { groupBy = "strftime('%Y-W%W', b.checkin_date)"; dateFormat = 'weekly'; } else { groupBy = "strftime('%Y-%m', b.checkin_date)"; dateFormat = 'monthly'; } const sinceDate = new Date(); sinceDate.setMonth(sinceDate.getMonth() - months); const since = sinceDate.toISOString().split('T')[0]; db.all( `SELECT ${groupBy} as label, COALESCE(SUM(total_price), 0) as revenue, COUNT(*) as bookings FROM bookings b WHERE b.status NOT IN ('отменена','новая') AND b.checkin_date >= ? GROUP BY label ORDER BY label ASC`, [since], (err, rows) => { if (err) return res.status(500).json({ error: 'Database error' }); res.json({ period: dateFormat, data: rows }); } ); } function setupRoutes(app, authenticateToken, requireAdmin) { app.get('/api/admin/reports/summary', authenticateToken, getSummary); app.get('/api/admin/reports/revenue', authenticateToken, getRevenue); } module.exports = { init, setupRoutes };