feat: добавлено редактирование витринных комплектов и изолированное состояние tempCart
- Добавлены API endpoints для получения и обновления витринных комплектов - GET /pos/api/product-kits/<id>/ - получение деталей комплекта - POST /pos/api/product-kits/<id>/update/ - обновление комплекта - Реализовано редактирование комплектов из POS интерфейса - Кнопка редактирования (карандаш) на карточках витринных букетов - Модальное окно предзаполняется данными комплекта - Поддержка изменения состава, цен, описания и фото - Умное управление резервами при изменении состава - Введено изолированное состояние tempCart для модального окна - Основная корзина (cart) больше не затрагивается при редактировании - tempCart используется для создания и редактирования комплектов - Автоочистка tempCart при закрытии модального окна - Устранён побочный эффект загрузки состава комплекта в основную корзину
This commit is contained in:
@@ -8,6 +8,13 @@ let currentCategoryId = null;
|
||||
let isShowcaseView = false; // Флаг режима просмотра витринных букетов
|
||||
const cart = new Map(); // "type-id" -> {id, name, price, qty, type}
|
||||
|
||||
// Переменные для режима редактирования
|
||||
let isEditMode = false;
|
||||
let editingKitId = null;
|
||||
|
||||
// Временная корзина для модального окна создания/редактирования комплекта
|
||||
const tempCart = new Map(); // Изолированное состояние для модалки
|
||||
|
||||
function formatMoney(v) {
|
||||
return (Number(v)).toFixed(2);
|
||||
}
|
||||
@@ -116,8 +123,25 @@ function renderProducts() {
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card product-card';
|
||||
card.style.position = 'relative';
|
||||
card.onclick = () => addToCart(item);
|
||||
|
||||
// Если это витринный комплект - добавляем кнопку редактирования
|
||||
if (item.type === 'showcase_kit') {
|
||||
const editBtn = document.createElement('button');
|
||||
editBtn.className = 'btn btn-sm btn-outline-primary';
|
||||
editBtn.style.position = 'absolute';
|
||||
editBtn.style.top = '5px';
|
||||
editBtn.style.right = '5px';
|
||||
editBtn.style.zIndex = '10';
|
||||
editBtn.innerHTML = '<i class="bi bi-pencil"></i>';
|
||||
editBtn.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
openEditKitModal(item.id);
|
||||
};
|
||||
card.appendChild(editBtn);
|
||||
}
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'card-body';
|
||||
|
||||
@@ -319,6 +343,12 @@ async function openCreateTempKitModal() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Копируем содержимое cart в tempCart (изолированное состояние модалки)
|
||||
tempCart.clear();
|
||||
cart.forEach((item, key) => {
|
||||
tempCart.set(key, {...item}); // Глубокая копия объекта
|
||||
});
|
||||
|
||||
// Генерируем название по умолчанию
|
||||
const now = new Date();
|
||||
const defaultName = `Витрина — ${now.toLocaleDateString('ru-RU')} ${now.toLocaleTimeString('ru-RU', {hour: '2-digit', minute: '2-digit'})}`;
|
||||
@@ -327,7 +357,7 @@ async function openCreateTempKitModal() {
|
||||
// Загружаем список витрин
|
||||
await loadShowcases();
|
||||
|
||||
// Заполняем список товаров из корзины
|
||||
// Заполняем список товаров из tempCart
|
||||
renderTempKitItems();
|
||||
|
||||
// Открываем модальное окно
|
||||
@@ -335,6 +365,87 @@ async function openCreateTempKitModal() {
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Открытие модального окна для редактирования комплекта
|
||||
async function openEditKitModal(kitId) {
|
||||
try {
|
||||
// Загружаем данные комплекта
|
||||
const response = await fetch(`/pos/api/product-kits/${kitId}/`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
alert(`Ошибка: ${data.error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const kit = data.kit;
|
||||
|
||||
// Устанавливаем режим редактирования
|
||||
isEditMode = true;
|
||||
editingKitId = kitId;
|
||||
|
||||
// Загружаем список витрин
|
||||
await loadShowcases();
|
||||
|
||||
// Очищаем tempCart и заполняем составом комплекта
|
||||
tempCart.clear();
|
||||
kit.items.forEach(item => {
|
||||
const cartKey = `product-${item.product_id}`;
|
||||
tempCart.set(cartKey, {
|
||||
id: item.product_id,
|
||||
name: item.name,
|
||||
price: Number(item.price),
|
||||
qty: Number(item.qty),
|
||||
type: 'product'
|
||||
});
|
||||
});
|
||||
renderTempKitItems(); // Отображаем товары в модальном окне
|
||||
|
||||
// Заполняем поля формы
|
||||
document.getElementById('tempKitName').value = kit.name;
|
||||
document.getElementById('tempKitDescription').value = kit.description;
|
||||
document.getElementById('priceAdjustmentType').value = kit.price_adjustment_type;
|
||||
document.getElementById('priceAdjustmentValue').value = kit.price_adjustment_value;
|
||||
|
||||
if (kit.sale_price) {
|
||||
document.getElementById('useSalePrice').checked = true;
|
||||
document.getElementById('salePrice').value = kit.sale_price;
|
||||
document.getElementById('salePriceBlock').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('useSalePrice').checked = false;
|
||||
document.getElementById('salePrice').value = '';
|
||||
document.getElementById('salePriceBlock').style.display = 'none';
|
||||
}
|
||||
|
||||
// Выбираем витрину
|
||||
if (kit.showcase_id) {
|
||||
document.getElementById('showcaseSelect').value = kit.showcase_id;
|
||||
}
|
||||
|
||||
// Отображаем фото, если есть
|
||||
if (kit.photo_url) {
|
||||
document.getElementById('photoPreviewImg').src = kit.photo_url;
|
||||
document.getElementById('photoPreview').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('photoPreview').style.display = 'none';
|
||||
}
|
||||
|
||||
// Обновляем цены
|
||||
updatePriceCalculations();
|
||||
|
||||
// Меняем заголовок и кнопку
|
||||
document.getElementById('createTempKitModalLabel').textContent = 'Редактирование витринного букета';
|
||||
document.getElementById('confirmCreateTempKit').textContent = 'Сохранить изменения';
|
||||
|
||||
// Открываем модальное окно
|
||||
const modal = new bootstrap.Modal(document.getElementById('createTempKitModal'));
|
||||
modal.show();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading kit for edit:', error);
|
||||
alert('Ошибка при загрузке комплекта');
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление списка витринных комплектов
|
||||
async function refreshShowcaseKits() {
|
||||
try {
|
||||
@@ -388,14 +499,14 @@ async function loadShowcases() {
|
||||
}
|
||||
}
|
||||
|
||||
// Отображение товаров из корзины в модальном окне
|
||||
// Отображение товаров из tempCart в модальном окне
|
||||
function renderTempKitItems() {
|
||||
const container = document.getElementById('tempKitItemsList');
|
||||
container.innerHTML = '';
|
||||
|
||||
let estimatedTotal = 0;
|
||||
|
||||
cart.forEach((item, cartKey) => {
|
||||
tempCart.forEach((item, cartKey) => {
|
||||
// Только товары (не комплекты)
|
||||
if (item.type !== 'product') return;
|
||||
|
||||
@@ -422,10 +533,10 @@ function renderTempKitItems() {
|
||||
|
||||
// Расчет и обновление всех цен
|
||||
function updatePriceCalculations(basePrice = null) {
|
||||
// Если basePrice не передан, пересчитываем из корзины
|
||||
// Если basePrice не передан, пересчитываем из tempCart
|
||||
if (basePrice === null) {
|
||||
basePrice = 0;
|
||||
cart.forEach((item, cartKey) => {
|
||||
tempCart.forEach((item, cartKey) => {
|
||||
if (item.type === 'product') {
|
||||
basePrice += item.qty * item.price;
|
||||
}
|
||||
@@ -524,7 +635,7 @@ document.getElementById('removePhoto').addEventListener('click', function() {
|
||||
document.getElementById('photoPreviewImg').src = '';
|
||||
});
|
||||
|
||||
// Подтверждение создания временного комплекта
|
||||
// Подтверждение создания/редактирования временного комплекта
|
||||
document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
const kitName = document.getElementById('tempKitName').value.trim();
|
||||
const showcaseId = document.getElementById('showcaseSelect').value;
|
||||
@@ -537,14 +648,14 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!showcaseId) {
|
||||
if (!showcaseId && !isEditMode) {
|
||||
alert('Выберите витрину');
|
||||
return;
|
||||
}
|
||||
|
||||
// Собираем товары из корзины
|
||||
// Собираем товары из tempCart (изолированное состояние модалки)
|
||||
const items = [];
|
||||
cart.forEach((item, cartKey) => {
|
||||
tempCart.forEach((item, cartKey) => {
|
||||
if (item.type === 'product') {
|
||||
items.push({
|
||||
product_id: item.id,
|
||||
@@ -567,7 +678,9 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
// Формируем FormData для отправки с файлом
|
||||
const formData = new FormData();
|
||||
formData.append('kit_name', kitName);
|
||||
formData.append('showcase_id', showcaseId);
|
||||
if (showcaseId) {
|
||||
formData.append('showcase_id', showcaseId);
|
||||
}
|
||||
formData.append('description', description);
|
||||
formData.append('items', JSON.stringify(items));
|
||||
formData.append('price_adjustment_type', priceAdjustmentType);
|
||||
@@ -575,17 +688,28 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
if (useSalePrice && salePrice > 0) {
|
||||
formData.append('sale_price', salePrice);
|
||||
}
|
||||
|
||||
// Фото: для редактирования проверяем, удалено ли оно
|
||||
if (photoFile) {
|
||||
formData.append('photo', photoFile);
|
||||
} else if (isEditMode && document.getElementById('photoPreview').style.display === 'none') {
|
||||
// Если фото было удалено
|
||||
formData.append('remove_photo', '1');
|
||||
}
|
||||
|
||||
// Отправляем запрос на сервер
|
||||
const confirmBtn = document.getElementById('confirmCreateTempKit');
|
||||
confirmBtn.disabled = true;
|
||||
confirmBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Создание...';
|
||||
|
||||
const url = isEditMode
|
||||
? `/pos/api/product-kits/${editingKitId}/update/`
|
||||
: '/pos/api/create-temp-kit/';
|
||||
|
||||
const actionText = isEditMode ? 'Сохранение...' : 'Создание...';
|
||||
confirmBtn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${actionText}`;
|
||||
|
||||
try {
|
||||
const response = await fetch('/pos/api/create-temp-kit/', {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
@@ -598,14 +722,18 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
|
||||
if (data.success) {
|
||||
// Успех!
|
||||
alert(`✅ ${data.message}
|
||||
const successMessage = isEditMode
|
||||
? `✅ ${data.message}\n\nКомплект: ${data.kit_name}\nЦена: ${data.kit_price} руб.`
|
||||
: `✅ ${data.message}
|
||||
|
||||
Комплект: ${data.kit_name}
|
||||
Цена: ${data.kit_price} руб.
|
||||
Зарезервировано компонентов: ${data.reservations_count}`);
|
||||
Зарезервировано компонентов: ${data.reservations_count}`;
|
||||
|
||||
// Очищаем корзину
|
||||
clearCart();
|
||||
alert(successMessage);
|
||||
|
||||
// Очищаем tempCart (изолированное состояние модалки)
|
||||
tempCart.clear();
|
||||
|
||||
// Сбрасываем поля формы
|
||||
document.getElementById('tempKitDescription').value = '';
|
||||
@@ -618,6 +746,10 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
document.getElementById('salePrice').value = '';
|
||||
document.getElementById('salePriceBlock').style.display = 'none';
|
||||
|
||||
// Сбрасываем режим редактирования
|
||||
isEditMode = false;
|
||||
editingKitId = null;
|
||||
|
||||
// Закрываем модальное окно
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal'));
|
||||
modal.hide();
|
||||
@@ -632,11 +764,14 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
||||
alert(`Ошибка: ${data.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating temp kit:', error);
|
||||
alert('Ошибка при создании комплекта');
|
||||
console.error('Error saving kit:', error);
|
||||
alert('Ошибка при сохранении комплекта');
|
||||
} finally {
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.innerHTML = '<i class="bi bi-check-circle"></i> Создать и зарезервировать';
|
||||
const btnText = isEditMode
|
||||
? '<i class="bi bi-check-circle"></i> Сохранить изменения'
|
||||
: '<i class="bi bi-check-circle"></i> Создать и зарезервировать';
|
||||
confirmBtn.innerHTML = btnText;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -656,6 +791,22 @@ function getCookie(name) {
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
// Сброс режима редактирования при закрытии модального окна
|
||||
document.getElementById('createTempKitModal').addEventListener('hidden.bs.modal', function() {
|
||||
// Очищаем tempCart (изолированное состояние модалки)
|
||||
tempCart.clear();
|
||||
|
||||
if (isEditMode) {
|
||||
// Сбрасываем режим редактирования
|
||||
isEditMode = false;
|
||||
editingKitId = null;
|
||||
|
||||
// Восстанавливаем заголовок и текст кнопки
|
||||
document.getElementById('createTempKitModalLabel').textContent = 'Создать витринный букет из корзины';
|
||||
document.getElementById('confirmCreateTempKit').innerHTML = '<i class="bi bi-check-circle"></i> Создать и зарезервировать';
|
||||
}
|
||||
});
|
||||
|
||||
// Заглушки для функционала (будет реализовано позже)
|
||||
document.getElementById('checkoutNow').onclick = async () => {
|
||||
alert('Функционал будет подключен позже: создание заказа и списание со склада.');
|
||||
|
||||
Reference in New Issue
Block a user