This commit is contained in:
2026-05-09 00:16:04 +05:00
parent 0a9a8387e4
commit 3e91ebd083
2 changed files with 89 additions and 3 deletions

View File

@@ -625,8 +625,10 @@ function renderBookings() {
return '<tr class="' + rowClass + '">' +
'<td><strong>' + esc(r.name) + '</strong></td><td>' + esc(r.phone) + '</td>' +
'<td><select class="form-select form-select-sm room-select" style="width: 120px; font-size: 0.8rem;" data-booking-id="' + r.id + '" onchange="changeRoom(' + r.id + ', this.value)">' + roomOpts + '</select></td>' +
'<td>' + r.adults + '</td><td>' + r.children + '</td>' +
'<td>' + esc(r.checkin_date) + '</td><td>' + esc(r.checkout_date) + '</td>' +
'<td><input type="number" min="0" class="form-control form-control-sm" style="width:60px; font-size:0.8rem;" value="' + r.adults + '" onchange="changeDetails(' + r.id + ', \'adults\', this.value)"></td>' +
'<td><input type="number" min="0" class="form-control form-control-sm" style="width:60px; font-size:0.8rem;" value="' + r.children + '" onchange="changeDetails(' + r.id + ', \'children\', this.value)"></td>' +
'<td><input type="date" class="form-control form-control-sm" style="width:130px; font-size:0.8rem;" value="' + esc(r.checkin_date) + '" onchange="changeDetails(' + r.id + ', \'checkin_date\', this.value)"></td>' +
'<td><input type="date" class="form-control form-control-sm" style="width:130px; font-size:0.8rem;" value="' + esc(r.checkout_date) + '" onchange="changeDetails(' + r.id + ', \'checkout_date\', this.value)"></td>' +
'<td>' + esc(r.wishes || '—') + '</td>' +
'<td>' + commentHtml + '</td>' +
'<td>' + (r.base_price || 0) + '</td>' +
@@ -883,6 +885,21 @@ function hideHistoryModal() { document.getElementById('historyModal').classList.
document.getElementById('historyModal').addEventListener('click', function(e) { if (e.target === this) hideHistoryModal(); });
document.getElementById('promocodeModal').addEventListener('click', function(e) { if (e.target === this) hidePromocodeModal(); });
async function changeDetails(id, field, value) {
try {
const body = {};
if (field === 'adults' || field === 'children') {
body[field] = parseInt(value);
} else {
body[field] = value;
}
const data = await api('/api/admin/bookings/' + id + '/details', { method: 'PATCH', body: JSON.stringify(body) });
allBookingsData = allBookingsData.map(b => b.id === id ? data.booking : b);
renderBookings(); updateBookingStats(); loadDashboard();
showToast('Данные обновлены');
} catch(err) { showToast(err.message, 'error'); }
}
checkAuth();
</script>
</body>

View File

@@ -313,7 +313,6 @@ app.get('/api/admin/bookings', authenticateToken, (req, res) => {
});
app.patch('/api/admin/bookings/:id', authenticateToken, requireAdmin, (req, res) => {
console.log('PATCH /api/admin/bookings/:id hit', req.params.id, req.body);
const bookingId = parseInt(req.params.id);
const { status } = req.body;
const validStatuses = ['новая', 'оплачена', 'зарезервирована', 'заселена', 'выехала', 'отменена'];
@@ -404,6 +403,76 @@ app.patch('/api/admin/bookings/:id/discount', authenticateToken, requireAdmin, (
});
});
app.patch('/api/admin/bookings/:id/details', authenticateToken, requireAdmin, (req, res) => {
const bookingId = parseInt(req.params.id);
const { adults, children, checkin_date, checkout_date } = req.body;
if (adults === undefined && children === undefined && checkin_date === undefined && checkout_date === undefined) {
return res.status(400).json({ error: 'No fields to update' });
}
db.get(`SELECT * FROM bookings WHERE id = ?`, [bookingId], (err, row) => {
if (err) return res.status(500).json({ error: 'Database error' });
if (!row) return res.status(404).json({ error: 'Booking not found' });
const oldValues = {
adults: row.adults,
children: row.children,
checkin_date: row.checkin_date,
checkout_date: row.checkout_date
};
let fields = [];
let values = [];
if (adults !== undefined) { fields.push('adults = ?'); values.push(adults); }
if (children !== undefined) { fields.push('children = ?'); values.push(children); }
if (checkin_date !== undefined) { fields.push('checkin_date = ?'); values.push(checkin_date); }
if (checkout_date !== undefined) { fields.push('checkout_date = ?'); values.push(checkout_date); }
if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' });
values.push(bookingId);
db.run(`UPDATE bookings SET ${fields.join(', ')} WHERE id = ?`, values, function(err) {
if (err) return res.status(500).json({ error: 'Database error' });
if (adults !== undefined && adults !== oldValues.adults) {
logHistory(bookingId, req.user.id, req.user.login, 'adults', oldValues.adults.toString(), adults.toString());
}
if (children !== undefined && children !== oldValues.children) {
logHistory(bookingId, req.user.id, req.user.login, 'children', oldValues.children.toString(), children.toString());
}
if (checkin_date !== undefined && checkin_date !== oldValues.checkin_date) {
logHistory(bookingId, req.user.id, req.user.login, 'checkin_date', oldValues.checkin_date, checkin_date);
}
if (checkout_date !== undefined && checkout_date !== oldValues.checkout_date) {
logHistory(bookingId, req.user.id, req.user.login, 'checkout_date', oldValues.checkout_date, checkout_date);
}
const newAdults = adults !== undefined ? adults : oldValues.adults;
const newChildren = children !== undefined ? children : oldValues.children;
const newCheckin = checkin_date !== undefined ? checkin_date : oldValues.checkin_date;
const newCheckout = checkout_date !== undefined ? checkout_date : oldValues.checkout_date;
const totalGuests = newAdults + newChildren;
const roomType = row.room_type;
if (!roomType) {
return finishUpdate();
}
db.get(`SELECT price_per_guest FROM rooms WHERE type = ? AND is_active = 1`, [roomType], (err, roomData) => {
if (err) return res.status(500).json({ error: 'Database error' });
const pricePerGuest = roomData ? roomData.price_per_guest : (ROOM_PRICES[roomType] || 0);
const nights = calculateNights(newCheckin, newCheckout);
const basePrice = pricePerGuest * totalGuests * nights;
const discountPercent = row.discount_percent || 0;
const discountAmount = Math.round(basePrice * discountPercent / 100);
const totalPrice = basePrice - discountAmount;
db.run(`UPDATE bookings SET base_price = ?, discount_amount = ?, total_price = ? WHERE id = ?`,
[basePrice, discountAmount, totalPrice, bookingId], (err) => {
if (err) return res.status(500).json({ error: 'Database error' });
finishUpdate();
});
});
function finishUpdate() {
db.get(`SELECT b.*, p.code as promocode_code FROM bookings b LEFT JOIN promocodes p ON b.promocode_id = p.id WHERE b.id = ?`, [bookingId], (err, row) => {
if (err) return res.status(500).json({ error: 'Database error' });
res.json({ message: 'Booking details updated', booking: row });
});
}
});
});
});
app.get('/api/admin/bookings/:id/history', authenticateToken, (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