feat(pos): улучшена работа с витринными комплектами
- добавлена кнопка редактирования комплектов с индикатором устаревшей цены - реализовано редактирование цен товаров в комплекте через inline-поля ввода - добавлена автовыбор витрины по умолчанию при создании комплекта - улучшена генерация названия комплекта по умолчанию - исправлены поля API для совместимости с сервером (kit_name, qty, name) - добавлен глобальный доступ к showcaseManager из terminal.js
This commit is contained in:
@@ -517,6 +517,47 @@ export class ProductManager {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_renderShowcaseKitBadges(card, item, cart) {
|
_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 = '<i class="bi bi-pencil"></i>';
|
||||||
|
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) {
|
if (item.is_locked) {
|
||||||
const lockBadge = document.createElement('div');
|
const lockBadge = document.createElement('div');
|
||||||
|
|||||||
@@ -162,9 +162,9 @@ export class ShowcaseManager {
|
|||||||
const key = `product-${item.product_id}-${item.sales_unit_id || 'base'}`;
|
const key = `product-${item.product_id}-${item.sales_unit_id || 'base'}`;
|
||||||
this.tempCart.set(key, {
|
this.tempCart.set(key, {
|
||||||
id: item.product_id,
|
id: item.product_id,
|
||||||
name: item.product_name,
|
name: item.name || item.product_name, // Сервер отдаёт name
|
||||||
price: item.price,
|
price: parseFloat(item.price) || 0,
|
||||||
qty: item.quantity,
|
qty: parseFloat(item.qty || item.quantity) || 1, // Сервер отдаёт qty
|
||||||
type: 'product',
|
type: 'product',
|
||||||
sales_unit_id: item.sales_unit_id,
|
sales_unit_id: item.sales_unit_id,
|
||||||
unit_name: item.unit_name
|
unit_name: item.unit_name
|
||||||
@@ -254,11 +254,26 @@ export class ShowcaseManager {
|
|||||||
if (!select) return;
|
if (!select) return;
|
||||||
|
|
||||||
let html = '<option value="">Выберите витрину...</option>';
|
let html = '<option value="">Выберите витрину...</option>';
|
||||||
|
let defaultShowcaseId = null;
|
||||||
|
|
||||||
this.showcases.forEach(showcase => {
|
this.showcases.forEach(showcase => {
|
||||||
html += `<option value="${showcase.id}">${escapeHtml(showcase.name)}</option>`;
|
const displayName = showcase.warehouse_name
|
||||||
|
? `${showcase.name} (${showcase.warehouse_name})`
|
||||||
|
: showcase.name;
|
||||||
|
html += `<option value="${showcase.id}">${escapeHtml(displayName)}</option>`;
|
||||||
|
|
||||||
|
// Запоминаем витрину по умолчанию
|
||||||
|
if (showcase.is_default) {
|
||||||
|
defaultShowcaseId = showcase.id;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
select.innerHTML = html;
|
select.innerHTML = html;
|
||||||
|
|
||||||
|
// Автовыбор витрины по умолчанию (только в режиме создания)
|
||||||
|
if (!this.isEditMode && defaultShowcaseId) {
|
||||||
|
select.value = defaultShowcaseId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,33 +283,83 @@ export class ShowcaseManager {
|
|||||||
const container = document.getElementById('tempKitItemsList');
|
const container = document.getElementById('tempKitItemsList');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
if (this.tempCart.size === 0) {
|
if (this.tempCart.size === 0) {
|
||||||
container.innerHTML = '<p class="text-muted text-center mb-0">Нет товаров</p>';
|
container.innerHTML = '<p class="text-muted text-center mb-0">Нет товаров</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = '';
|
|
||||||
let totalBasePrice = 0;
|
let totalBasePrice = 0;
|
||||||
|
|
||||||
this.tempCart.forEach((item, key) => {
|
this.tempCart.forEach((item, key) => {
|
||||||
const itemTotal = item.price * item.qty;
|
const itemTotal = item.price * item.qty;
|
||||||
totalBasePrice += itemTotal;
|
totalBasePrice += itemTotal;
|
||||||
|
|
||||||
html += `
|
const itemDiv = document.createElement('div');
|
||||||
<div class="d-flex justify-content-between align-items-center py-1 border-bottom">
|
itemDiv.className = 'd-flex justify-content-between align-items-center py-1 border-bottom';
|
||||||
<div class="flex-grow-1">
|
|
||||||
<div class="small">${escapeHtml(item.name)}</div>
|
|
||||||
<div class="text-muted" style="font-size: 0.75rem;">
|
|
||||||
${formatMoney(item.price)} × ${roundQuantity(item.qty)}
|
|
||||||
${item.unit_name ? ' ' + item.unit_name : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fw-semibold small">${formatMoney(itemTotal)}</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
|
|
||||||
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) + ' руб.';
|
document.getElementById('tempKitBasePrice').textContent = formatMoney(totalBasePrice) + ' руб.';
|
||||||
@@ -384,8 +449,11 @@ export class ShowcaseManager {
|
|||||||
* Сбрасывает форму
|
* Сбрасывает форму
|
||||||
*/
|
*/
|
||||||
resetForm() {
|
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('showcaseKitQuantity').value = '1';
|
||||||
document.getElementById('tempKitDescription').value = '';
|
document.getElementById('tempKitDescription').value = '';
|
||||||
document.getElementById('showcaseCreatedAt').value = '';
|
document.getElementById('showcaseCreatedAt').value = '';
|
||||||
@@ -463,7 +531,7 @@ export class ShowcaseManager {
|
|||||||
|
|
||||||
// Формируем данные для отправки
|
// Формируем данные для отправки
|
||||||
const formData = new FormData();
|
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('showcase_id', data.showcaseId);
|
||||||
formData.append('description', data.description || '');
|
formData.append('description', data.description || '');
|
||||||
formData.append('showcase_created_at', data.createdAt || '');
|
formData.append('showcase_created_at', data.createdAt || '');
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// Глобальные ссылки для обратной совместимости с cart-item-editor.js
|
// Глобальные ссылки для обратной совместимости с cart-item-editor.js
|
||||||
window.cart = cart;
|
window.cart = cart;
|
||||||
window.renderCart = renderCart;
|
window.renderCart = renderCart;
|
||||||
|
window.showcaseManager = showcaseManager;
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== РЕНДЕРИНГ КАТЕГОРИЙ =====
|
// ===== РЕНДЕРИНГ КАТЕГОРИЙ =====
|
||||||
|
|||||||
Reference in New Issue
Block a user