хз
This commit is contained in:
@@ -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>
|
||||
|
||||
71
server.js
71
server.js
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user