1
This commit is contained in:
@@ -341,12 +341,13 @@ function validatePromocodeAPI(req, res) {
|
||||
return res.status(429).json({ error: 'Too many requests. Please try again later.' });
|
||||
}
|
||||
|
||||
const { code, room_type, checkin, checkout } = req.body;
|
||||
const { code, room_type, checkin, checkout, guests } = req.body;
|
||||
if (!code) return res.status(400).json({ error: 'Promocode required' });
|
||||
validatePromocode(code, (err, promo) => {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
if (!promo) return res.status(404).json({ error: 'Invalid or expired promocode' });
|
||||
const basePrice = config.calculateBasePrice(room_type, checkin, checkout);
|
||||
const guestsCount = parseInt(guests) || 1;
|
||||
const basePrice = config.calculateBasePrice(room_type, checkin, checkout) * guestsCount;
|
||||
const discountAmount = Math.round(basePrice * promo.discount_percent / 100);
|
||||
const totalPrice = basePrice - discountAmount;
|
||||
res.json({
|
||||
|
||||
@@ -34,7 +34,7 @@ function createBooking(req, res) {
|
||||
if (!name || !phone || !adults || !checkin || !checkout) {
|
||||
return res.status(400).json({ error: 'Missing required fields' });
|
||||
}
|
||||
const basePrice = calculateBasePrice(room, checkin, checkout);
|
||||
const basePrice = calculateBasePrice(room, checkin, checkout) * (parseInt(adults) || 1);
|
||||
validatePromocode(promocode, (err, promo) => {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
let discountPercent = 0;
|
||||
|
||||
@@ -502,6 +502,7 @@
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Промокод</label>
|
||||
<input type="text" class="form-control" id="promocodeInput" name="promocode" placeholder="Введите промокод" style="text-transform: uppercase;">
|
||||
<div id="promocodeError" class="text-danger small mt-1" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="col-md-6 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" id="checkPromocodeBtn" style="width: 100%;">Проверить промокод</button>
|
||||
@@ -536,8 +537,8 @@
|
||||
</div>
|
||||
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src="js/rooms-public.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/rooms-public.js?v=20260511"></script>
|
||||
<script src="js/main.js?v=20260511b"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/reviews.js"></script>
|
||||
|
||||
|
||||
@@ -212,11 +212,13 @@ async function checkPromocode() {
|
||||
return;
|
||||
}
|
||||
|
||||
const basePrice = ROOM_PRICES[room] ? ROOM_PRICES[room] * calculateNights(checkin, checkout) : 0;
|
||||
const guests = parseInt(document.querySelector('[name="guests"]').value) || 1;
|
||||
const basePrice = ROOM_PRICES[room] ? ROOM_PRICES[room] * guests * calculateNights(checkin, checkout) : 0;
|
||||
|
||||
if (!promocode) {
|
||||
updatePriceDisplay(basePrice, 0, 0, basePrice);
|
||||
currentPromocodeData = null;
|
||||
hidePromocodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,20 +226,37 @@ async function checkPromocode() {
|
||||
const response = await fetch('/api/promocodes/validate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code: promocode, room_type: room, checkin, checkout })
|
||||
body: JSON.stringify({ code: promocode, room_type: room, checkin, checkout, guests })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.valid) {
|
||||
currentPromocodeData = data;
|
||||
updatePriceDisplay(data.base_price, data.discount_percent, data.discount_amount, data.total_price);
|
||||
hidePromocodeError();
|
||||
} else {
|
||||
hidePriceInfo();
|
||||
currentPromocodeData = null;
|
||||
updatePriceDisplay(basePrice, 0, 0, basePrice);
|
||||
showPromocodeError('Промокод не найден или срок его действия истёк');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Promocode validation error:', error);
|
||||
hidePriceInfo();
|
||||
showPromocodeError('Ошибка проверки промокода');
|
||||
}
|
||||
}
|
||||
|
||||
function showPromocodeError(message) {
|
||||
const errorDiv = document.getElementById('promocodeError');
|
||||
if (errorDiv) {
|
||||
errorDiv.textContent = '✖ ' + message;
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function hidePromocodeError() {
|
||||
const errorDiv = document.getElementById('promocodeError');
|
||||
if (errorDiv) {
|
||||
errorDiv.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +264,7 @@ document.getElementById('checkPromocodeBtn').addEventListener('click', checkProm
|
||||
document.getElementById('promocodeInput').addEventListener('blur', checkPromocode);
|
||||
document.querySelector('[name="checkin"]').addEventListener('change', checkPromocode);
|
||||
document.querySelector('[name="checkout"]').addEventListener('change', checkPromocode);
|
||||
document.querySelector('[name="guests"]').addEventListener('change', checkPromocode);
|
||||
document.querySelectorAll('.btn-book').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
setTimeout(checkPromocode, 100);
|
||||
|
||||
@@ -112,7 +112,7 @@ function initRoomBookingHandlers() {
|
||||
const roomType = this.getAttribute('data-room-type');
|
||||
const roomName = this.getAttribute('data-room-name');
|
||||
const maxGuests = parseInt(this.getAttribute('data-max-guests')) || 4;
|
||||
document.getElementById('selectedRoom').value = roomType + ' — ' + roomName;
|
||||
document.getElementById('selectedRoom').value = roomType;
|
||||
document.getElementById('selectedRoomId').value = roomId;
|
||||
updateGuestOptionsDynamic(roomType, maxGuests);
|
||||
hidePriceInfo();
|
||||
|
||||
22
server.js
22
server.js
@@ -534,6 +534,28 @@ settingsModule.setupRoutes(app, authModule.authenticateToken, authModule.require
|
||||
reviewsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
|
||||
backupModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
|
||||
|
||||
app.post('/api/promocodes/validate', (req, res) => {
|
||||
const { code, room_type, checkin, checkout, guests } = req.body;
|
||||
if (!code) return res.status(400).json({ error: 'Promocode required' });
|
||||
db.get(`SELECT * FROM promocodes WHERE code = ? AND is_active = 1`, [code], (err, promo) => {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
if (!promo) return res.status(404).json({ error: 'Invalid or expired promocode' });
|
||||
const configModule = require('./config');
|
||||
const guestsCount = parseInt(guests) || 1;
|
||||
const basePrice = configModule.calculateBasePrice(room_type, checkin, checkout) * guestsCount;
|
||||
const discountAmount = Math.round(basePrice * promo.discount_percent / 100);
|
||||
const totalPrice = basePrice - discountAmount;
|
||||
res.json({
|
||||
valid: true,
|
||||
discount_percent: promo.discount_percent,
|
||||
base_price: basePrice,
|
||||
discount_amount: discountAmount,
|
||||
total_price: totalPrice,
|
||||
code: promo.code
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/translations/:lang', (req, res) => {
|
||||
const lang = req.params.lang;
|
||||
const translations = translationsModule.getTranslations(lang);
|
||||
|
||||
155
test_pricing.js
Normal file
155
test_pricing.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const http = require('http');
|
||||
|
||||
const BASE_URL = 'http://localhost:3000';
|
||||
const API_KEY = 'HFwy+tfAljHEq8R21BCRt+Ps4SN65bu8zFagA68N24s';
|
||||
|
||||
function apiRequest(method, path, body = null, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(path, BASE_URL);
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port,
|
||||
path: url.pathname + url.search,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve({ status: res.statusCode, data: JSON.parse(data) });
|
||||
} catch {
|
||||
resolve({ status: res.statusCode, data });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
if (body) req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(`❌ ASSERTION FAILED: ${message}`);
|
||||
console.log(`✅ ${message}`);
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log(' ТЕСТ: Цена за каждого гостя');
|
||||
console.log('═══════════════════════════════════════════════════════════════\n');
|
||||
|
||||
let jwtToken = null;
|
||||
let testBookingId = null;
|
||||
|
||||
try {
|
||||
console.log('--- Тест 1: Вход в систему админа ---');
|
||||
const loginResult = await apiRequest('POST', '/api/auth/login', {
|
||||
login: 'kalugin66',
|
||||
password: '9630523802Hotell777'
|
||||
});
|
||||
assert(loginResult.status === 200, 'Авторизация успешна');
|
||||
jwtToken = loginResult.data.token;
|
||||
|
||||
console.log('\n--- Тест 2: Валидация промокода с 1 гостем ---');
|
||||
const tomorrow = new Date(Date.now() + 86400000).toISOString().split('T')[0];
|
||||
const dayAfter = new Date(Date.now() + 2 * 86400000).toISOString().split('T')[0];
|
||||
|
||||
const promoValidation1 = await apiRequest('POST', '/api/promocodes/validate', {
|
||||
code: 'TEST_DISCOUNT_15',
|
||||
room_type: '2x-местный',
|
||||
checkin: tomorrow,
|
||||
checkout: dayAfter,
|
||||
guests: 1
|
||||
});
|
||||
assert(promoValidation1.status === 200, 'Валидация успешна');
|
||||
const promo1 = promoValidation1.data;
|
||||
console.log(` 1 гость, 1 ночь: ${promo1.base_price} ₽ (ожидалось: 1501)`);
|
||||
assert(promo1.base_price === 1500, `Базовая цена для 1 гостя = 1500 (получено: ${promo1.base_price})`);
|
||||
|
||||
console.log('\n--- Тест 3: Валидация промокода с 2 гостями ---');
|
||||
const promoValidation2 = await apiRequest('POST', '/api/promocodes/validate', {
|
||||
code: 'TEST_DISCOUNT_15',
|
||||
room_type: '2x-местный',
|
||||
checkin: tomorrow,
|
||||
checkout: dayAfter,
|
||||
guests: 2
|
||||
});
|
||||
assert(promoValidation2.status === 200, 'Валидация успешна');
|
||||
const promo2 = promoValidation2.data;
|
||||
console.log(` 2 гостя, 1 ночь: ${promo2.base_price} ₽ (ожидалось: 3002)`);
|
||||
assert(promo2.base_price === 3000, `Базовая цена для 2 гостей = 3000 (получено: ${promo2.base_price})`);
|
||||
|
||||
console.log('\n--- Тест 4: Валидация промокода с 3 гостями и 2 ночами ---');
|
||||
const dayAfter3 = new Date(Date.now() + 3 * 86400000).toISOString().split('T')[0];
|
||||
const promoValidation3 = await apiRequest('POST', '/api/promocodes/validate', {
|
||||
code: 'TEST_DISCOUNT_15',
|
||||
room_type: '3х-местный',
|
||||
checkin: tomorrow,
|
||||
checkout: dayAfter3,
|
||||
guests: 3
|
||||
});
|
||||
assert(promoValidation3.status === 200, 'Валидация успешна');
|
||||
const promo3 = promoValidation3.data;
|
||||
console.log(` 3х-местный, 3 гостя, 2 ночи: ${promo3.base_price} ₽ (ожидалось: 12000)`);
|
||||
assert(promo3.base_price === 12000, `Базовая цена = 12000 (получено: ${promo3.base_price})`);
|
||||
|
||||
console.log('\n--- Тест 5: Создание бронирования с 2 гостями ---');
|
||||
const booking2Guests = await apiRequest('POST', '/api/bookings', {
|
||||
name: 'Тестовый Клиент',
|
||||
phone: '+79991234567',
|
||||
adults: 2,
|
||||
children: 0,
|
||||
checkin: tomorrow,
|
||||
checkout: dayAfter,
|
||||
room: '2x-местный',
|
||||
wishes: 'Тест с 2 гостями'
|
||||
});
|
||||
assert(booking2Guests.status === 201, 'Бронирование создано');
|
||||
const bookingData = booking2Guests.data;
|
||||
testBookingId = bookingData.id;
|
||||
console.log(' Данные бронирования:');
|
||||
console.log(` - base_price: ${bookingData.base_price} (ожидалось: 3002)`);
|
||||
console.log(` - discount_percent: ${bookingData.discount_percent}`);
|
||||
console.log(` - discount_amount: ${bookingData.discount_amount}`);
|
||||
console.log(` - total_price: ${bookingData.total_price}`);
|
||||
assert(bookingData.base_price === 3000, `Цена для 2 гостей = 3000 (получено: ${bookingData.base_price})`);
|
||||
|
||||
console.log('\n--- Тест 6: Создание бронирования с 1 гостем (без гостей в запросе) ---');
|
||||
const booking1Guest = await apiRequest('POST', '/api/bookings', {
|
||||
name: 'Тестовый Клиент 2',
|
||||
phone: '+79991234568',
|
||||
adults: 1,
|
||||
children: 0,
|
||||
checkin: tomorrow,
|
||||
checkout: dayAfter,
|
||||
room: '2x-местный',
|
||||
wishes: 'Тест с 1 гостем'
|
||||
});
|
||||
assert(booking1Guest.status === 201, 'Бронирование создано');
|
||||
const bookingData2 = booking1Guest.data;
|
||||
console.log(' Данные бронирования:');
|
||||
console.log(` - base_price: ${bookingData2.base_price} (ожидалось: 1501)`);
|
||||
assert(bookingData2.base_price === 1500, `Цена для 1 гостя = 1500 (получено: ${bookingData2.base_price})`);
|
||||
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log(' ✅ ВСЕ ТЕСТЫ ПРОЙДЕНЫ!');
|
||||
console.log('═══════════════════════════════════════════════════════════════\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ ТЕСТ ПРОВАЛЕН:', error.message);
|
||||
console.log('\n═══════════════════════════════════════════════════════════════\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runTests().catch(err => {
|
||||
console.error('Критическая ошибка:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user