feat: динамическая загрузка витринных комплектов в POS
- Добавлен API endpoint GET /pos/api/showcase-kits/ для получения актуальных витринных букетов - Изменена переменная SHOWCASE_KITS на изменяемую showcaseKits - Добавлена функция refreshShowcaseKits() для обновления данных с сервера - Кнопка ВИТРИНА теперь загружает свежие данные перед отображением - После создания временного букета автоматически обновляется список и переключается вид на витрину - Исправлена проблема с отображением только что созданных витринных букетов
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
const CATEGORIES = JSON.parse(document.getElementById('categoriesData').textContent);
|
||||
const ITEMS = JSON.parse(document.getElementById('itemsData').textContent); // Единый массив товаров и комплектов
|
||||
const SHOWCASE_KITS = JSON.parse(document.getElementById('showcaseKitsData').textContent); // Витринные комплекты
|
||||
let showcaseKits = JSON.parse(document.getElementById('showcaseKitsData').textContent); // Витринные комплекты (изменяемый)
|
||||
|
||||
let currentCategoryId = null;
|
||||
let isShowcaseView = false; // Флаг режима просмотра витринных букетов
|
||||
@@ -23,9 +23,10 @@ function renderCategories() {
|
||||
showcaseCard.className = 'card category-card showcase-card' + (isShowcaseView ? ' active' : '');
|
||||
showcaseCard.style.backgroundColor = '#fff3cd';
|
||||
showcaseCard.style.borderColor = '#ffc107';
|
||||
showcaseCard.onclick = () => {
|
||||
showcaseCard.onclick = async () => {
|
||||
isShowcaseView = true;
|
||||
currentCategoryId = null;
|
||||
await refreshShowcaseKits(); // Загружаем свежие данные
|
||||
renderCategories();
|
||||
renderProducts();
|
||||
};
|
||||
@@ -97,7 +98,7 @@ function renderProducts() {
|
||||
|
||||
// Если выбран режим витрины - показываем витринные комплекты
|
||||
if (isShowcaseView) {
|
||||
filtered = SHOWCASE_KITS;
|
||||
filtered = showcaseKits; // Используем изменяемую переменную
|
||||
} else {
|
||||
// Обычный режим - показываем товары и комплекты
|
||||
filtered = currentCategoryId
|
||||
@@ -334,6 +335,22 @@ async function openCreateTempKitModal() {
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Обновление списка витринных комплектов
|
||||
async function refreshShowcaseKits() {
|
||||
try {
|
||||
const response = await fetch('/pos/api/showcase-kits/');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showcaseKits = data.items;
|
||||
} else {
|
||||
console.error('Failed to refresh showcase kits:', data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error refreshing showcase kits:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Загрузка списка витрин
|
||||
async function loadShowcases() {
|
||||
try {
|
||||
@@ -344,12 +361,24 @@ async function loadShowcases() {
|
||||
select.innerHTML = '<option value="">Выберите витрину...</option>';
|
||||
|
||||
if (data.success && data.showcases.length > 0) {
|
||||
let defaultShowcaseId = null;
|
||||
|
||||
data.showcases.forEach(showcase => {
|
||||
const option = document.createElement('option');
|
||||
option.value = showcase.id;
|
||||
option.textContent = `${showcase.name} (${showcase.warehouse_name})`;
|
||||
select.appendChild(option);
|
||||
|
||||
// Запоминаем витрину склада по умолчанию
|
||||
if (showcase.is_default_warehouse) {
|
||||
defaultShowcaseId = showcase.id;
|
||||
}
|
||||
});
|
||||
|
||||
// Автовыбор витрины склада по умолчанию
|
||||
if (defaultShowcaseId) {
|
||||
select.value = defaultShowcaseId;
|
||||
}
|
||||
} else {
|
||||
select.innerHTML = '<option value="">Нет доступных витрин</option>';
|
||||
}
|
||||
@@ -371,15 +400,15 @@ function renderTempKitItems() {
|
||||
if (item.type !== 'product') return;
|
||||
|
||||
const itemDiv = document.createElement('div');
|
||||
itemDiv.className = 'd-flex justify-content-between align-items-center mb-2 pb-2 border-bottom';
|
||||
itemDiv.className = 'd-flex justify-content-between align-items-center mb-1 pb-1 border-bottom';
|
||||
itemDiv.innerHTML = `
|
||||
<div>
|
||||
<strong>${item.name}</strong>
|
||||
<strong class="small">${item.name}</strong>
|
||||
<br>
|
||||
<small class="text-muted">${item.qty} шт × ${formatMoney(item.price)} руб.</small>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<strong>${formatMoney(item.qty * item.price)} руб.</strong>
|
||||
<strong class="small">${formatMoney(item.qty * item.price)} руб.</strong>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(itemDiv);
|
||||
@@ -387,13 +416,120 @@ function renderTempKitItems() {
|
||||
estimatedTotal += item.qty * item.price;
|
||||
});
|
||||
|
||||
document.getElementById('tempKitEstimatedPrice').textContent = formatMoney(estimatedTotal);
|
||||
// Обновляем все расчеты цен
|
||||
updatePriceCalculations(estimatedTotal);
|
||||
}
|
||||
|
||||
// Расчет и обновление всех цен
|
||||
function updatePriceCalculations(basePrice = null) {
|
||||
// Если basePrice не передан, пересчитываем из корзины
|
||||
if (basePrice === null) {
|
||||
basePrice = 0;
|
||||
cart.forEach((item, cartKey) => {
|
||||
if (item.type === 'product') {
|
||||
basePrice += item.qty * item.price;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Базовая цена
|
||||
document.getElementById('tempKitBasePrice').textContent = formatMoney(basePrice) + ' руб.';
|
||||
|
||||
// Корректировка
|
||||
const adjustmentType = document.getElementById('priceAdjustmentType').value;
|
||||
const adjustmentValue = parseFloat(document.getElementById('priceAdjustmentValue').value) || 0;
|
||||
|
||||
let calculatedPrice = basePrice;
|
||||
if (adjustmentType !== 'none' && adjustmentValue > 0) {
|
||||
if (adjustmentType === 'increase_percent') {
|
||||
calculatedPrice = basePrice + (basePrice * adjustmentValue / 100);
|
||||
} else if (adjustmentType === 'increase_amount') {
|
||||
calculatedPrice = basePrice + adjustmentValue;
|
||||
} else if (adjustmentType === 'decrease_percent') {
|
||||
calculatedPrice = Math.max(0, basePrice - (basePrice * adjustmentValue / 100));
|
||||
} else if (adjustmentType === 'decrease_amount') {
|
||||
calculatedPrice = Math.max(0, basePrice - adjustmentValue);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('tempKitCalculatedPrice').textContent = formatMoney(calculatedPrice) + ' руб.';
|
||||
|
||||
// Финальная цена (с учетом sale_price если задана)
|
||||
const useSalePrice = document.getElementById('useSalePrice').checked;
|
||||
const salePrice = parseFloat(document.getElementById('salePrice').value) || 0;
|
||||
|
||||
let finalPrice = calculatedPrice;
|
||||
if (useSalePrice && salePrice > 0) {
|
||||
finalPrice = salePrice;
|
||||
}
|
||||
|
||||
document.getElementById('tempKitFinalPrice').textContent = formatMoney(finalPrice);
|
||||
}
|
||||
|
||||
// Обработчики для полей цены
|
||||
document.getElementById('priceAdjustmentType').addEventListener('change', function() {
|
||||
const adjustmentBlock = document.getElementById('adjustmentValueBlock');
|
||||
if (this.value === 'none') {
|
||||
adjustmentBlock.style.display = 'none';
|
||||
document.getElementById('priceAdjustmentValue').value = '0';
|
||||
} else {
|
||||
adjustmentBlock.style.display = 'block';
|
||||
}
|
||||
updatePriceCalculations();
|
||||
});
|
||||
|
||||
document.getElementById('priceAdjustmentValue').addEventListener('input', function() {
|
||||
updatePriceCalculations();
|
||||
});
|
||||
|
||||
document.getElementById('useSalePrice').addEventListener('change', function() {
|
||||
const salePriceBlock = document.getElementById('salePriceBlock');
|
||||
if (this.checked) {
|
||||
salePriceBlock.style.display = 'block';
|
||||
} else {
|
||||
salePriceBlock.style.display = 'none';
|
||||
document.getElementById('salePrice').value = '';
|
||||
}
|
||||
updatePriceCalculations();
|
||||
});
|
||||
|
||||
document.getElementById('salePrice').addEventListener('input', function() {
|
||||
updatePriceCalculations();
|
||||
});
|
||||
|
||||
// Обработчик загрузки фото
|
||||
document.getElementById('tempKitPhoto').addEventListener('change', function(e) {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('Пожалуйста, выберите файл изображения');
|
||||
this.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Превью
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
document.getElementById('photoPreviewImg').src = event.target.result;
|
||||
document.getElementById('photoPreview').style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Удаление фото
|
||||
document.getElementById('removePhoto').addEventListener('click', function() {
|
||||
document.getElementById('tempKitPhoto').value = '';
|
||||
document.getElementById('photoPreview').style.display = 'none';
|
||||
document.getElementById('photoPreviewImg').src = '';
|
||||
});
|
||||
|
||||
// Подтверждение создания временного комплекта
|
||||
document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
const kitName = document.getElementById('tempKitName').value.trim();
|
||||
const showcaseId = document.getElementById('showcaseSelect').value;
|
||||
const description = document.getElementById('tempKitDescription').value.trim();
|
||||
const photoFile = document.getElementById('tempKitPhoto').files[0];
|
||||
|
||||
// Валидация
|
||||
if (!kitName) {
|
||||
@@ -422,6 +558,27 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Получаем данные о ценах
|
||||
const priceAdjustmentType = document.getElementById('priceAdjustmentType').value;
|
||||
const priceAdjustmentValue = parseFloat(document.getElementById('priceAdjustmentValue').value) || 0;
|
||||
const useSalePrice = document.getElementById('useSalePrice').checked;
|
||||
const salePrice = useSalePrice ? (parseFloat(document.getElementById('salePrice').value) || 0) : 0;
|
||||
|
||||
// Формируем FormData для отправки с файлом
|
||||
const formData = new FormData();
|
||||
formData.append('kit_name', kitName);
|
||||
formData.append('showcase_id', showcaseId);
|
||||
formData.append('description', description);
|
||||
formData.append('items', JSON.stringify(items));
|
||||
formData.append('price_adjustment_type', priceAdjustmentType);
|
||||
formData.append('price_adjustment_value', priceAdjustmentValue);
|
||||
if (useSalePrice && salePrice > 0) {
|
||||
formData.append('sale_price', salePrice);
|
||||
}
|
||||
if (photoFile) {
|
||||
formData.append('photo', photoFile);
|
||||
}
|
||||
|
||||
// Отправляем запрос на сервер
|
||||
const confirmBtn = document.getElementById('confirmCreateTempKit');
|
||||
confirmBtn.disabled = true;
|
||||
@@ -431,16 +588,10 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
const response = await fetch('/pos/api/create-temp-kit/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
// Не указываем Content-Type - браузер сам установит multipart/form-data
|
||||
},
|
||||
body: JSON.stringify({
|
||||
kit_name: kitName,
|
||||
showcase_id: parseInt(showcaseId),
|
||||
items: items,
|
||||
price_adjustment_type: 'none',
|
||||
price_adjustment_value: 0
|
||||
})
|
||||
body: formData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
@@ -456,9 +607,27 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
// Очищаем корзину
|
||||
clearCart();
|
||||
|
||||
// Сбрасываем поля формы
|
||||
document.getElementById('tempKitDescription').value = '';
|
||||
document.getElementById('tempKitPhoto').value = '';
|
||||
document.getElementById('photoPreview').style.display = 'none';
|
||||
document.getElementById('priceAdjustmentType').value = 'none';
|
||||
document.getElementById('priceAdjustmentValue').value = '0';
|
||||
document.getElementById('adjustmentValueBlock').style.display = 'none';
|
||||
document.getElementById('useSalePrice').checked = false;
|
||||
document.getElementById('salePrice').value = '';
|
||||
document.getElementById('salePriceBlock').style.display = 'none';
|
||||
|
||||
// Закрываем модальное окно
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal'));
|
||||
modal.hide();
|
||||
|
||||
// Обновляем витринные комплекты и переключаемся на вид витрины
|
||||
isShowcaseView = true;
|
||||
currentCategoryId = null;
|
||||
await refreshShowcaseKits();
|
||||
renderCategories();
|
||||
renderProducts();
|
||||
} else {
|
||||
alert(`Ошибка: ${data.error}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user