From c0e9b92e4a6a49d4c73615b49eed992af42354fc Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Tue, 27 Jan 2026 20:29:35 +0300 Subject: [PATCH] =?UTF-8?q?feat(pos):=20=D1=83=D0=BB=D1=83=D1=87=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20?= =?UTF-8?q?=D1=81=20=D0=B2=D0=B8=D1=82=D1=80=D0=B8=D0=BD=D0=BD=D1=8B=D0=BC?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB=D0=B5=D0=BA=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавлена кнопка редактирования комплектов с индикатором устаревшей цены - реализовано редактирование цен товаров в комплекте через inline-поля ввода - добавлена автовыбор витрины по умолчанию при создании комплекта - улучшена генерация названия комплекта по умолчанию - исправлены поля API для совместимости с сервером (kit_name, qty, name) - добавлен глобальный доступ к showcaseManager из terminal.js --- myproject/pos/static/pos/js/products.js | 41 +++++++++ myproject/pos/static/pos/js/showcase.js | 112 +++++++++++++++++++----- myproject/pos/static/pos/js/terminal.js | 1 + 3 files changed, 132 insertions(+), 22 deletions(-) diff --git a/myproject/pos/static/pos/js/products.js b/myproject/pos/static/pos/js/products.js index 3622ce6..ed127a7 100644 --- a/myproject/pos/static/pos/js/products.js +++ b/myproject/pos/static/pos/js/products.js @@ -517,6 +517,47 @@ export class ProductManager { * @private */ _renderShowcaseKitBadges(card, item, cart) { + // Кнопка редактирования (только если не заблокирован другим) + 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.title = 'Редактировать комплект'; + editBtn.onclick = (e) => { + e.stopPropagation(); + if (window.showcaseManager) { + window.showcaseManager.openEditModal(item.id); + } + }; + card.appendChild(editBtn); + + // Индикатор неактуальной цены (красный кружок) + if (item.price_outdated) { + const outdatedBadge = document.createElement('div'); + outdatedBadge.className = 'badge bg-danger'; + outdatedBadge.style.position = 'absolute'; + outdatedBadge.style.top = '5px'; + outdatedBadge.style.right = '45px'; + outdatedBadge.style.zIndex = '10'; + outdatedBadge.style.width = '18px'; + outdatedBadge.style.height = '18px'; + outdatedBadge.style.padding = '0'; + outdatedBadge.style.borderRadius = '50%'; + outdatedBadge.style.display = 'flex'; + outdatedBadge.style.alignItems = 'center'; + outdatedBadge.style.justifyContent = 'center'; + outdatedBadge.style.fontSize = '10px'; + outdatedBadge.style.minWidth = '18px'; + outdatedBadge.title = 'Цена неактуальна'; + outdatedBadge.innerHTML = '!'; + card.appendChild(outdatedBadge); + } + } + // Индикация блокировки if (item.is_locked) { const lockBadge = document.createElement('div'); diff --git a/myproject/pos/static/pos/js/showcase.js b/myproject/pos/static/pos/js/showcase.js index 62152a2..8284ed1 100644 --- a/myproject/pos/static/pos/js/showcase.js +++ b/myproject/pos/static/pos/js/showcase.js @@ -162,9 +162,9 @@ export class ShowcaseManager { const key = `product-${item.product_id}-${item.sales_unit_id || 'base'}`; this.tempCart.set(key, { id: item.product_id, - name: item.product_name, - price: item.price, - qty: item.quantity, + name: item.name || item.product_name, // Сервер отдаёт name + price: parseFloat(item.price) || 0, + qty: parseFloat(item.qty || item.quantity) || 1, // Сервер отдаёт qty type: 'product', sales_unit_id: item.sales_unit_id, unit_name: item.unit_name @@ -254,11 +254,26 @@ export class ShowcaseManager { if (!select) return; let html = ''; + let defaultShowcaseId = null; + this.showcases.forEach(showcase => { - html += ``; + const displayName = showcase.warehouse_name + ? `${showcase.name} (${showcase.warehouse_name})` + : showcase.name; + html += ``; + + // Запоминаем витрину по умолчанию + if (showcase.is_default) { + defaultShowcaseId = showcase.id; + } }); select.innerHTML = html; + + // Автовыбор витрины по умолчанию (только в режиме создания) + if (!this.isEditMode && defaultShowcaseId) { + select.value = defaultShowcaseId; + } } /** @@ -268,33 +283,83 @@ export class ShowcaseManager { const container = document.getElementById('tempKitItemsList'); if (!container) return; + container.innerHTML = ''; + if (this.tempCart.size === 0) { container.innerHTML = '

Нет товаров

'; return; } - let html = ''; let totalBasePrice = 0; this.tempCart.forEach((item, key) => { const itemTotal = item.price * item.qty; totalBasePrice += itemTotal; - html += ` -
-
-
${escapeHtml(item.name)}
-
- ${formatMoney(item.price)} × ${roundQuantity(item.qty)} - ${item.unit_name ? ' ' + item.unit_name : ''} -
-
-
${formatMoney(itemTotal)}
-
- `; - }); + const itemDiv = document.createElement('div'); + itemDiv.className = 'd-flex justify-content-between align-items-center py-1 border-bottom'; - container.innerHTML = html; + // Левая часть: название и цена + const leftDiv = document.createElement('div'); + leftDiv.className = 'flex-grow-1'; + + // Название товара + const nameDiv = document.createElement('div'); + nameDiv.className = 'small'; + nameDiv.textContent = item.name; + leftDiv.appendChild(nameDiv); + + // Контейнер цены с полем ввода + const priceContainer = document.createElement('div'); + priceContainer.className = 'text-muted d-flex align-items-center'; + priceContainer.style.fontSize = '0.75rem'; + priceContainer.style.gap = '4px'; + + // Поле ввода цены (всегда видимое, без стрелочек) + const priceInput = document.createElement('input'); + priceInput.type = 'text'; + priceInput.inputMode = 'decimal'; + priceInput.className = 'form-control form-control-sm'; + priceInput.style.width = '70px'; + priceInput.style.padding = '2px 4px'; + priceInput.style.textAlign = 'right'; + priceInput.value = formatMoney(item.price); + + // Сохранение цены при изменении + const savePrice = () => { + const newPrice = parseFloat(priceInput.value.replace(',', '.')) || 0; + if (item.price !== newPrice) { + item.price = newPrice; + this.renderTempKitItems(); + this.updatePriceCalculation(); + } + }; + + priceInput.onblur = savePrice; + priceInput.onkeydown = (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + priceInput.blur(); + } + }; + priceInput.onfocus = () => priceInput.select(); + + priceContainer.appendChild(priceInput); + priceContainer.appendChild(document.createTextNode(`× ${roundQuantity(item.qty)}`)); + if (item.unit_name) { + priceContainer.appendChild(document.createTextNode(` ${item.unit_name}`)); + } + leftDiv.appendChild(priceContainer); + + // Правая часть: итоговая сумма + const rightDiv = document.createElement('div'); + rightDiv.className = 'fw-semibold small'; + rightDiv.textContent = formatMoney(itemTotal); + + itemDiv.appendChild(leftDiv); + itemDiv.appendChild(rightDiv); + container.appendChild(itemDiv); + }); // Обновляем базовую цену document.getElementById('tempKitBasePrice').textContent = formatMoney(totalBasePrice) + ' руб.'; @@ -384,8 +449,11 @@ export class ShowcaseManager { * Сбрасывает форму */ resetForm() { - document.getElementById('tempKitName').value = ''; - document.getElementById('showcaseSelect').value = ''; + // Генерируем название по умолчанию + const randomSuffix = Math.floor(Math.random() * 900) + 100; + const defaultName = `Витринный букет ${randomSuffix}`; + document.getElementById('tempKitName').value = defaultName; + // Не сбрасываем showcaseSelect - витрина по умолчанию выбирается в renderShowcaseSelect() document.getElementById('showcaseKitQuantity').value = '1'; document.getElementById('tempKitDescription').value = ''; document.getElementById('showcaseCreatedAt').value = ''; @@ -463,7 +531,7 @@ export class ShowcaseManager { // Формируем данные для отправки const formData = new FormData(); - formData.append('name', data.name); + formData.append('kit_name', data.name); // Сервер ожидает kit_name formData.append('showcase_id', data.showcaseId); formData.append('description', data.description || ''); formData.append('showcase_created_at', data.createdAt || ''); diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js index 62e185a..1702c0e 100644 --- a/myproject/pos/static/pos/js/terminal.js +++ b/myproject/pos/static/pos/js/terminal.js @@ -157,6 +157,7 @@ document.addEventListener('DOMContentLoaded', () => { // Глобальные ссылки для обратной совместимости с cart-item-editor.js window.cart = cart; window.renderCart = renderCart; + window.showcaseManager = showcaseManager; }); // ===== РЕНДЕРИНГ КАТЕГОРИЙ =====