feat(pos): фиксировать цены товаров в витринных комплектах
- Добавлено поле KitItem.unit_price для хранения зафиксированной цены - Витринные комплекты больше не обновляются при изменении цен товаров - Добавлен красный индикатор на карточке если цена неактуальна - Добавлен warning в модалке редактирования с кнопкой "Актуализировать" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -859,6 +859,28 @@ function renderProducts() {
|
||||
openEditKitModal(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,6 +1827,7 @@ async function openEditKitModal(kitId) {
|
||||
id: item.product_id,
|
||||
name: item.name,
|
||||
price: Number(item.price),
|
||||
actual_catalog_price: item.actual_catalog_price ? Number(item.actual_catalog_price) : Number(item.price),
|
||||
qty: Number(item.qty),
|
||||
type: 'product'
|
||||
});
|
||||
@@ -1889,6 +1912,9 @@ async function openEditKitModal(kitId) {
|
||||
// Открываем модальное окно
|
||||
const modal = new bootstrap.Modal(document.getElementById('createTempKitModal'));
|
||||
modal.show();
|
||||
|
||||
// Проверяем актуальность цен (сразу после открытия)
|
||||
checkPricesActual();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading kit for edit:', error);
|
||||
@@ -1896,6 +1922,83 @@ async function openEditKitModal(kitId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка актуальности цен в витринном комплекте
|
||||
function checkPricesActual() {
|
||||
// Удаляем старый warning если есть
|
||||
const existingWarning = document.getElementById('priceOutdatedWarning');
|
||||
if (existingWarning) existingWarning.remove();
|
||||
|
||||
// Проверяем цены используя actual_catalog_price из tempCart (уже загружен с бэкенда)
|
||||
const outdatedItems = [];
|
||||
let oldTotalPrice = 0;
|
||||
let newTotalPrice = 0;
|
||||
|
||||
tempCart.forEach((item, cartKey) => {
|
||||
if (item.type === 'product' && item.actual_catalog_price !== undefined) {
|
||||
const savedPrice = parseFloat(item.price);
|
||||
const actualPrice = parseFloat(item.actual_catalog_price);
|
||||
const qty = parseFloat(item.qty) || 1;
|
||||
|
||||
if (Math.abs(savedPrice - actualPrice) > 0.01) {
|
||||
oldTotalPrice += savedPrice * qty;
|
||||
newTotalPrice += actualPrice * qty;
|
||||
outdatedItems.push({
|
||||
name: item.name,
|
||||
old: savedPrice,
|
||||
new: actualPrice,
|
||||
qty: qty
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (outdatedItems.length > 0) {
|
||||
showPriceOutdatedWarning(oldTotalPrice, newTotalPrice);
|
||||
}
|
||||
}
|
||||
|
||||
// Показать warning о неактуальных ценах
|
||||
function showPriceOutdatedWarning(oldTotalPrice, newTotalPrice) {
|
||||
const modalBody = document.querySelector('#createTempKitModal .modal-body');
|
||||
|
||||
const warning = document.createElement('div');
|
||||
warning.id = 'priceOutdatedWarning';
|
||||
warning.className = 'alert alert-warning alert-dismissible fade show d-flex align-items-start';
|
||||
warning.innerHTML = `
|
||||
<i class="bi bi-exclamation-triangle-fill flex-shrink-0 me-2 mt-1"></i>
|
||||
<div class="flex-grow-1">
|
||||
<strong>Цена неактуальна!</strong><br>
|
||||
<small class="text-muted">При сохранении комплекта было: <strong>${formatMoney(oldTotalPrice)} руб.</strong></small><br>
|
||||
<small class="text-muted">Актуальная цена сейчас: <strong>${formatMoney(newTotalPrice)} руб.</strong></small>
|
||||
<button type="button" class="btn btn-sm btn-warning mt-2" onclick="actualizeKitPrices()">
|
||||
<i class="bi bi-arrow-clockwise"></i> Пересчитать по актуальным ценам
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn-close flex-shrink-0" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
modalBody.insertBefore(warning, modalBody.firstChild);
|
||||
}
|
||||
|
||||
// Актуализировать цены в комплекте
|
||||
function actualizeKitPrices() {
|
||||
tempCart.forEach((item) => {
|
||||
if (item.type === 'product' && item.actual_catalog_price !== undefined) {
|
||||
item.price = item.actual_catalog_price;
|
||||
// Удаляем actual_catalog_price чтобы не показывался warning снова
|
||||
delete item.actual_catalog_price;
|
||||
}
|
||||
});
|
||||
|
||||
// Перерисовываем товары и пересчитываем цену
|
||||
renderTempKitItems();
|
||||
updatePriceCalculations();
|
||||
|
||||
// Убираем warning
|
||||
const warning = document.getElementById('priceOutdatedWarning');
|
||||
if (warning) warning.remove();
|
||||
}
|
||||
|
||||
// Обновление списка витринных комплектов
|
||||
async function loadShowcaseKits() {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user