const pool = require('../db/connection'); const getReservations = async (req, res) => { try { const { status, from_date, to_date, search } = req.query; let query = ` SELECT r.*, g.first_name, g.last_name, g.phone, g.email, rm.room_number, rm.room_type FROM reservations r JOIN guests g ON g.id = r.guest_id LEFT JOIN rooms rm ON rm.id = r.room_id WHERE 1=1 `; const params = []; let paramIndex = 1; if (status && status !== 'all') { query += ` AND r.status = $${paramIndex++}`; params.push(status); } if (from_date) { query += ` AND r.check_in >= $${paramIndex++}`; params.push(from_date); } if (to_date) { query += ` AND r.check_out <= $${paramIndex++}`; params.push(to_date); } if (search) { query += ` AND (g.first_name ILIKE $${paramIndex} OR g.last_name ILIKE $${paramIndex})`; params.push(`%${search}%`); paramIndex++; } query += ' ORDER BY r.created_at DESC LIMIT 100'; const result = await pool.query(query, params); res.json({ reservations: result.rows }); } catch (error) { console.error(error); res.status(500).json({ message: 'Error al obtener reservaciones' }); } }; const createReservation = async (req, res) => { try { const { room_id, guest_id, guest, check_in, check_out, channel, total_amount, adults, children, notes } = req.body; const created_by = req.user?.user_id; // Create guest if new let finalGuestId = guest_id; if (!finalGuestId && guest) { const guestResult = await pool.query( 'INSERT INTO guests (first_name, last_name, email, phone, nationality) VALUES ($1, $2, $3, $4, $5) RETURNING id', [guest.first_name, guest.last_name, guest.email || null, guest.phone || null, guest.nationality || null] ); finalGuestId = guestResult.rows[0].id; } // Check availability const overlap = await pool.query( `SELECT id FROM reservations WHERE room_id = $1 AND status IN ('confirmed', 'checked_in') AND check_in < $3 AND check_out > $2`, [room_id, check_in, check_out] ); if (overlap.rows.length > 0) { return res.status(409).json({ message: 'La habitacion no esta disponible para esas fechas' }); } const result = await pool.query( `INSERT INTO reservations (room_id, guest_id, check_in, check_out, status, channel, total_amount, adults, children, notes, created_by) VALUES ($1, $2, $3, $4, 'pending', $5, $6, $7, $8, $9, $10) RETURNING *`, [room_id, finalGuestId, check_in, check_out, channel || 'direct', total_amount, adults || 1, children || 0, notes, created_by] ); res.status(201).json({ reservation: result.rows[0], message: 'Reservacion creada correctamente' }); } catch (error) { console.error(error); res.status(500).json({ message: 'Error al crear reservacion' }); } }; const updateReservation = async (req, res) => { try { const { id } = req.params; const { room_id, check_in, check_out, channel, total_amount, adults, children, notes } = req.body; const result = await pool.query( `UPDATE reservations SET room_id = COALESCE($1, room_id), check_in = COALESCE($2, check_in), check_out = COALESCE($3, check_out), channel = COALESCE($4, channel), total_amount = COALESCE($5, total_amount), adults = COALESCE($6, adults), children = COALESCE($7, children), notes = COALESCE($8, notes), updated_at = NOW() WHERE id = $9 RETURNING *`, [room_id, check_in, check_out, channel, total_amount, adults, children, notes, id] ); if (result.rows.length === 0) return res.status(404).json({ message: 'Reservacion no encontrada' }); res.json({ reservation: result.rows[0] }); } catch (error) { console.error(error); res.status(500).json({ message: 'Error al actualizar reservacion' }); } }; const updateReservationStatus = async (req, res) => { try { const { id } = req.params; const { status } = req.body; const userId = req.user?.user_id; const current = await pool.query('SELECT * FROM reservations WHERE id = $1', [id]); if (current.rows.length === 0) return res.status(404).json({ message: 'Reservacion no encontrada' }); const reservation = current.rows[0]; const validTransitions = { pending: ['confirmed', 'cancelled'], confirmed: ['checked_in', 'cancelled'], checked_in: ['checked_out'], }; const allowed = validTransitions[reservation.status]; if (!allowed || !allowed.includes(status)) { return res.status(400).json({ message: `No se puede cambiar de ${reservation.status} a ${status}` }); } await pool.query('UPDATE reservations SET status = $1, updated_at = NOW() WHERE id = $2', [status, id]); // Cascading side effects if (status === 'checked_in') { // Set room to occupied await pool.query("UPDATE rooms SET status = 'occupied' WHERE id = $1", [reservation.room_id]); await pool.query( 'INSERT INTO room_status_log (room_id, previous_status, new_status, changed_by) VALUES ($1, $2, $3, $4)', [reservation.room_id, 'available', 'occupied', userId] ); // Create guest stay record await pool.query( 'INSERT INTO guest_stays (guest_id, reservation_id, room_id, check_in) VALUES ($1, $2, $3, NOW())', [reservation.guest_id, reservation.id, reservation.room_id] ); } if (status === 'checked_out') { // Set room to cleaning await pool.query("UPDATE rooms SET status = 'cleaning' WHERE id = $1", [reservation.room_id]); await pool.query( 'INSERT INTO room_status_log (room_id, previous_status, new_status, changed_by) VALUES ($1, $2, $3, $4)', [reservation.room_id, 'occupied', 'cleaning', userId] ); // Create housekeeping task await pool.query( `INSERT INTO housekeeping_tasks (room_id, priority, type, status) VALUES ($1, 'high', 'checkout', 'pending')`, [reservation.room_id] ); // Close guest stay await pool.query( 'UPDATE guest_stays SET check_out = NOW(), total_charged = $1 WHERE reservation_id = $2 AND check_out IS NULL', [reservation.total_amount, reservation.id] ); } if (status === 'cancelled') { // Free room if it was occupied if (reservation.status === 'checked_in') { await pool.query("UPDATE rooms SET status = 'available' WHERE id = $1", [reservation.room_id]); } } res.json({ message: 'Estado de reservacion actualizado' }); } catch (error) { console.error(error); res.status(500).json({ message: 'Error al actualizar estado' }); } }; const checkAvailability = async (req, res) => { try { const { room_id, check_in, check_out } = req.query; const overlap = await pool.query( `SELECT id FROM reservations WHERE room_id = $1 AND status IN ('confirmed', 'checked_in') AND check_in < $3 AND check_out > $2`, [room_id, check_in, check_out] ); res.json({ available: overlap.rows.length === 0 }); } catch (error) { console.error(error); res.status(500).json({ message: 'Error al verificar disponibilidad' }); } }; module.exports = { getReservations, createReservation, updateReservation, updateReservationStatus, checkAvailability };