const config = require('../../config'); const { getRoomBasePriceByType } = config; const seasonalModule = require('../seasonalPrices'); let db; function init(database) { db = database; } function getEffectivePrice(roomType, date, callback) { seasonalModule.getSeasonalPrice(roomType, date, (err, seasonalPrice) => { if (err || !seasonalPrice) { const basePrice = getRoomBasePriceByType(roomType); return callback(null, basePrice); } callback(null, seasonalPrice); }); } function calculateNightPrices(roomType, checkin, checkout, callback) { const nights = config.calculateNights(checkin, checkout); if (nights <= 0) return callback(new Error('Invalid dates')); const dates = []; for (let i = 0; i < nights; i++) { const d = new Date(checkin); d.setDate(d.getDate() + i); dates.push(d.toISOString().split('T')[0]); } let total = 0; let processed = 0; let finished = false; dates.forEach(date => { getEffectivePrice(roomType, date, (err, price) => { if (finished) return; if (err) { finished = true; return callback(err, null); } total += price; processed++; if (processed === dates.length) { finished = true; callback(null, total); } }); }); } function validatePromocode(promocode, callback) { if (!promocode) return callback(null, null); const now = new Date(); db.get(`SELECT * FROM promocodes WHERE code = ? AND is_active = 1`, [promocode], (err, row) => { if (err || !row) return callback(null, null); if (row.valid_from && new Date(row.valid_from) > now) return callback(null, null); if (row.valid_to && new Date(row.valid_to) < now) return callback(null, null); if (row.valid_days) { const createdDate = new Date(row.created_at); const expireDate = new Date(createdDate.getTime() + row.valid_days * 24 * 60 * 60 * 1000); if (expireDate < new Date()) return callback(null, null); } callback(null, row); }); } function checkAvailability(roomId, roomType, checkin, checkout, requestedCount, callback) { let sql; let params; if (roomId) { sql = `SELECT r.rooms_count, COALESCE( (SELECT COUNT(b.id) FROM bookings b WHERE b.room_id = r.id AND b.status IN ('новая','оплачена','зарезервирована','заселена') AND b.checkin_date < ? AND b.checkout_date > ?), 0) as booked FROM rooms r WHERE r.id = ? AND r.is_active = 1`; params = [checkout, checkin, roomId]; } else { sql = `SELECT COALESCE(SUM(r.rooms_count), 0) as rooms_count, (SELECT COUNT(b.id) FROM bookings b WHERE b.room_type = ? AND b.status IN ('новая','оплачена','зарезервирована','заселена') AND b.checkin_date < ? AND b.checkout_date > ?) as booked FROM rooms r WHERE r.type = ? AND r.is_active = 1`; params = [roomType, checkout, checkin, roomType]; } db.get(sql, params, (err, row) => { if (err) return callback(err, null); if (!row) return callback(null, { available: 0, total: 0 }); const available = Math.max(0, row.rooms_count - row.booked); callback(null, { available, total: row.rooms_count, booked: row.booked }); }); } function createBooking(req, res) { const { name, phone, adults, children, checkin, checkout, wishes, room, room_id, promocode } = req.body; if (!name || !phone || !room || !adults || !checkin || !checkout) { return res.status(400).json({ error: 'Missing required fields' }); } const roomType = room; const roomIdValue = room_id ? parseInt(room_id) : null; checkAvailability(roomIdValue, roomType, checkin, checkout, 1, (err, avail) => { if (err) return res.status(500).json({ error: 'Database error' }); if (avail.available < 1) { return res.status(409).json({ error: `На выбранные даты нет свободных номеров типа "${roomType}". Доступно: ${avail.available} из ${avail.total}`, available: avail.available }); } calculateNightPrices(roomType, checkin, checkout, (err, baseSum) => { if (err) return res.status(400).json({ error: err.message }); const basePrice = baseSum * (parseInt(adults) || 1); validatePromocode(promocode, (err, promo) => { if (err) return res.status(500).json({ error: 'Database error' }); let discountPercent = 0; let promocodeId = null; if (promo) { discountPercent = promo.discount_percent; promocodeId = promo.id; } const safeBasePrice = basePrice || 0; const discountAmount = Math.round(safeBasePrice * discountPercent / 100); const totalPrice = safeBasePrice - discountAmount; const stmt = db.prepare(`INSERT INTO bookings (name, phone, adults, children, checkin_date, checkout_date, wishes, status, room_type, room_id, base_price, discount_percent, discount_amount, total_price, promocode_id) VALUES (?, ?, ?, ?, ?, ?, ?, 'новая', ?, ?, ?, ?, ?, ?, ?)`); stmt.run(name, phone, parseInt(adults), parseInt(children || 0), checkin, checkout, wishes || null, room || null, roomIdValue, safeBasePrice || null, discountPercent || 0, discountAmount || 0, totalPrice || null, promocodeId, function(err) { if (err) { console.error(err); return res.status(500).json({ error: 'Database error' }); } const booking = { id: this.lastID, name, phone, adults: parseInt(adults), children: parseInt(children || 0), checkin_date: checkin, checkout_date: checkout, wishes, room_type: room, room_id: roomIdValue, base_price: safeBasePrice, discount_percent: discountPercent, discount_amount: discountAmount, total_price: totalPrice, promocode_id: promocodeId }; try { require('../email').sendBookingConfirmation(booking); } catch {} try { require('../telegram').sendBookingNotification(booking); } catch {} res.status(201).json({ id: this.lastID, message: 'Booking saved', base_price: safeBasePrice, discount_percent: discountPercent, discount_amount: discountAmount, total_price: totalPrice }); }); stmt.finalize(); }); }); }); } function getBookings(req, res) { const providedKey = req.headers['x-api-key']; if (!providedKey) return res.status(401).json({ error: 'Invalid or missing API key' }); const API_KEY = process.env.HOTEL777KEY; if (providedKey !== API_KEY) return res.status(401).json({ error: 'Invalid or missing API key' }); db.all(`SELECT b.*, p.code as promocode_code FROM bookings b LEFT JOIN promocodes p ON b.promocode_id = p.id ORDER BY b.checkin_date ASC`, (err, rows) => { if (err) { console.error(err); return res.status(500).json({ error: 'Database error' }); } res.json(rows); }); } function getBookingHistory(req, res) { const bookingId = parseInt(req.params.id); db.all(`SELECT id, booking_id, user_id, user_login, field, old_value, new_value, created_at FROM booking_history WHERE booking_id = ? ORDER BY created_at DESC`, [bookingId], (err, rows) => { if (err) return res.status(500).json({ error: 'Database error' }); res.json(rows); }); } function setupRoutes(app, authenticateToken, requireAdmin) { app.post('/api/bookings', createBooking); app.get('/api/bookings', getBookings); app.get('/api/admin/bookings/:id/history', authenticateToken, getBookingHistory); } module.exports = { init, setupRoutes, validatePromocode, calculateNightPrices, checkAvailability };