').text(customer.phone).html());
}
if (customer.email) {
contactInfo.push($('
').text(customer.email).html());
}
if (contactInfo.length > 0) {
parts.push('
(' + contactInfo.join(', ') + ')');
}
return $('
' + parts.join('') + '');
}
/**
* Форматирование выбранного клиента в поле Select2
* Показывает только имя
*/
function formatCustomerSelection(customer) {
return customer.name || customer.text;
}
/**
* Открывает модальное окно создания нового клиента
* @param {string} prefillName - Предзаполненное имя (из поиска)
*/
function openCreateCustomerModal(prefillName = '') {
const modal = new bootstrap.Modal(document.getElementById('createCustomerModal'));
// Очищаем форму
document.getElementById('newCustomerName').value = prefillName || '';
document.getElementById('newCustomerPhone').value = '';
document.getElementById('newCustomerEmail').value = '';
document.getElementById('createCustomerError').classList.add('d-none');
modal.show();
}
/**
* Создаёт нового клиента через API
*/
async function createNewCustomer() {
const name = document.getElementById('newCustomerName').value.trim();
const phone = document.getElementById('newCustomerPhone').value.trim();
const email = document.getElementById('newCustomerEmail').value.trim();
const errorBlock = document.getElementById('createCustomerError');
// Валидация
if (!name) {
errorBlock.textContent = 'Укажите имя клиента';
errorBlock.classList.remove('d-none');
return;
}
// Скрываем ошибку
errorBlock.classList.add('d-none');
try {
const response = await fetch('/customers/api/create/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
},
body: JSON.stringify({
name: name,
phone: phone || null,
email: email || null
})
});
const data = await response.json();
if (data.success) {
// Выбираем созданного клиента с балансом
selectCustomer(data.id, data.name, data.wallet_balance || 0);
// Закрываем модалку
const modal = bootstrap.Modal.getInstance(document.getElementById('createCustomerModal'));
modal.hide();
// Показываем уведомление
alert(`Клиент "${data.name}" успешно создан!`);
} else {
// Показываем ошибку
errorBlock.textContent = data.error || 'Ошибка при создании клиента';
errorBlock.classList.remove('d-none');
}
} catch (error) {
console.error('Error creating customer:', error);
errorBlock.textContent = 'Ошибка сети при создании клиента';
errorBlock.classList.remove('d-none');
}
}
// ===== ФУНКЦИИ ДЛЯ МОДАЛЬНОГО ОКНА ВЫБОРА ЕДИНИЦЫ ПРОДАЖИ =====
/**
* Открывает модальное окно выбора единицы продажи
* @param {object} product - Объект товара с информацией о единицах продажи
*/
async function openProductUnitModal(product) {
unitModalProduct = product;
// Устанавливаем название товара
document.getElementById('unitModalProductName').textContent =
`${product.name}${product.sku ? ' (' + product.sku + ')' : ''}`;
// Загружаем единицы продажи
try {
const response = await fetch(
`/products/api/products/${product.id}/sales-units/?warehouse=${currentWarehouse.id}`
);
const data = await response.json();
if (!data.success || !data.sales_units || data.sales_units.length === 0) {
alert('Не удалось загрузить единицы продажи');
return;
}
unitModalSalesUnits = data.sales_units;
// Отрисовываем список единиц
renderUnitSelectionList();
// Выбираем единицу по умолчанию или первую
const defaultUnit = unitModalSalesUnits.find(u => u.is_default) || unitModalSalesUnits[0];
if (defaultUnit) {
selectUnit(defaultUnit);
}
// Открываем модальное окно
if (!unitModalInstance) {
unitModalInstance = new bootstrap.Modal(document.getElementById('selectProductUnitModal'));
}
unitModalInstance.show();
} catch (error) {
console.error('Ошибка загрузки единиц продажи:', error);
alert('Ошибка загрузки данных. Попробуйте ещё раз.');
}
}
/**
* Отрисовывает список единиц продажи
*/
function renderUnitSelectionList() {
const listContainer = document.getElementById('unitSelectionList');
listContainer.innerHTML = '';
unitModalSalesUnits.forEach(unit => {
const card = document.createElement('div');
card.className = 'unit-selection-card';
card.dataset.unitId = unit.id;
card.onclick = () => selectUnit(unit);
// Доступное количество
const availableQty = parseFloat(unit.available_quantity || 0);
let stockBadgeClass = 'stock-badge-none';
let stockText = 'Нет на складе';
if (availableQty > 10) {
stockBadgeClass = 'stock-badge-good';
stockText = `${availableQty} ${unit.unit_short_name} доступно`;
} else if (availableQty > 0) {
stockBadgeClass = 'stock-badge-low';
stockText = `${availableQty} ${unit.unit_short_name} доступно`;
}
// Бейдж "По умолчанию"
const defaultBadge = unit.is_default ?
'
По умолчанию' : '';
card.innerHTML = `
${unit.name}${defaultBadge}
${unit.unit_code} (${unit.unit_short_name})
${formatMoney(unit.actual_price)} руб
${stockText}
`;
listContainer.appendChild(card);
});
}
/**
* Выбирает единицу продажи
* @param {object} unit - Объект единицы продажи
*/
function selectUnit(unit) {
selectedSalesUnit = unit;
// Обновляем визуальное выделение
document.querySelectorAll('.unit-selection-card').forEach(card => {
if (card.dataset.unitId === String(unit.id)) {
card.classList.add('selected');
} else {
card.classList.remove('selected');
}
});
// Обновляем отображение выбранной единицы
document.getElementById('selectedUnitDisplay').textContent =
`${unit.name} (${unit.unit_short_name})`;
// Устанавливаем минимальное количество и шаг
const qtyInput = document.getElementById('unitModalQuantity');
qtyInput.value = roundQuantity(unit.min_quantity, 3);
qtyInput.min = unit.min_quantity;
qtyInput.step = unit.quantity_step;
// Устанавливаем цену
document.getElementById('unitModalPrice').value = unit.actual_price;
// Обновляем подсказку
const hintEl = document.getElementById('unitQtyHint');
hintEl.textContent = `Мин. ${unit.min_quantity}, шаг ${unit.quantity_step}`;
// Сбрасываем индикатор изменения цены
document.getElementById('priceOverrideIndicator').style.display = 'none';
// Пересчитываем итого
calculateUnitModalSubtotal();
// Валидируем количество
validateUnitQuantity();
}
/**
* Проверяет количество на соответствие ограничениям
* @returns {boolean} - true если валидно
*/
function validateUnitQuantity() {
if (!selectedSalesUnit) return false;
const qtyInput = document.getElementById('unitModalQuantity');
const qty = parseFloat(qtyInput.value);
const errorEl = document.getElementById('unitQtyError');
const confirmBtn = document.getElementById('confirmAddUnitToCart');
// Проверка минимального количества
if (qty < parseFloat(selectedSalesUnit.min_quantity)) {
errorEl.textContent = `Минимальное количество: ${selectedSalesUnit.min_quantity}`;
errorEl.style.display = 'block';
confirmBtn.disabled = true;
return false;
}
// Проверка шага (с учётом погрешности)
const step = parseFloat(selectedSalesUnit.quantity_step);
const minQty = parseFloat(selectedSalesUnit.min_quantity);
const diff = qty - minQty;
const remainder = diff % step;
const epsilon = 0.0001;
if (remainder > epsilon && (step - remainder) > epsilon) {
errorEl.textContent = `Количество должно быть кратно ${step}`;
errorEl.style.display = 'block';
confirmBtn.disabled = true;
return false;
}
// Всё ок, скрываем ошибку
errorEl.style.display = 'none';
confirmBtn.disabled = false;
return true;
}
/**
* Рассчитывает итоговую сумму
*/
function calculateUnitModalSubtotal() {
const qtyRaw = parseFloat(document.getElementById('unitModalQuantity').value) || 0;
const qty = roundQuantity(qtyRaw, 3); // Округляем количество
const price = parseFloat(document.getElementById('unitModalPrice').value) || 0;
// Округляем до 2 знаков после запятой для корректного отображения
const subtotal = Math.round(qty * price * 100) / 100;
document.getElementById('unitModalSubtotal').textContent = `${formatMoney(subtotal)} руб`;
// Проверяем изменение цены
if (selectedSalesUnit && Math.abs(price - parseFloat(selectedSalesUnit.actual_price)) > 0.01) {
document.getElementById('priceOverrideIndicator').style.display = 'block';
} else {
document.getElementById('priceOverrideIndicator').style.display = 'none';
}
}
/**
* Добавляет товар с выбранной единицей в корзину
*/
function addToCartFromModal() {
if (!validateUnitQuantity()) {
return;
}
const qtyRaw = parseFloat(document.getElementById('unitModalQuantity').value);
const qty = roundQuantity(qtyRaw, 3); // Округляем количество
const price = parseFloat(document.getElementById('unitModalPrice').value);
const priceOverridden = Math.abs(price - parseFloat(selectedSalesUnit.actual_price)) > 0.01;
// Формируем ключ корзины: product-{id}-{sales_unit_id}
const cartKey = `product-${unitModalProduct.id}-${selectedSalesUnit.id}`;
// Добавляем или обновляем в корзине
if (cart.has(cartKey)) {
const existing = cart.get(cartKey);
existing.qty = roundQuantity(existing.qty + qty, 3); // Округляем сумму
existing.price = price; // Обновляем цену
existing.quantity_step = parseFloat(selectedSalesUnit.quantity_step) || 1; // Обновляем шаг
existing.price_overridden = priceOverridden;
} else {
cart.set(cartKey, {
id: unitModalProduct.id,
name: unitModalProduct.name,
price: price,
qty: qty,
type: 'product',
sales_unit_id: selectedSalesUnit.id,
unit_name: selectedSalesUnit.name,
unit_short_name: selectedSalesUnit.unit_short_name,
quantity_step: parseFloat(selectedSalesUnit.quantity_step) || 1, // Сохраняем шаг количества
price_overridden: priceOverridden
});
}
// Обновляем корзину
renderCart();
saveCartToRedis();
// Перерисовываем товары для обновления визуального остатка
if (!isShowcaseView) {
renderProducts();
}
// Закрываем модальное окно
unitModalInstance.hide();
}
function renderCategories() {
const grid = document.getElementById('categoryGrid');
grid.innerHTML = '';
// Кнопка "Витрина" - первая в ряду
const showcaseCol = document.createElement('div');
showcaseCol.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const showcaseCard = document.createElement('div');
showcaseCard.className = 'card category-card showcase-card' + (isShowcaseView ? ' active' : '');
showcaseCard.style.backgroundColor = '#fff3cd';
showcaseCard.style.borderColor = '#ffc107';
showcaseCard.onclick = async () => {
isShowcaseView = true;
currentCategoryId = null;
await refreshShowcaseKits(); // Загружаем свежие данные
renderCategories();
renderProducts();
};
const showcaseBody = document.createElement('div');
showcaseBody.className = 'card-body';
const showcaseName = document.createElement('div');
showcaseName.className = 'category-name';
showcaseName.innerHTML = '
ВИТРИНА';
showcaseBody.appendChild(showcaseName);
showcaseCard.appendChild(showcaseBody);
showcaseCol.appendChild(showcaseCard);
grid.appendChild(showcaseCol);
// Кнопка "Все"
const allCol = document.createElement('div');
allCol.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const allCard = document.createElement('div');
allCard.className = 'card category-card' + (currentCategoryId === null && !isShowcaseView ? ' active' : '');
allCard.onclick = async () => {
currentCategoryId = null;
isShowcaseView = false;
currentSearchQuery = ''; // Сбрасываем поиск
document.getElementById('searchInput').value = ''; // Очищаем поле поиска
renderCategories();
await loadItems(); // Загрузка через API
};
const allBody = document.createElement('div');
allBody.className = 'card-body';
const allName = document.createElement('div');
allName.className = 'category-name';
allName.textContent = 'Все товары';
allBody.appendChild(allName);
allCard.appendChild(allBody);
allCol.appendChild(allCard);
grid.appendChild(allCol);
// Категории
CATEGORIES.forEach(cat => {
const col = document.createElement('div');
col.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const card = document.createElement('div');
card.className = 'card category-card' + (currentCategoryId === cat.id && !isShowcaseView ? ' active' : '');
card.onclick = async () => {
currentCategoryId = cat.id;
isShowcaseView = false;
currentSearchQuery = ''; // Сбрасываем поиск
document.getElementById('searchInput').value = ''; // Очищаем поле поиска
renderCategories();
await loadItems(); // Загрузка через API
};
const body = document.createElement('div');
body.className = 'card-body';
const name = document.createElement('div');
name.className = 'category-name';
name.textContent = cat.name;
body.appendChild(name);
card.appendChild(body);
col.appendChild(card);
grid.appendChild(col);
});
}
function renderProducts() {
const grid = document.getElementById('productGrid');
grid.innerHTML = '';
let filtered;
// Если выбран режим витрины - показываем витринные комплекты
if (isShowcaseView) {
filtered = showcaseKits;
// Для витрины — клиентская фильтрация по поиску
const searchTerm = document.getElementById('searchInput').value.toLowerCase().trim();
if (searchTerm) {
filtered = filtered.filter(item => {
const name = (item.name || '').toLowerCase();
const sku = (item.sku || '').toLowerCase();
return name.includes(searchTerm) || sku.includes(searchTerm);
});
}
} else {
// Обычный режим - ITEMS уже отфильтрованы на сервере (категория + поиск)
filtered = ITEMS;
}
filtered.forEach(item => {
const col = document.createElement('div');
col.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const card = document.createElement('div');
card.className = 'card product-card';
card.style.position = 'relative';
card.onclick = () => addToCart(item);
// Если это витринный комплект - добавляем кнопку редактирования
if (item.type === 'showcase_kit') {
// ИНДИКАЦИЯ БЛОКИРОВКИ
if (item.is_locked) {
// Создаем бейдж блокировки
const lockBadge = document.createElement('div');
lockBadge.style.position = 'absolute';
lockBadge.style.top = '5px';
lockBadge.style.left = '5px';
lockBadge.style.zIndex = '10';
if (item.locked_by_me) {
// Заблокирован мной - зеленый бейдж
lockBadge.className = 'badge bg-success';
lockBadge.innerHTML = '
В корзине';
lockBadge.title = 'Добавлен в вашу корзину';
} else {
// Заблокирован другим кассиром - красный бейдж + блокируем карточку
lockBadge.className = 'badge bg-danger';
lockBadge.innerHTML = '
Занят';
lockBadge.title = `В корзине ${item.locked_by_user}`;
// Затемняем карточку и блокируем клики
card.style.opacity = '0.5';
card.style.cursor = 'not-allowed';
card.onclick = (e) => {
e.stopPropagation();
alert(`Этот букет уже в корзине кассира "${item.locked_by_user}".\nДождитесь освобождения блокировки.`);
};
}
card.appendChild(lockBadge);
}
// Кнопка редактирования (только если НЕ заблокирован другим)
if (!item.is_locked || item.locked_by_me) {
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 = '
';
editBtn.onclick = (e) => {
e.stopPropagation();
openEditKitModal(item.id);
};
card.appendChild(editBtn);
}
}
const body = document.createElement('div');
body.className = 'card-body';
// Изображение товара/комплекта
const imageDiv = document.createElement('div');
imageDiv.className = 'product-image';
if (item.image) {
const img = document.createElement('img');
img.src = item.image;
img.alt = item.name;
img.loading = 'lazy'; // Lazy loading
imageDiv.appendChild(img);
} else {
imageDiv.innerHTML = '
';
}
// Информация о товаре/комплекте
const info = document.createElement('div');
info.className = 'product-info';
const name = document.createElement('div');
name.className = 'product-name';
name.textContent = item.name;
const stock = document.createElement('div');
stock.className = 'product-stock';
// Для витринных комплектов показываем название витрины И количество (доступно/всего)
if (item.type === 'showcase_kit') {
const availableCount = item.available_count || 0;
const totalCount = item.total_count || availableCount;
const inCart = totalCount - availableCount;
// Показываем: доступно / всего (и сколько в корзине)
let badgeClass = availableCount > 0 ? 'bg-success' : 'bg-secondary';
let badgeText = totalCount > 1 ? `${availableCount}/${totalCount}` : `${availableCount}`;
let cartInfo = inCart > 0 ? `
🛒${inCart}` : '';
stock.innerHTML = `🌺 ${item.showcase_name}
${badgeText}${cartInfo}`;
stock.style.color = '#856404';
stock.style.fontWeight = 'bold';
} else if (item.type === 'product' && item.available_qty !== undefined && item.reserved_qty !== undefined) {
// Для обычных товаров показываем остатки: FREE(-RESERVED-IN_CART)
// FREE = доступно для продажи (available - reserved - в корзине)
const available = parseFloat(item.available_qty) || 0;
const reserved = parseFloat(item.reserved_qty) || 0;
// Вычитаем количество из корзины для визуализации
const cartKey = `product-${item.id}`;
const inCart = cart.has(cartKey) ? cart.get(cartKey).qty : 0;
const free = available - reserved - inCart;
const freeRounded = roundQuantity(free, 3); // Округляем для отображения
// Создаём элементы для стилизации разных размеров
const freeSpan = document.createElement('span');
freeSpan.textContent = freeRounded; // Используем округлённое значение
freeSpan.style.fontSize = '1.1em';
freeSpan.style.fontWeight = 'bold';
freeSpan.style.fontStyle = 'normal';
// Отображаем резерв и корзину если они есть
const suffixParts = [];
if (reserved > 0) {
suffixParts.push(`−${roundQuantity(reserved, 3)}`);
}
if (inCart > 0) {
suffixParts.push(`−${roundQuantity(inCart, 3)}🛒`);
}
if (suffixParts.length > 0) {
const suffixSpan = document.createElement('span');
suffixSpan.textContent = `(${suffixParts.join(' ')})`;
suffixSpan.style.fontSize = '0.85em';
suffixSpan.style.marginLeft = '3px';
suffixSpan.style.fontStyle = 'normal';
stock.appendChild(freeSpan);
stock.appendChild(suffixSpan);
} else {
stock.appendChild(freeSpan);
}
// Цветовая индикация: красный если свободных остатков нет или отрицательные
if (free <= 0) {
stock.style.color = '#dc3545'; // Красный
} else if (free < 5) {
stock.style.color = '#ffc107'; // Жёлтый (мало остатков)
} else {
stock.style.color = '#28a745'; // Зелёный (достаточно)
}
} else {
// Комплекты: показываем доступное количество
if (item.type === 'kit' && item.free_qty !== undefined) {
const availableKits = parseFloat(item.free_qty) || 0;
if (availableKits > 0) {
stock.textContent = `В наличии: ${Math.floor(availableKits)} компл.`;
stock.style.color = '#28a745'; // Зелёный
} else {
stock.textContent = 'Под заказ';
stock.style.color = '#dc3545'; // Красный
}
} else {
// Fallback для старых данных
stock.textContent = item.in_stock ? 'В наличии' : 'Под заказ';
if (!item.in_stock) {
stock.style.color = '#dc3545';
}
}
}
const sku = document.createElement('div');
sku.className = 'product-sku';
const skuText = document.createElement('span');
skuText.textContent = item.sku || 'н/д';
const priceSpan = document.createElement('span');
priceSpan.className = 'product-price';
priceSpan.textContent = `${formatMoney(item.price)}`;
sku.appendChild(skuText);
sku.appendChild(priceSpan);
info.appendChild(name);
info.appendChild(stock);
info.appendChild(sku);
body.appendChild(imageDiv);
body.appendChild(info);
card.appendChild(body);
col.appendChild(card);
grid.appendChild(col);
});
}
// Загрузка товаров через API
async function loadItems(append = false) {
if (isLoadingItems) return;
isLoadingItems = true;
if (!append) {
currentPage = 1;
ITEMS = [];
}
try {
const params = new URLSearchParams({
page: currentPage,
page_size: 60
});
if (currentCategoryId) {
params.append('category_id', currentCategoryId);
}
// Добавляем поисковый запрос, если есть
if (currentSearchQuery) {
params.append('query', currentSearchQuery);
}
const response = await fetch(`/pos/api/items/?${params}`);
const data = await response.json();
if (data.success) {
if (append) {
ITEMS = ITEMS.concat(data.items);
} else {
ITEMS = data.items;
}
hasMoreItems = data.has_more;
if (data.has_more) {
currentPage = data.next_page;
}
renderProducts();
}
} catch (error) {
console.error('Ошибка загрузки товаров:', error);
} finally {
isLoadingItems = false;
}
}
// Infinite scroll
function setupInfiniteScroll() {
const grid = document.getElementById('productGrid');
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && hasMoreItems && !isLoadingItems && !isShowcaseView) {
loadItems(true); // Догрузка
}
});
},
{
rootMargin: '200px'
}
);
// Наблюдаем за концом грида
const sentinel = document.createElement('div');
sentinel.id = 'scroll-sentinel';
sentinel.style.height = '1px';
grid.parentElement.appendChild(sentinel);
observer.observe(sentinel);
}
async function addToCart(item) {
// ПРОВЕРКА НА НАЛИЧИЕ НЕСКОЛЬКИХ ЕДИНИЦ ПРОДАЖИ
if (item.type === 'product' && item.sales_units_count > 1) {
// Открываем модальное окно выбора единицы
await openProductUnitModal(item);
return;
}
const cartKey = `${item.type}-${item.id}`; // Уникальный ключ: "product-1" или "kit-1"
// СПЕЦИАЛЬНАЯ ЛОГИКА ДЛЯ ВИТРИННЫХ КОМПЛЕКТОВ (Soft Lock)
if (item.type === 'showcase_kit') {
// Пытаемся заблокировать 1 экземпляр через API
// API сам проверит доступность и вернёт ошибку если нет свободных
try {
const response = await fetch(`/pos/api/showcase-kits/${item.id}/add-to-cart/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ quantity: 1 })
});
const data = await response.json();
if (!response.ok || !data.success) {
// Нет доступных экземпляров или другая ошибка
alert(data.error || 'Не удалось добавить букет в корзину');
// Обновляем витрину чтобы показать актуальное состояние
if (isShowcaseView) {
await loadShowcaseKits();
}
return;
}
// Успешно заблокировали - добавляем/обновляем в корзине
const lockedItemIds = data.locked_item_ids || [];
if (cart.has(cartKey)) {
// Добавляем к существующим
const existing = cart.get(cartKey);
existing.qty += lockedItemIds.length;
existing.showcase_item_ids = [...(existing.showcase_item_ids || []), ...lockedItemIds];
} else {
// Создаём новую запись
cart.set(cartKey, {
id: item.id,
name: item.name,
price: Number(item.price),
qty: lockedItemIds.length,
type: item.type,
showcase_item_ids: lockedItemIds,
lock_expires_at: data.lock_expires_at
});
}
// Обновляем список витрины (чтобы показать актуальные available_count)
if (isShowcaseView) {
await loadShowcaseKits();
}
} catch (error) {
console.error('Ошибка при добавлении витринного комплекта:', error);
alert('Ошибка сервера. Попробуйте еще раз.');
return;
}
} else {
// ОБЫЧНАЯ ЛОГИКА для товаров и комплектов
if (!cart.has(cartKey)) {
cart.set(cartKey, { id: item.id, name: item.name, price: Number(item.price), qty: 1, type: item.type });
} else {
const cartItem = cart.get(cartKey);
cartItem.qty = roundQuantity(cartItem.qty + 1, 3);
}
}
renderCart();
saveCartToRedis(); // Сохраняем в Redis
// Перерисовываем товары для обновления визуального остатка
if (!isShowcaseView && item.type === 'product') {
renderProducts();
}
// Автоматический фокус на поле количества (только для обычных товаров)
if (item.type !== 'showcase_kit') {
setTimeout(() => {
const qtyInputs = document.querySelectorAll('.qty-input');
const itemIndex = Array.from(cart.keys()).indexOf(cartKey);
if (itemIndex !== -1 && qtyInputs[itemIndex]) {
qtyInputs[itemIndex].focus();
qtyInputs[itemIndex].select(); // Выделяем весь текст
}
}, 50);
}
}
// Вспомогательная функция для обновления количества товара в корзине
async function updateCartItemQty(cartKey, newQty) {
const item = cart.get(cartKey);
if (!item) return;
// Округляем новое количество
const roundedQty = roundQuantity(newQty, 3);
if (roundedQty <= 0) {
await removeFromCart(cartKey);
} else {
item.qty = roundedQty;
renderCart();
saveCartToRedis();
// Перерисовываем товары для обновления визуального остатка
if (!isShowcaseView && item.type === 'product') {
renderProducts();
}
}
}
function renderCart() {
const list = document.getElementById('cartList');
list.innerHTML = '';
let total = 0;
if (cart.size === 0) {
list.innerHTML = '
Корзина пуста
';
document.getElementById('cartTotal').textContent = '0.00';
updateShowcaseButtonState(); // Обновляем состояние кнопки
return;
}
cart.forEach((item, cartKey) => {
const row = document.createElement('div');
row.className = 'cart-item mb-2';
// СПЕЦИАЛЬНАЯ СТИЛИЗАЦИЯ для витринных комплектов
const isShowcaseKit = item.type === 'showcase_kit';
if (isShowcaseKit) {
row.style.backgroundColor = '#fff3cd'; // Желтый фон
row.style.border = '1px solid #ffc107';
row.style.borderRadius = '4px';
row.style.padding = '8px';
}
// Левая часть: Название и цена единицы
const namePrice = document.createElement('div');
namePrice.className = 'item-name-price';
// Иконка только для комплектов
let typeIcon = '';
if (item.type === 'kit' || item.type === 'showcase_kit') {
typeIcon = '
';
}
// Единица продажи (если есть)
let unitInfo = '';
if (item.sales_unit_id && item.unit_name) {
unitInfo = `
${item.unit_name}`;
}
namePrice.innerHTML = `
${typeIcon}${item.name}${unitInfo}
${formatMoney(item.price)} / ${item.unit_short_name || 'шт'}
`;
// Знак умножения
const multiplySign = document.createElement('span');
multiplySign.className = 'multiply-sign';
multiplySign.textContent = 'x';
// Контейнер для кнопок количества
const qtyControl = document.createElement('div');
qtyControl.className = 'd-flex align-items-center';
qtyControl.style.gap = '2px';
// СПЕЦИАЛЬНАЯ ЛОГИКА для витринных комплектов
if (isShowcaseKit) {
// Кнопка минус
const minusBtn = document.createElement('button');
minusBtn.className = 'btn btn-outline-secondary btn-sm';
minusBtn.innerHTML = '
';
minusBtn.onclick = async (e) => {
e.preventDefault();
await decreaseShowcaseKitQty(cartKey);
};
// Поле количества (только для отображения, readonly)
const qtyInput = document.createElement('input');
qtyInput.type = 'number';
qtyInput.className = 'qty-input form-control form-control-sm';
qtyInput.style.width = '60px';
qtyInput.style.textAlign = 'center';
qtyInput.style.padding = '0.375rem 0.25rem';
qtyInput.value = roundQuantity(item.qty, 3);
qtyInput.min = 1;
qtyInput.readOnly = true; // Только чтение - изменяем только через +/-
qtyInput.style.backgroundColor = '#fff3cd'; // Желтый фон как у витринных
// Кнопка плюс
const plusBtn = document.createElement('button');
plusBtn.className = 'btn btn-outline-secondary btn-sm';
plusBtn.innerHTML = '
';
plusBtn.onclick = async (e) => {
e.preventDefault();
await increaseShowcaseKitQty(cartKey);
};
// Собираем контейнер
qtyControl.appendChild(minusBtn);
qtyControl.appendChild(qtyInput);
qtyControl.appendChild(plusBtn);
} else {
// ОБЫЧНАЯ ЛОГИКА для товаров и комплектов
// Кнопка минус
const minusBtn = document.createElement('button');
minusBtn.className = 'btn btn-outline-secondary btn-sm';
minusBtn.innerHTML = '
';
minusBtn.onclick = async (e) => {
e.preventDefault();
const currentQty = cart.get(cartKey).qty;
const step = item.quantity_step || 1; // Используем шаг единицы или 1 по умолчанию
await updateCartItemQty(cartKey, roundQuantity(currentQty - step, 3));
};
// Поле ввода количества
const qtyInput = document.createElement('input');
qtyInput.type = 'number';
qtyInput.className = 'qty-input form-control form-control-sm';
qtyInput.style.width = '60px';
qtyInput.style.textAlign = 'center';
qtyInput.style.padding = '0.375rem 0.25rem';
qtyInput.value = roundQuantity(item.qty, 3);
qtyInput.min = 1;
qtyInput.step = item.quantity_step || 0.001; // Устанавливаем шаг единицы продажи
qtyInput.onchange = async (e) => {
const newQty = parseFloat(e.target.value) || 1;
await updateCartItemQty(cartKey, newQty);
};
// Округление при потере фокуса
qtyInput.onblur = (e) => {
const rawValue = parseFloat(e.target.value) || 1;
e.target.value = roundQuantity(rawValue, 3);
};
// Кнопка плюс
const plusBtn = document.createElement('button');
plusBtn.className = 'btn btn-outline-secondary btn-sm';
plusBtn.innerHTML = '
';
plusBtn.onclick = async (e) => {
e.preventDefault();
const currentQty = cart.get(cartKey).qty;
const step = item.quantity_step || 1; // Используем шаг единицы или 1 по умолчанию
await updateCartItemQty(cartKey, roundQuantity(currentQty + step, 3));
};
// Собираем контейнер
qtyControl.appendChild(minusBtn);
qtyControl.appendChild(qtyInput);
qtyControl.appendChild(plusBtn);
}
// Сумма за позицию
const itemTotal = document.createElement('div');
itemTotal.className = 'item-total';
itemTotal.textContent = formatMoney(item.price * item.qty);
// Кнопка удаления
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-sm btn-link text-danger p-0';
deleteBtn.innerHTML = '
';
deleteBtn.onclick = () => removeFromCart(cartKey);
row.appendChild(namePrice);
row.appendChild(multiplySign);
row.appendChild(qtyControl);
row.appendChild(itemTotal);
row.appendChild(deleteBtn);
list.appendChild(row);
total += item.qty * item.price;
});
document.getElementById('cartTotal').textContent = formatMoney(total);
// Обновляем состояние кнопки "НА ВИТРИНУ"
updateShowcaseButtonState();
}
async function removeFromCart(cartKey) {
const item = cart.get(cartKey);
// СПЕЦИАЛЬНАЯ ЛОГИКА для витринных комплектов - снимаем блокировку
if (item && item.type === 'showcase_kit') {
try {
// Передаём конкретные showcase_item_ids для снятия блокировки
const body = {};
if (item.showcase_item_ids && item.showcase_item_ids.length > 0) {
body.showcase_item_ids = item.showcase_item_ids;
}
const response = await fetch(`/pos/api/showcase-kits/${item.id}/remove-from-cart/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const data = await response.json();
if (!response.ok) {
console.error('Ошибка при снятии блокировки:', data.error);
// Продолжаем удаление из корзины даже при ошибке
}
// Обновляем список витрины (чтобы убрать индикацию блокировки)
if (isShowcaseView) {
await loadShowcaseKits();
}
} catch (error) {
console.error('Ошибка при снятии блокировки витринного комплекта:', error);
// Продолжаем удаление из корзины
}
}
cart.delete(cartKey);
renderCart();
saveCartToRedis(); // Сохраняем в Redis
// Перерисовываем товары для обновления визуального остатка
if (!isShowcaseView && item && item.type === 'product') {
renderProducts();
}
}
/**
* Увеличивает количество витринного комплекта в корзине
* Добавляет еще один экземпляр через API (если есть доступные)
*/
async function increaseShowcaseKitQty(cartKey) {
const item = cart.get(cartKey);
if (!item || item.type !== 'showcase_kit') return;
try {
// Пытаемся заблокировать еще 1 экземпляр
const response = await fetch(`/pos/api/showcase-kits/${item.id}/add-to-cart/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ quantity: 1 })
});
const data = await response.json();
if (!response.ok || !data.success) {
// Нет доступных экземпляров
alert(data.error || 'Нет доступных экземпляров этого букета на витрине');
return;
}
// Успешно заблокировали - обновляем корзину
const lockedItemIds = data.locked_item_ids || [];
item.qty += lockedItemIds.length;
item.showcase_item_ids = [...(item.showcase_item_ids || []), ...lockedItemIds];
renderCart();
saveCartToRedis();
// Обновляем список витрины
if (isShowcaseView) {
await loadShowcaseKits();
}
} catch (error) {
console.error('Ошибка при увеличении количества витринного комплекта:', error);
alert('Ошибка сервера. Попробуйте еще раз.');
}
}
/**
* Уменьшает количество витринного комплекта в корзине
* Снимает блокировку с одного экземпляра (последнего в списке)
*/
async function decreaseShowcaseKitQty(cartKey) {
const item = cart.get(cartKey);
if (!item || item.type !== 'showcase_kit') return;
// Если количество = 1, удаляем полностью
if (item.qty <= 1) {
await removeFromCart(cartKey);
return;
}
try {
// Снимаем блокировку с последнего экземпляра
const showcaseItemIds = item.showcase_item_ids || [];
if (showcaseItemIds.length === 0) {
// Нет ID - просто удаляем
await removeFromCart(cartKey);
return;
}
// Берем последний ID из списка
const itemIdToRelease = showcaseItemIds[showcaseItemIds.length - 1];
const response = await fetch(`/pos/api/showcase-kits/${item.id}/remove-from-cart/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify({ showcase_item_ids: [itemIdToRelease] })
});
const data = await response.json();
if (!response.ok) {
console.error('Ошибка при снятии блокировки:', data.error);
}
// Обновляем корзину
item.qty -= 1;
item.showcase_item_ids = showcaseItemIds.filter(id => id !== itemIdToRelease);
renderCart();
saveCartToRedis();
// Обновляем список витрины
if (isShowcaseView) {
await loadShowcaseKits();
}
} catch (error) {
console.error('Ошибка при уменьшении количества витринного комплекта:', error);
alert('Ошибка сервера. Попробуйте еще раз.');
}
}
async function clearCart() {
// Сбрасываем все свои блокировки витринных букетов
try {
await fetch('/pos/api/showcase-kits/release-all-my-locks/', {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') }
});
} catch (e) {
console.error('Ошибка сброса блокировок:', e);
}
// Очищаем корзину
cart.clear();
renderCart();
saveCartToRedis(); // Сохраняем пустую корзину в Redis
// Обновляем отображение товаров/витрины чтобы показать актуальные остатки
if (isShowcaseView) {
await loadShowcaseKits();
} else {
renderProducts(); // Перерисовать карточки товаров с актуальными остатками
}
}
document.getElementById('clearCart').onclick = clearCart;
/**
* Обновляет состояние кнопки "НА ВИТРИНУ"
* Блокирует кнопку если в корзине есть витринный комплект
*/
function updateShowcaseButtonState() {
const showcaseBtn = document.getElementById('addToShowcaseBtn');
if (!showcaseBtn) return;
// Проверяем наличие витринных комплектов в корзине
let hasShowcaseKit = false;
for (const [cartKey, item] of cart) {
if (item.type === 'showcase_kit') {
hasShowcaseKit = true;
break;
}
}
if (hasShowcaseKit) {
// Блокируем кнопку
showcaseBtn.disabled = true;
showcaseBtn.classList.add('disabled');
showcaseBtn.style.opacity = '0.5';
showcaseBtn.style.cursor = 'not-allowed';
showcaseBtn.title = '⚠️ В корзине уже есть витринный комплект. Удалите его перед созданием нового';
} else {
// Разблокируем кнопку
showcaseBtn.disabled = false;
showcaseBtn.classList.remove('disabled');
showcaseBtn.style.opacity = '1';
showcaseBtn.style.cursor = 'pointer';
showcaseBtn.title = 'Создать букет на витрину из текущей корзины';
}
}
// Кнопка "На витрину" - функционал будет добавлен позже
document.getElementById('addToShowcaseBtn').onclick = () => {
openCreateTempKitModal();
};
// Функция открытия модального окна для создания временного комплекта
async function openCreateTempKitModal() {
// Проверяем что корзина не пуста
if (cart.size === 0) {
alert('Корзина пуста. Добавьте товары перед созданием комплекта.');
return;
}
// Проверяем что в корзине НЕТ витринных комплектов
let hasShowcaseKit = false;
for (const [cartKey, item] of cart) {
if (item.type === 'showcase_kit') {
hasShowcaseKit = true;
break;
}
}
if (hasShowcaseKit) {
alert('⚠️ В корзине уже есть витринный комплект!\n\nНельзя создать новый букет на витрину, пока в корзине находится другой витринный букет.\n\nУдалите витринный букет из корзины или завершите текущую продажу.');
return;
}
// Проверяем что в корзине только товары (не обычные комплекты)
let hasKits = false;
for (const [cartKey, item] of cart) {
if (item.type === 'kit') {
hasKits = true;
break;
}
}
if (hasKits) {
alert('В корзине есть комплекты. Для витрины добавляйте только отдельные товары.');
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'})}`;
document.getElementById('tempKitName').value = defaultName;
// Загружаем список витрин
await loadShowcases();
// Заполняем список товаров из tempCart
renderTempKitItems();
// Открываем модальное окно
const modal = new bootstrap.Modal(document.getElementById('createTempKitModal'));
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 = 'Сохранить изменения';
// Показываем кнопку "Разобрать" и блок добавления товаров
document.getElementById('disassembleKitBtn').style.display = 'block';
document.getElementById('showcaseKitQuantityBlock').style.display = 'none';
document.getElementById('addProductBlock').style.display = 'block';
// Инициализируем компонент поиска товаров
setTimeout(() => {
if (window.ProductSearchPicker) {
const picker = ProductSearchPicker.init('#temp-kit-product-picker', {
onAddSelected: function(product, instance) {
if (product) {
// Добавляем товар в tempCart
const cartKey = `product-${product.id}`;
if (tempCart.has(cartKey)) {
// Увеличиваем количество
const existing = tempCart.get(cartKey);
existing.qty += 1;
} else {
// Добавляем новый товар
tempCart.set(cartKey, {
id: product.id,
name: product.text || product.name,
price: Number(product.price || 0),
qty: 1,
type: 'product'
});
}
// Обновляем отображение
renderTempKitItems();
// Очищаем выбор в пикере
instance.clearSelection();
}
}
});
}
}, 100);
// Открываем модальное окно
const modal = new bootstrap.Modal(document.getElementById('createTempKitModal'));
modal.show();
} catch (error) {
console.error('Error loading kit for edit:', error);
alert('Ошибка при загрузке комплекта');
}
}
// Обновление списка витринных комплектов
async function loadShowcaseKits() {
try {
const response = await fetch('/pos/api/showcase-kits/');
const data = await response.json();
if (data.success) {
showcaseKits = data.items;
// Перерисовываем грид если мы в режиме витрины
if (isShowcaseView) {
renderProducts();
}
} else {
console.error('Failed to refresh showcase kits:', data);
}
} catch (error) {
console.error('Error refreshing showcase kits:', error);
}
}
// Алиас для совместимости
const refreshShowcaseKits = loadShowcaseKits;
// Загрузка списка витрин
async function loadShowcases() {
try {
const response = await fetch('/pos/api/get-showcases/');
const data = await response.json();
const select = document.getElementById('showcaseSelect');
select.innerHTML = '
';
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) {
defaultShowcaseId = showcase.id;
}
});
// Автовыбор витрины по умолчанию
if (defaultShowcaseId) {
select.value = defaultShowcaseId;
}
} else {
select.innerHTML = '
';
}
} catch (error) {
console.error('Error loading showcases:', error);
alert('Ошибка загрузки витрин');
}
}
// Отображение товаров из tempCart в модальном окне
function renderTempKitItems() {
const container = document.getElementById('tempKitItemsList');
container.innerHTML = '';
let estimatedTotal = 0;
tempCart.forEach((item, cartKey) => {
// Только товары (не комплекты)
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';
// Левая часть: название и цена
const leftDiv = document.createElement('div');
leftDiv.className = 'flex-grow-1';
leftDiv.innerHTML = `
${item.name}
${formatMoney(item.price)} руб. / шт.
`;
// Правая часть: контролы количества и удаление
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 = '
';
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 = '
';
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 = `
${formatMoney(item.qty * item.price)} руб.`;
// Кнопка удаления
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-sm btn-outline-danger';
deleteBtn.innerHTML = '
';
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 = '
Нет товаров
';
}
// Обновляем все расчеты цен
updatePriceCalculations(estimatedTotal);
}
// Расчет и обновление всех цен
function updatePriceCalculations(basePrice = null) {
// Если basePrice не передан, пересчитываем из tempCart
if (basePrice === null) {
basePrice = 0;
tempCart.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) {
alert('Введите название комплекта');
return;
}
if (!showcaseId && !isEditMode) {
alert('Выберите витрину');
return;
}
// Собираем товары из tempCart (изолированное состояние модалки)
const items = [];
tempCart.forEach((item, cartKey) => {
if (item.type === 'product') {
items.push({
product_id: item.id,
quantity: item.qty
});
}
});
if (items.length === 0) {
alert('Нет товаров для создания комплекта');
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;
// Получаем количество букетов для создания
const showcaseKitQuantity = parseInt(document.getElementById('showcaseKitQuantity').value, 10) || 1;
// Формируем FormData для отправки с файлом
const formData = new FormData();
formData.append('kit_name', kitName);
if (showcaseId) {
formData.append('showcase_id', showcaseId);
formData.append('quantity', showcaseKitQuantity); // Количество экземпляров на витрину
}
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);
} else if (isEditMode && document.getElementById('photoPreview').style.display === 'none') {
// Если фото было удалено
formData.append('remove_photo', '1');
}
// Отправляем запрос на сервер
const confirmBtn = document.getElementById('confirmCreateTempKit');
confirmBtn.disabled = true;
const url = isEditMode
? `/pos/api/product-kits/${editingKitId}/update/`
: '/pos/api/create-temp-kit/';
const actionText = isEditMode ? 'Сохранение...' : 'Создание...';
confirmBtn.innerHTML = `
${actionText}`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
// Не указываем Content-Type - браузер сам установит multipart/form-data
},
body: formData
});
const data = await response.json();
if (data.success) {
// Успех!
const createdCount = data.available_count || 1;
const qtyInfo = createdCount > 1 ? `\nСоздано экземпляров: ${createdCount}` : '';
let successMessage = isEditMode
? `✅ ${data.message}\n\nКомплект: ${data.kit_name}\nЦена: ${data.kit_price} руб.`
: `✅ ${data.message}
Комплект: ${data.kit_name}
Цена: ${data.kit_price} руб.${qtyInfo}
Зарезервировано компонентов: ${data.reservations_count}`;
// Если есть предупреждение о нехватке товара - добавляем его
if (data.warnings && data.warnings.length > 0) {
successMessage += '\n\n⚠️ ВНИМАНИЕ: Нехватка товара на складе!\n';
data.warnings.forEach(warning => {
successMessage += `\n• ${warning}`;
});
successMessage += '\n\nПроверьте остатки и пополните склад.';
}
alert(successMessage);
// Очищаем tempCart (изолированное состояние модалки)
tempCart.clear();
// Сбрасываем поля формы
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';
document.getElementById('showcaseKitQuantity').value = '1'; // Сброс количества
// Запоминаем, был ли режим редактирования до сброса
const wasEditMode = isEditMode;
// Сбрасываем режим редактирования
isEditMode = false;
editingKitId = null;
// Закрываем модальное окно
const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal'));
modal.hide();
// Если это было СОЗДАНИЕ витринного комплекта из корзины,
// очищаем основную корзину POS
if (!wasEditMode) {
await clearCart();
}
// Обновляем витринные комплекты и переключаемся на вид витрины
isShowcaseView = true;
currentCategoryId = null;
await refreshShowcaseKits();
renderCategories();
renderProducts();
} else {
alert(`Ошибка: ${data.error}`);
}
} catch (error) {
console.error('Error saving kit:', error);
alert('Ошибка при сохранении комплекта');
} finally {
confirmBtn.disabled = false;
const btnText = isEditMode
? '
Сохранить изменения'
: '
Создать и зарезервировать';
confirmBtn.innerHTML = btnText;
}
};
// Обработчик кнопки "Разобрать букет"
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;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Алиас для обратной совместимости
// ВАЖНО: При CSRF_USE_SESSIONS=True токен хранится в сессии, а не в cookie
// Извлекаем его из скрытого поля в HTML ({% csrf_token %})
const getCsrfToken = () => {
// Пытаемся найти токен в DOM (из {% csrf_token %})
const csrfInput = document.querySelector('[name=csrfmiddlewaretoken]');
if (csrfInput) {
return csrfInput.value;
}
// Fallback: пытаемся прочитать из cookie (если CSRF_USE_SESSIONS=False)
return getCookie('csrftoken');
};
// Сброс режима редактирования при закрытии модального окна
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 = '
Создать и зарезервировать';
// Скрываем кнопку "Разобрать" и блок добавления товаров
document.getElementById('disassembleKitBtn').style.display = 'none';
document.getElementById('showcaseKitQuantityBlock').style.display = 'block';
document.getElementById('addProductBlock').style.display = 'none';
}
});
// Открытие модалки "Продажа" и рендер сводки корзины
document.getElementById('checkoutNow').onclick = () => {
if (cart.size === 0) {
alert('Корзина пуста. Добавьте товары перед продажей.');
return;
}
renderCheckoutModal();
const modal = new bootstrap.Modal(document.getElementById('checkoutModal'));
modal.show();
};
// Рендер позиций корзины и итога в модалке продажи
function renderCheckoutModal() {
const container = document.getElementById('checkoutItems');
container.innerHTML = '';
let total = 0;
cart.forEach((item) => {
const row = document.createElement('div');
row.className = 'd-flex justify-content-between align-items-center mb-2 pb-2 border-bottom';
// Иконка для комплектов
let typeIcon = '';
if (item.type === 'kit' || item.type === 'showcase_kit') {
typeIcon = '
';
} else {
typeIcon = '
';
}
row.innerHTML = `
${typeIcon}${item.name}
${item.qty} шт × ${formatMoney(item.price)} руб.
${formatMoney(item.qty * item.price)} руб.
`;
container.appendChild(row);
total += item.qty * item.price;
});
// Обновляем информацию о клиенте
updateCustomerDisplay();
}
// ===== CHECKOUT: ПОДТВЕРЖДЕНИЕ ПРОДАЖИ =====
let paymentWidget = null;
// При открытии модалки checkout
document.getElementById('checkoutModal').addEventListener('show.bs.modal', () => {
const customer = selectedCustomer || SYSTEM_CUSTOMER;
const walletBalance = customer.wallet_balance || 0;
// Показываем баланс кошелька
const walletDiv = document.getElementById('checkoutWalletBalance');
if (customer.id !== SYSTEM_CUSTOMER.id) {
document.getElementById('checkoutWalletBalanceAmount').textContent = walletBalance.toFixed(2);
walletDiv.style.display = 'block';
} else {
walletDiv.style.display = 'none';
}
// Вычисляем итоговую сумму
let totalAmount = 0;
cart.forEach((item) => {
totalAmount += item.qty * item.price;
});
document.getElementById('checkoutFinalPrice').textContent = formatMoney(totalAmount) + ' руб.';
// Инициализируем виджет в single mode
initPaymentWidget('single', {
order: { total: totalAmount, amount_due: totalAmount, amount_paid: 0 },
customer: { id: customer.id, name: customer.name, wallet_balance: walletBalance }
});
});
// Переключение режима оплаты
document.getElementById('singlePaymentMode').addEventListener('click', function() {
document.getElementById('singlePaymentMode').classList.add('active');
document.getElementById('mixedPaymentMode').classList.remove('active');
reinitPaymentWidget('single');
});
document.getElementById('mixedPaymentMode').addEventListener('click', function() {
document.getElementById('mixedPaymentMode').classList.add('active');
document.getElementById('singlePaymentMode').classList.remove('active');
reinitPaymentWidget('mixed');
});
function reinitPaymentWidget(mode) {
const customer = selectedCustomer || SYSTEM_CUSTOMER;
const totalAmountText = document.getElementById('checkoutFinalPrice').textContent;
const totalAmount = parseFloat(totalAmountText.replace(/[^\d.]/g, ''));
initPaymentWidget(mode, {
order: { total: totalAmount, amount_due: totalAmount, amount_paid: 0 },
customer: { id: customer.id, name: customer.name, wallet_balance: customer.wallet_balance || 0 }
});
}
async function initPaymentWidget(mode, data) {
const paymentMethods = [
{ id: 1, code: 'account_balance', name: 'С баланса счёта' },
{ id: 2, code: 'cash', name: 'Наличными' },
{ id: 3, code: 'card', name: 'Картой' },
{ id: 4, code: 'online', name: 'Онлайн' }
];
// Динамически загружаем PaymentWidget если еще не загружен
if (!window.PaymentWidget) {
try {
const module = await import('/static/orders/js/payment_widget.js');
window.PaymentWidget = module.PaymentWidget;
} catch (error) {
console.error('Ошибка загрузки PaymentWidget:', error);
alert('Ошибка загрузки модуля оплаты. Перезагрузите страницу.');
return;
}
}
paymentWidget = new window.PaymentWidget({
containerId: 'paymentWidgetContainer',
mode: mode,
order: data.order,
customer: data.customer,
paymentMethods: paymentMethods,
onSubmit: (paymentsData) => handleCheckoutSubmit(paymentsData)
});
}
// Обработчик кнопки "Подтвердить продажу"
document.getElementById('confirmCheckoutBtn').onclick = () => {
if (paymentWidget) {
paymentWidget.submit();
}
};
// Отправка заказа на сервер
async function handleCheckoutSubmit(paymentsData) {
try {
// Блокируем кнопку
const btn = document.getElementById('confirmCheckoutBtn');
btn.disabled = true;
btn.innerHTML = '
Обработка...';
// Собираем данные
const customer = selectedCustomer || SYSTEM_CUSTOMER;
const orderData = {
customer_id: customer.id,
warehouse_id: currentWarehouse.id,
items: Array.from(cart.values()).map(item => {
const itemData = {
type: item.type,
id: item.id,
quantity: item.qty,
price: item.price
};
// Для витринных букетов передаём ID конкретных экземпляров
if (item.type === 'showcase_kit' && item.showcase_item_ids) {
itemData.showcase_item_ids = item.showcase_item_ids;
}
// Для товаров с единицами продажи
if (item.sales_unit_id) {
itemData.sales_unit_id = item.sales_unit_id;
}
return itemData;
}),
payments: paymentsData,
notes: document.getElementById('orderNote').value.trim()
};
// Отправляем на сервер
const response = await fetch('/pos/api/checkout/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify(orderData)
});
const result = await response.json();
if (result.success) {
console.log('✅ Заказ успешно создан:', result);
// Успех
alert(`Заказ #${result.order_number} успешно создан!\nСумма: ${result.total_amount.toFixed(2)} руб.`);
// Очищаем корзину
cart.clear();
renderCart();
console.log('🧹 Корзина очищена');
// Закрываем модалку
const modal = bootstrap.Modal.getInstance(document.getElementById('checkoutModal'));
if (modal) {
modal.hide();
console.log('❌ Модалка закрыта');
}
// Перезагружаем страницу для обновления остатков товаров
console.log('🔄 Перезагрузка страницы...');
setTimeout(() => {
window.location.reload();
}, 500);
} else {
alert('Ошибка: ' + result.error);
}
} catch (error) {
console.error('Ошибка checkout:', error);
alert('Ошибка при проведении продажи: ' + error.message);
} finally {
// Разблокируем кнопку
const btn = document.getElementById('confirmCheckoutBtn');
btn.disabled = false;
btn.innerHTML = '
Подтвердить продажу';
}
}
// ===== ОБРАБОТЧИКИ ДЛЯ РАБОТЫ С КЛИЕНТОМ =====
// Кнопка "Выбрать клиента" в корзине
document.getElementById('customerSelectBtn').addEventListener('click', () => {
const modal = new bootstrap.Modal(document.getElementById('selectCustomerModal'));
modal.show();
});
// Кнопка сброса клиента на системного (в корзине)
document.getElementById('resetCustomerBtn').addEventListener('click', () => {
selectCustomer(SYSTEM_CUSTOMER.id, SYSTEM_CUSTOMER.name, SYSTEM_CUSTOMER.wallet_balance || 0);
});
// Кнопка "Выбрать клиента" в модалке продажи
document.getElementById('checkoutCustomerSelectBtn').addEventListener('click', () => {
const modal = new bootstrap.Modal(document.getElementById('selectCustomerModal'));
modal.show();
});
// Кнопка сброса клиента на системного (в модалке продажи)
document.getElementById('checkoutResetCustomerBtn').addEventListener('click', () => {
selectCustomer(SYSTEM_CUSTOMER.id, SYSTEM_CUSTOMER.name, SYSTEM_CUSTOMER.wallet_balance || 0);
});
// Кнопка "Создать нового клиента" в модалке выбора
document.getElementById('createNewCustomerBtn').addEventListener('click', () => {
// Закрываем модалку выбора
const selectModal = bootstrap.Modal.getInstance(document.getElementById('selectCustomerModal'));
selectModal.hide();
// Открываем модалку создания
openCreateCustomerModal();
});
// Кнопка "Выбрать системного клиента"
document.getElementById('selectSystemCustomerBtn').addEventListener('click', () => {
selectCustomer(SYSTEM_CUSTOMER.id, SYSTEM_CUSTOMER.name, SYSTEM_CUSTOMER.wallet_balance || 0);
// Закрываем модалку
const modal = bootstrap.Modal.getInstance(document.getElementById('selectCustomerModal'));
modal.hide();
});
// Кнопка подтверждения создания клиента
document.getElementById('confirmCreateCustomerBtn').addEventListener('click', () => {
createNewCustomer();
});
// Инициализация Select2 при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
initCustomerSelect2();
updateCustomerDisplay(); // Обновляем UI с системным клиентом
// Восстанавливаем корзину из Redis (если есть сохраненные данные)
const savedCartData = JSON.parse(document.getElementById('cartData').textContent);
if (savedCartData && Object.keys(savedCartData).length > 0) {
// Конвертируем обычный объект обратно в Map
Object.entries(savedCartData).forEach(([key, value]) => {
cart.set(key, value);
});
renderCart(); // Отрисовываем восстановленную корзину
}
// ===== ОБРАБОТЧИКИ ДЛЯ МОДАЛКИ ВЫБОРА ЕДИНИЦЫ ПРОДАЖИ =====
// Кнопки изменения количества
document.getElementById('unitQtyDecrement').addEventListener('click', () => {
const input = document.getElementById('unitModalQuantity');
const step = parseFloat(input.step) || 1;
const newValue = Math.max(parseFloat(input.min), parseFloat(input.value) - step);
input.value = roundQuantity(newValue, 3);
calculateUnitModalSubtotal();
validateUnitQuantity();
});
document.getElementById('unitQtyIncrement').addEventListener('click', () => {
const input = document.getElementById('unitModalQuantity');
const step = parseFloat(input.step) || 1;
const newValue = parseFloat(input.value) + step;
input.value = roundQuantity(newValue, 3);
calculateUnitModalSubtotal();
validateUnitQuantity();
});
// Изменение количества вручную
document.getElementById('unitModalQuantity').addEventListener('input', () => {
calculateUnitModalSubtotal();
validateUnitQuantity();
});
// Округление количества при потере фокуса
document.getElementById('unitModalQuantity').addEventListener('blur', (e) => {
const rawValue = parseFloat(e.target.value) || 0;
e.target.value = roundQuantity(rawValue, 3);
calculateUnitModalSubtotal();
validateUnitQuantity();
});
// Изменение цены
document.getElementById('unitModalPrice').addEventListener('input', () => {
calculateUnitModalSubtotal();
});
// Кнопка подтверждения добавления в корзину
document.getElementById('confirmAddUnitToCart').addEventListener('click', () => {
addToCartFromModal();
});
});
// Смена склада
const changeWarehouseBtn = document.getElementById('changeWarehouseBtn');
if (changeWarehouseBtn) {
changeWarehouseBtn.addEventListener('click', () => {
const modal = new bootstrap.Modal(document.getElementById('selectWarehouseModal'));
modal.show();
});
}
// Обработка выбора склада из списка
document.addEventListener('click', async (e) => {
const warehouseItem = e.target.closest('.warehouse-item');
if (!warehouseItem) return;
const warehouseId = warehouseItem.dataset.warehouseId;
const warehouseName = warehouseItem.dataset.warehouseName;
// Проверяем, есть ли товары в корзине
if (cart.size > 0) {
const confirmed = confirm(`При смене склада корзина будет очищена.\n\nПереключиться на склад "${warehouseName}"?`);
if (!confirmed) return;
}
try {
// Отправляем запрос на смену склада
const response = await fetch(`/pos/api/set-warehouse/${warehouseId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCsrfToken()
}
});
const data = await response.json();
if (data.success) {
// Перезагружаем страницу для обновления данных
location.reload();
} else {
alert(`Ошибка: ${data.error}`);
}
} catch (error) {
console.error('Ошибка при смене склада:', error);
alert('Произошла ошибка при смене склада');
}
});
// Обработчик поиска с debounce
const searchInput = document.getElementById('searchInput');
const clearSearchBtn = document.getElementById('clearSearchBtn');
searchInput.addEventListener('input', (e) => {
const query = e.target.value.trim();
// Показываем/скрываем кнопку очистки
if (e.target.value.length > 0) {
clearSearchBtn.style.display = 'block';
} else {
clearSearchBtn.style.display = 'none';
}
// Отменяем предыдущий таймер
if (searchDebounceTimer) {
clearTimeout(searchDebounceTimer);
}
// Если поле пустое — очищаем экран
if (query === '') {
currentSearchQuery = '';
ITEMS = []; // Очистка
renderProducts(); // Пустой экран
return;
}
// Минимальная длина поиска — 3 символа
if (query.length < 3) {
// Не реагируем на ввод менее 3 символов
return;
}
// Для витрины — мгновенная клиентская фильтрация
if (isShowcaseView) {
renderProducts();
return;
}
// Для обычных товаров/комплектов — серверный поиск с debounce 300мс
searchDebounceTimer = setTimeout(async () => {
currentSearchQuery = query;
await loadItems(); // Перезагрузка с серверным поиском
}, 300);
});
// Обработчик кнопки очистки поиска
clearSearchBtn.addEventListener('click', () => {
searchInput.value = '';
clearSearchBtn.style.display = 'none';
currentSearchQuery = '';
ITEMS = [];
renderProducts(); // Пустой экран
});
// Инициализация
renderCategories();
renderProducts(); // Сначала пустая сетка
renderCart();
setupInfiniteScroll(); // Установка infinite scroll
// Установить фокус на строку поиска
document.getElementById('searchInput').focus();
// ===== ОТЛОЖЕННЫЙ ЗАКАЗ =====
/**
* Создаёт отложенный заказ (черновик) и резервирует витринные букеты
*
* FLOW:
* 1. Создаём Order (статус 'draft') через API
* 2. ShowcaseItem резервируются в той же транзакции (in_cart → reserved)
* 3. Очищаем корзину POS
* 4. Открываем форму редактирования заказа
*/
async function createDeferredOrder() {
// Проверяем, что корзина не пуста
if (cart.size === 0) {
alert('Корзина пуста! Добавьте товары в корзину.');
return;
}
try {
// Собираем данные для черновика
const items = Array.from(cart.values()).map(item => {
const itemData = {
type: item.type,
id: item.id,
quantity: item.qty,
price: item.price
};
// Для товаров с единицами продажи передаём sales_unit_id
if (item.sales_unit_id) {
itemData.sales_unit_id = item.sales_unit_id;
}
// Для витринных букетов передаём showcase_item_ids
if (item.type === 'showcase_kit' && item.showcase_item_ids) {
itemData.showcase_item_ids = item.showcase_item_ids;
}
return itemData;
});
const customer = selectedCustomer || SYSTEM_CUSTOMER;
const orderData = {
customer_id: customer.id,
items: items
};
// Создаём заказ через новый endpoint
const response = await fetch('/orders/api/create-from-pos/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
},
body: JSON.stringify(orderData)
});
const result = await response.json();
if (result.success) {
console.log(`✅ Заказ #${result.order_number} создан (черновик). ShowcaseItem зарезервированы.`);
// КРИТИЧНО: Очищаем корзину POS (включая витринные букеты)
cart.clear();
renderCart();
saveCartToRedis(); // Сохраняем пустую корзину в Redis
// Перезагружаем витрину (чтобы зарезервированные букеты исчезли)
if (isShowcaseView) {
await refreshShowcaseKits();
renderProducts();
}
// Открываем форму редактирования в новой вкладке
window.open(`/orders/${result.order_number}/edit/`, '_blank');
} else {
alert(`Ошибка: ${result.error}`);
}
} catch (error) {
console.error('Ошибка при создании отложенного заказа:', error);
alert('Произошла ошибка при создании черновика заказа');
}
}
// Обработчик кнопки "ОТЛОЖЕННЫЙ заказ"
const scheduleLaterBtn = document.getElementById('scheduleLater');
if (scheduleLaterBtn) {
scheduleLaterBtn.addEventListener('click', createDeferredOrder);
}