Реализована возможность редактирования состава витринного комплекта с поддержкой отрицательных резервов
- Добавлены методы reserve_product_to_showcase и release_showcase_reservation в ShowcaseManager - Методы работают с резервами для всех активных экземпляров витринного комплекта - НЕ блокируют создание резерва при нехватке товара, возвращают информацию о дефиците (overdraft) - Обновлён API endpoint update_product_kit для корректировки резервов при изменении состава - Добавлено визуальное предупреждение на фронте о нехватке товара на складе - В модалке редактирования комплекта добавлены контролы для изменения количества товаров (+/-, поле ввода, удаление) - Автоматический пересчёт цен при изменении состава - Очистка корзины POS после успешного создания витринного комплекта
This commit is contained in:
@@ -1440,22 +1440,92 @@ function renderTempKitItems() {
|
||||
if (item.type !== 'product') return;
|
||||
|
||||
const itemDiv = document.createElement('div');
|
||||
itemDiv.className = 'd-flex justify-content-between align-items-center mb-1 pb-1 border-bottom';
|
||||
itemDiv.innerHTML = `
|
||||
<div>
|
||||
<strong class="small">${item.name}</strong>
|
||||
<br>
|
||||
<small class="text-muted">${item.qty} шт × ${formatMoney(item.price)} руб.</small>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<strong class="small">${formatMoney(item.qty * item.price)} руб.</strong>
|
||||
</div>
|
||||
itemDiv.className = 'd-flex justify-content-between align-items-center mb-2 pb-2 border-bottom';
|
||||
|
||||
// Левая часть: название и цена
|
||||
const leftDiv = document.createElement('div');
|
||||
leftDiv.className = 'flex-grow-1';
|
||||
leftDiv.innerHTML = `
|
||||
<strong class="small">${item.name}</strong>
|
||||
<br>
|
||||
<small class="text-muted">${formatMoney(item.price)} руб. / шт.</small>
|
||||
`;
|
||||
|
||||
// Правая часть: контролы количества и удаление
|
||||
const rightDiv = document.createElement('div');
|
||||
rightDiv.className = 'd-flex align-items-center gap-2';
|
||||
|
||||
// Кнопка минус
|
||||
const minusBtn = document.createElement('button');
|
||||
minusBtn.className = 'btn btn-sm btn-outline-secondary';
|
||||
minusBtn.innerHTML = '<i class="bi bi-dash"></i>';
|
||||
minusBtn.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
if (item.qty > 1) {
|
||||
item.qty--;
|
||||
} else {
|
||||
tempCart.delete(cartKey);
|
||||
}
|
||||
renderTempKitItems();
|
||||
};
|
||||
|
||||
// Поле количества
|
||||
const qtyInput = document.createElement('input');
|
||||
qtyInput.type = 'number';
|
||||
qtyInput.className = 'form-control form-control-sm text-center';
|
||||
qtyInput.style.width = '60px';
|
||||
qtyInput.value = item.qty;
|
||||
qtyInput.min = 1;
|
||||
qtyInput.onchange = (e) => {
|
||||
const newQty = parseInt(e.target.value) || 1;
|
||||
item.qty = Math.max(1, newQty);
|
||||
renderTempKitItems();
|
||||
};
|
||||
|
||||
// Кнопка плюс
|
||||
const plusBtn = document.createElement('button');
|
||||
plusBtn.className = 'btn btn-sm btn-outline-secondary';
|
||||
plusBtn.innerHTML = '<i class="bi bi-plus"></i>';
|
||||
plusBtn.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
item.qty++;
|
||||
renderTempKitItems();
|
||||
};
|
||||
|
||||
// Сумма за товар
|
||||
const totalDiv = document.createElement('div');
|
||||
totalDiv.className = 'text-end ms-2';
|
||||
totalDiv.style.minWidth = '80px';
|
||||
totalDiv.innerHTML = `<strong class="small">${formatMoney(item.qty * item.price)} руб.</strong>`;
|
||||
|
||||
// Кнопка удаления
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'btn btn-sm btn-outline-danger';
|
||||
deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
|
||||
deleteBtn.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
tempCart.delete(cartKey);
|
||||
renderTempKitItems();
|
||||
};
|
||||
|
||||
rightDiv.appendChild(minusBtn);
|
||||
rightDiv.appendChild(qtyInput);
|
||||
rightDiv.appendChild(plusBtn);
|
||||
rightDiv.appendChild(totalDiv);
|
||||
rightDiv.appendChild(deleteBtn);
|
||||
|
||||
itemDiv.appendChild(leftDiv);
|
||||
itemDiv.appendChild(rightDiv);
|
||||
container.appendChild(itemDiv);
|
||||
|
||||
estimatedTotal += item.qty * item.price;
|
||||
});
|
||||
|
||||
// Если корзина пуста
|
||||
if (tempCart.size === 0) {
|
||||
container.innerHTML = '<p class="text-muted text-center py-3"><i class="bi bi-inbox"></i> Нет товаров</p>';
|
||||
}
|
||||
|
||||
// Обновляем все расчеты цен
|
||||
updatePriceCalculations(estimatedTotal);
|
||||
}
|
||||
@@ -1657,7 +1727,8 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
// Успех!
|
||||
const createdCount = data.available_count || 1;
|
||||
const qtyInfo = createdCount > 1 ? `\nСоздано экземпляров: ${createdCount}` : '';
|
||||
const successMessage = isEditMode
|
||||
|
||||
let successMessage = isEditMode
|
||||
? `✅ ${data.message}\n\nКомплект: ${data.kit_name}\nЦена: ${data.kit_price} руб.`
|
||||
: `✅ ${data.message}
|
||||
|
||||
@@ -1665,6 +1736,15 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
Цена: ${data.kit_price} руб.${qtyInfo}
|
||||
Зарезервировано компонентов: ${data.reservations_count}`;
|
||||
|
||||
// Если есть предупреждение о нехватке товара - добавляем его
|
||||
if (data.stock_warning && data.stock_warnings && data.stock_warnings.length > 0) {
|
||||
successMessage += '\n\n⚠️ ВНИМАНИЕ: Нехватка товара на складе!\n';
|
||||
data.stock_warnings.forEach(warning => {
|
||||
successMessage += `\n• ${warning.product_name}: не хватает ${warning.overdraft} ед.`;
|
||||
});
|
||||
successMessage += '\n\nПроверьте остатки и пополните склад.';
|
||||
}
|
||||
|
||||
alert(successMessage);
|
||||
|
||||
// Очищаем tempCart (изолированное состояние модалки)
|
||||
@@ -1682,6 +1762,9 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
document.getElementById('salePriceBlock').style.display = 'none';
|
||||
document.getElementById('showcaseKitQuantity').value = '1'; // Сброс количества
|
||||
|
||||
// Запоминаем, был ли режим редактирования до сброса
|
||||
const wasEditMode = isEditMode;
|
||||
|
||||
// Сбрасываем режим редактирования
|
||||
isEditMode = false;
|
||||
editingKitId = null;
|
||||
@@ -1690,6 +1773,12 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal'));
|
||||
modal.hide();
|
||||
|
||||
// Если это было СОЗДАНИЕ витринного комплекта из корзины,
|
||||
// очищаем основную корзину POS
|
||||
if (!wasEditMode) {
|
||||
await clearCart();
|
||||
}
|
||||
|
||||
// Обновляем витринные комплекты и переключаемся на вид витрины
|
||||
isShowcaseView = true;
|
||||
currentCategoryId = null;
|
||||
|
||||
Reference in New Issue
Block a user