Fix Product filtering and add kit disassembly functionality

Fixed:
- Replace is_active with status='active' for Product filtering in IncomingModelForm
- Product model uses status field instead of is_active

Added:
- Showcase field to ProductKit for tracking showcase placement
- product_kit field to Reservation for tracking kit-specific reservations
- Disassemble button in POS terminal for showcase kits
- API endpoint for kit disassembly (release reservations, mark discontinued)
- Improved reservation filtering when dismantling specific kits

Changes:
- ShowcaseManager now links reservations to specific kit instances
- POS terminal modal shows disassemble button in edit mode
- Kit disassembly properly updates stock aggregates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-20 23:03:47 +03:00
parent d3060176c0
commit ff0756498c
11 changed files with 221 additions and 14 deletions

View File

@@ -894,7 +894,10 @@ async function openEditKitModal(kitId) {
// Меняем заголовок и кнопку
document.getElementById('createTempKitModalLabel').textContent = 'Редактирование витринного букета';
document.getElementById('confirmCreateTempKit').textContent = 'Сохранить изменения';
// Показываем кнопку "Разобрать" в режиме редактирования
document.getElementById('disassembleKitBtn').style.display = 'block';
// Открываем модальное окно
const modal = new bootstrap.Modal(document.getElementById('createTempKitModal'));
modal.show();
@@ -1234,6 +1237,58 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
}
};
// Обработчик кнопки "Разобрать букет"
document.getElementById('disassembleKitBtn').addEventListener('click', async () => {
if (!isEditMode || !editingKitId) {
alert('Ошибка: режим редактирования не активен');
return;
}
// Запрос подтверждения
const confirmed = confirm(
'Вы уверены?\n\n' +
'Букет будет разобран:\n' +
'• Все резервы компонентов будут освобождены\n' +
'• Комплект будет помечен как "Снят"\n\n' +
'Это действие нельзя отменить!'
);
if (!confirmed) {
return;
}
try {
const response = await fetch(`/pos/api/product-kits/${editingKitId}/disassemble/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
});
const data = await response.json();
if (data.success) {
alert(`${data.message}\n\nОсвобождено резервов: ${data.released_count}`);
// Закрываем модальное окно
const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal'));
modal.hide();
// Обновляем витринные комплекты
isShowcaseView = true;
currentCategoryId = null;
await refreshShowcaseKits();
renderCategories();
renderProducts();
} else {
alert(`❌ Ошибка: ${data.error}`);
}
} catch (error) {
console.error('Error disassembling kit:', error);
alert('Произошла ошибка при разборе букета');
}
});
// Вспомогательная функция для получения CSRF токена
function getCookie(name) {
let cookieValue = null;
@@ -1259,10 +1314,13 @@ document.getElementById('createTempKitModal').addEventListener('hidden.bs.modal'
// Сбрасываем режим редактирования
isEditMode = false;
editingKitId = null;
// Восстанавливаем заголовок и текст кнопки
document.getElementById('createTempKitModalLabel').textContent = 'Создать витринный букет из корзины';
document.getElementById('confirmCreateTempKit').innerHTML = '<i class="bi bi-check-circle"></i> Создать и зарезервировать';
// Скрываем кнопку "Разобрать"
document.getElementById('disassembleKitBtn').style.display = 'none';
}
});