feat(pos): добавить редактирование цены товара в корзине
- Добавить модалку редактирования товара в корзине (edit_cart_item_modal.html) - Создать JS модуль cart-item-editor.js для логики редактирования - При клике на строку товара открывается модалка с возможностью изменения цены и количества - Добавить визуальную индикацию изменённой цены (оранжевый цвет и звёздочка) - Экспортировать корзину в window.cart для доступа из других модулей - Добавить авто-выделение текста при фокусе в полях ввода Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -872,3 +872,35 @@ body {
|
||||
.mobile-cart-actions .dropdown-item i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
ИНТЕРАКТИВНОСТЬ СТРОКИ КОРЗИНЫ (редактирование товара)
|
||||
============================================================ */
|
||||
|
||||
/* Интерактивность строки корзины при наведении */
|
||||
.cart-item {
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.cart-item:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
border-radius: 4px;
|
||||
padding-left: 0.5rem !important;
|
||||
padding-right: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Исключаем hover для витринных комплектов - они сохраняют свой фон */
|
||||
.cart-item[style*="background-color"]:hover {
|
||||
background-color: #ffe6a0 !important; /* чуть светлее желтого */
|
||||
}
|
||||
|
||||
/* Индикатор изменённой цены */
|
||||
.cart-item.price-overridden .item-name-price .text-muted {
|
||||
color: #f59e0b !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cart-item.price-overridden .item-name-price .text-muted::after {
|
||||
content: ' *';
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
194
myproject/pos/static/pos/js/cart-item-editor.js
Normal file
194
myproject/pos/static/pos/js/cart-item-editor.js
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Модуль редактирования товара в корзине POS-терминала
|
||||
* Отвечает за открытие модалки и сохранение изменений
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
let editingCartKey = null;
|
||||
let basePrice = 0;
|
||||
|
||||
/**
|
||||
* Округление цены до 2 знаков
|
||||
*/
|
||||
function roundPrice(value) {
|
||||
if (value === null || value === undefined || isNaN(value)) return '0.00';
|
||||
return (Number(value)).toFixed(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Открытие модалки редактирования
|
||||
* @param {string} cartKey - ключ товара в корзине
|
||||
*/
|
||||
function openModal(cartKey) {
|
||||
const item = window.cart?.get(cartKey);
|
||||
if (!item) {
|
||||
console.error('CartItemEditor: Item not found for key:', cartKey);
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем наличие модалки
|
||||
const modalEl = document.getElementById('editCartItemModal');
|
||||
if (!modalEl) {
|
||||
console.error('CartItemEditor: Modal element not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
editingCartKey = cartKey;
|
||||
basePrice = parseFloat(item.price) || 0;
|
||||
|
||||
// Заполнение полей
|
||||
document.getElementById('editModalProductName').textContent = item.name || '—';
|
||||
|
||||
// Используем formatMoney из terminal.js
|
||||
const fmtMoney = typeof formatMoney === 'function' ? formatMoney : (v) => Number(v).toFixed(2);
|
||||
document.getElementById('editModalBasePrice').textContent = fmtMoney(basePrice) + ' руб.';
|
||||
|
||||
document.getElementById('editModalPrice').value = roundPrice(basePrice);
|
||||
document.getElementById('editModalQuantity').value = item.qty || 1;
|
||||
|
||||
// Бейдж единицы измерения
|
||||
const unitBadge = document.getElementById('editModalUnitBadge');
|
||||
if (item.unit_name) {
|
||||
unitBadge.textContent = item.unit_name;
|
||||
unitBadge.style.display = 'inline-block';
|
||||
} else {
|
||||
unitBadge.style.display = 'none';
|
||||
}
|
||||
|
||||
updateTotal();
|
||||
|
||||
// Показ модалки
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
modal.show();
|
||||
|
||||
console.log('CartItemEditor: Modal opened for', item.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление суммы в модалке
|
||||
*/
|
||||
function updateTotal() {
|
||||
const price = parseFloat(document.getElementById('editModalPrice').value) || 0;
|
||||
const qty = parseFloat(document.getElementById('editModalQuantity').value) || 0;
|
||||
|
||||
const fmtMoney = typeof formatMoney === 'function' ? formatMoney : (v) => Number(v).toFixed(2);
|
||||
document.getElementById('editModalTotal').textContent = fmtMoney(price * qty) + ' руб.';
|
||||
|
||||
// Индикатор изменения цены
|
||||
const warning = document.getElementById('editModalPriceWarning');
|
||||
if (Math.abs(price - basePrice) > 0.01) {
|
||||
warning.style.display = 'block';
|
||||
} else {
|
||||
warning.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение изменений
|
||||
*/
|
||||
function saveChanges() {
|
||||
if (!editingCartKey) return;
|
||||
|
||||
const newPrice = parseFloat(document.getElementById('editModalPrice').value) || 0;
|
||||
const newQty = parseFloat(document.getElementById('editModalQuantity').value) || 1;
|
||||
|
||||
const item = window.cart?.get(editingCartKey);
|
||||
if (item) {
|
||||
// Используем roundQuantity из terminal.js
|
||||
const rndQty = typeof roundQuantity === 'function' ? roundQuantity : (v, d) => Math.round(v * Math.pow(10, d)) / Math.pow(10, d);
|
||||
|
||||
item.price = newPrice;
|
||||
item.qty = rndQty(newQty, 3);
|
||||
item.price_overridden = Math.abs(newPrice - basePrice) > 0.01;
|
||||
|
||||
window.cart.set(editingCartKey, item);
|
||||
|
||||
// Перерисовка корзины
|
||||
if (typeof renderCart === 'function') {
|
||||
renderCart();
|
||||
}
|
||||
|
||||
// Сохранение на сервере
|
||||
if (typeof saveCartToServer === 'function') {
|
||||
saveCartToServer();
|
||||
}
|
||||
|
||||
console.log('CartItemEditor: Changes saved for', item.name);
|
||||
}
|
||||
|
||||
// Закрытие модалки
|
||||
const modalEl = document.getElementById('editCartItemModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modal) modal.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Сброс состояния модалки
|
||||
*/
|
||||
function reset() {
|
||||
editingCartKey = null;
|
||||
basePrice = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация модуля
|
||||
*/
|
||||
function init() {
|
||||
const priceInput = document.getElementById('editModalPrice');
|
||||
const qtyInput = document.getElementById('editModalQuantity');
|
||||
const confirmBtn = document.getElementById('confirmEditCartItem');
|
||||
|
||||
if (!priceInput || !confirmBtn) {
|
||||
console.warn('CartItemEditor: Required elements not found, deferring init...');
|
||||
// Повторная попытка через короткое время
|
||||
setTimeout(init, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('CartItemEditor: Initialized successfully');
|
||||
|
||||
// Обновление суммы при изменении полей
|
||||
priceInput.addEventListener('input', updateTotal);
|
||||
qtyInput.addEventListener('input', updateTotal);
|
||||
|
||||
// Авто-выделение всего текста при фокусе
|
||||
priceInput.addEventListener('focus', function() {
|
||||
this.select();
|
||||
});
|
||||
qtyInput.addEventListener('focus', function() {
|
||||
this.select();
|
||||
});
|
||||
|
||||
// Кнопка сохранения
|
||||
confirmBtn.addEventListener('click', saveChanges);
|
||||
|
||||
// Сброс при закрытии модалки
|
||||
const modalEl = document.getElementById('editCartItemModal');
|
||||
modalEl.addEventListener('hidden.bs.modal', reset);
|
||||
|
||||
// Enter для сохранения
|
||||
priceInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') saveChanges();
|
||||
});
|
||||
qtyInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') saveChanges();
|
||||
});
|
||||
}
|
||||
|
||||
// Экспорт функций для использования из terminal.js
|
||||
window.CartItemEditor = {
|
||||
openModal: openModal,
|
||||
init: init
|
||||
};
|
||||
|
||||
console.log('CartItemEditor: Module loaded');
|
||||
|
||||
// Автоинициализация при загрузке
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
@@ -19,6 +19,8 @@ let showcaseKits = JSON.parse(document.getElementById('showcaseKitsData').textCo
|
||||
let currentCategoryId = null;
|
||||
let isShowcaseView = false;
|
||||
const cart = new Map();
|
||||
// Экспорт корзины для использования в других модулях
|
||||
window.cart = cart;
|
||||
|
||||
// Переменные для пагинации
|
||||
let currentPage = 1;
|
||||
@@ -1270,6 +1272,13 @@ function renderCart() {
|
||||
cart.forEach((item, cartKey) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'cart-item mb-2';
|
||||
row.style.cursor = 'pointer';
|
||||
row.title = 'Нажмите для редактирования';
|
||||
|
||||
// Индикатор изменённой цены
|
||||
if (item.price_overridden) {
|
||||
row.classList.add('price-overridden');
|
||||
}
|
||||
|
||||
// СПЕЦИАЛЬНАЯ СТИЛИЗАЦИЯ для витринных комплектов
|
||||
const isShowcaseKit = item.type === 'showcase_kit';
|
||||
@@ -1416,7 +1425,21 @@ function renderCart() {
|
||||
row.appendChild(qtyControl);
|
||||
row.appendChild(itemTotal);
|
||||
row.appendChild(deleteBtn);
|
||||
|
||||
|
||||
// Обработчик клика для редактирования товара
|
||||
row.addEventListener('click', function(e) {
|
||||
// Игнорируем клики на кнопки управления количеством и удаления
|
||||
if (e.target.closest('button') || e.target.closest('input')) {
|
||||
return;
|
||||
}
|
||||
console.log('Cart row clicked, cartKey:', cartKey, 'CartItemEditor:', typeof window.CartItemEditor);
|
||||
if (window.CartItemEditor) {
|
||||
window.CartItemEditor.openModal(cartKey);
|
||||
} else {
|
||||
console.error('CartItemEditor not available!');
|
||||
}
|
||||
});
|
||||
|
||||
list.appendChild(row);
|
||||
|
||||
total += item.qty * item.price;
|
||||
|
||||
Reference in New Issue
Block a user