diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js
index 8d94620..573fdbb 100644
--- a/myproject/pos/static/pos/js/terminal.js
+++ b/myproject/pos/static/pos/js/terminal.js
@@ -2165,11 +2165,63 @@ function renderTempKitItems() {
// Левая часть: название и цена
const leftDiv = document.createElement('div');
leftDiv.className = 'flex-grow-1';
- leftDiv.innerHTML = `
- ${item.name}
-
- ${formatMoney(item.price)} руб. / шт.
- `;
+
+ // Название товара
+ const nameSpan = document.createElement('strong');
+ nameSpan.className = 'small';
+ nameSpan.textContent = item.name;
+ leftDiv.appendChild(nameSpan);
+ leftDiv.appendChild(document.createElement('br'));
+
+ // Цена с возможностью редактирования
+ const priceContainer = document.createElement('div');
+ priceContainer.className = 'd-inline-flex align-items-center gap-1';
+
+ // Отображение цены (кликабельное)
+ const priceDisplay = document.createElement('small');
+ priceDisplay.className = 'text-muted price-display';
+ priceDisplay.style.cursor = 'pointer';
+ priceDisplay.innerHTML = `${formatMoney(item.price)} руб. / шт.`;
+ priceDisplay.title = 'Кликните для изменения цены';
+
+ // Поле ввода (скрыто по умолчанию)
+ const priceInput = document.createElement('input');
+ priceInput.type = 'number';
+ priceInput.step = '0.01';
+ priceInput.className = 'form-control form-control-sm';
+ priceInput.style.width = '80px';
+ priceInput.style.display = 'none';
+ priceInput.value = item.price;
+
+ // Клик на цену — показать input
+ priceDisplay.onclick = () => {
+ priceDisplay.style.display = 'none';
+ priceInput.style.display = 'inline-block';
+ priceInput.focus();
+ priceInput.select();
+ };
+
+ // Потеря фокуса или Enter — сохранить и скрыть input
+ const savePrice = () => {
+ const newPrice = parseFloat(priceInput.value) || 0;
+ item.price = newPrice;
+ priceDisplay.innerHTML = `${formatMoney(newPrice)} руб. / шт.`;
+ priceInput.style.display = 'none';
+ priceDisplay.style.display = 'inline';
+ renderTempKitItems(); // Пересчёт итогов
+ };
+
+ priceInput.onblur = savePrice;
+ priceInput.onkeydown = (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ savePrice();
+ }
+ };
+
+ priceContainer.appendChild(priceInput);
+ priceContainer.appendChild(priceDisplay);
+ leftDiv.appendChild(priceContainer);
// Правая часть: контролы количества и удаление
const rightDiv = document.createElement('div');
diff --git a/myproject/pos/views.py b/myproject/pos/views.py
index cdd39bf..b227d74 100644
--- a/myproject/pos/views.py
+++ b/myproject/pos/views.py
@@ -1371,12 +1371,19 @@ def update_product_kit(request, kit_id):
if len(products) != len(product_ids):
return JsonResponse({'success': False, 'error': 'Некоторые товары не найдены'}, status=400)
- # Агрегируем количества
+ # Агрегируем количества и цены
aggregated_items = {}
for item in items:
product_id = item['product_id']
quantity = Decimal(str(item['quantity']))
- aggregated_items[product_id] = aggregated_items.get(product_id, Decimal('0')) + quantity
+ unit_price = item.get('unit_price')
+ if product_id in aggregated_items:
+ aggregated_items[product_id]['quantity'] += quantity
+ else:
+ aggregated_items[product_id] = {
+ 'quantity': quantity,
+ 'unit_price': Decimal(str(unit_price)) if unit_price is not None else None
+ }
with transaction.atomic():
# Получаем старый состав для сравнения
@@ -1397,7 +1404,7 @@ def update_product_kit(request, kit_id):
for product_id in all_product_ids:
old_qty = old_items.get(product_id, Decimal('0'))
- new_qty = aggregated_items.get(product_id, Decimal('0'))
+ new_qty = aggregated_items.get(product_id, {}).get('quantity', Decimal('0'))
diff = new_qty - old_qty
if diff > 0 and showcase:
@@ -1436,13 +1443,15 @@ def update_product_kit(request, kit_id):
# Обновляем состав
kit.kit_items.all().delete()
- for product_id, quantity in aggregated_items.items():
+ for product_id, item_data in aggregated_items.items():
product = products[product_id]
+ # Используем переданную цену, если есть, иначе актуальную из каталога
+ final_price = item_data['unit_price'] if item_data['unit_price'] is not None else product.actual_price
KitItem.objects.create(
kit=kit,
product=product,
- quantity=quantity,
- unit_price=product.actual_price # Фиксируем актуальную цену
+ quantity=item_data['quantity'],
+ unit_price=final_price
)
kit.recalculate_base_price()
diff --git a/myproject/products/templates/products/productkit_edit.html b/myproject/products/templates/products/productkit_edit.html
index 033e13c..ccf91c6 100644
--- a/myproject/products/templates/products/productkit_edit.html
+++ b/myproject/products/templates/products/productkit_edit.html
@@ -748,6 +748,16 @@
// Кэш цен товаров для быстрого доступа
const priceCache = {};
+ function parsePrice(value) {
+ if (value === null || value === undefined) {
+ return 0;
+ }
+ if (typeof value === 'string') {
+ return parseFloat(value.replace(',', '.')) || 0;
+ }
+ return parseFloat(value) || 0;
+ }
+
// Функция для получения цены товара с AJAX если необходимо
async function getProductPrice(selectElement) {
// Строгая проверка: нужен валидный element с value
@@ -765,7 +775,7 @@
// Если уже загружена в кэш - возвращаем
if (priceCache[productId] !== undefined) {
- const cachedPrice = parseFloat(priceCache[productId]) || 0;
+ const cachedPrice = parsePrice(priceCache[productId]);
console.log('getProductPrice: from cache', productId, cachedPrice);
return cachedPrice;
}
@@ -776,7 +786,7 @@
const formPrice = form.getAttribute('data-product-price');
const formProductId = form.getAttribute('data-product-id');
if (formPrice && productId.toString() === formProductId) {
- const price = parseFloat(formPrice) || 0;
+ const price = parsePrice(formPrice);
if (price > 0) {
priceCache[productId] = price;
console.log('getProductPrice: from form data', productId, price);
@@ -789,7 +799,7 @@
const selectedOption = $(selectElement).find('option:selected');
let priceData = selectedOption.data('actual_price') || selectedOption.data('price');
if (priceData) {
- const price = parseFloat(priceData) || 0;
+ const price = parsePrice(priceData);
if (price > 0) {
priceCache[productId] = price;
console.log('getProductPrice: from select2 data', productId, price);
@@ -808,7 +818,7 @@
const data = await response.json();
if (data.results && data.results.length > 0) {
const productData = data.results[0];
- const price = parseFloat(productData.actual_price || productData.price || 0);
+ const price = parsePrice(productData.actual_price || productData.price || 0);
if (price > 0) {
priceCache[productId] = price;
console.log('getProductPrice: from API', productId, price);
@@ -868,7 +878,7 @@
// Если уже загружена в кэш - возвращаем
const cacheKey = `variant_${variantGroupId}`;
if (priceCache[cacheKey] !== undefined) {
- const cachedPrice = parseFloat(priceCache[cacheKey]) || 0;
+ const cachedPrice = parsePrice(priceCache[cacheKey]);
console.log('getVariantGroupPrice: from cache', variantGroupId, cachedPrice);
return cachedPrice;
}
@@ -877,7 +887,7 @@
const selectedOption = $(selectElement).find('option:selected');
let priceData = selectedOption.data('actual_price') || selectedOption.data('price');
if (priceData) {
- const price = parseFloat(priceData) || 0;
+ const price = parsePrice(priceData);
if (price > 0) {
priceCache[cacheKey] = price;
console.log('getVariantGroupPrice: from select2 data', variantGroupId, price);
@@ -896,7 +906,7 @@
const data = await response.json();
if (data.results && data.results.length > 0) {
const variantData = data.results[0];
- const price = parseFloat(variantData.actual_price || variantData.price || 0);
+ const price = parsePrice(variantData.actual_price || variantData.price || 0);
if (price > 0) {
priceCache[cacheKey] = price;
console.log('getVariantGroupPrice: from API', variantGroupId, price);
@@ -940,7 +950,7 @@
// Если уже загружена в кэш - возвращаем
const cacheKey = `sales_unit_${salesUnitId}`;
if (priceCache[cacheKey] !== undefined) {
- const cachedPrice = parseFloat(priceCache[cacheKey]) || 0;
+ const cachedPrice = parsePrice(priceCache[cacheKey]);
return cachedPrice;
}
@@ -949,7 +959,7 @@
if (selectedOption) {
let priceData = selectedOption.dataset.actual_price || selectedOption.dataset.price;
if (priceData) {
- const price = parseFloat(priceData) || 0;
+ const price = parsePrice(priceData);
if (price > 0) {
priceCache[cacheKey] = price;
console.log('getSalesUnitPrice: from standard select option data', salesUnitId, price);
@@ -966,7 +976,7 @@
const itemData = selectedData[0];
const priceData = itemData.actual_price || itemData.price;
if (priceData) {
- const price = parseFloat(priceData) || 0;
+ const price = parsePrice(priceData);
if (price > 0) {
priceCache[cacheKey] = price;
console.log('getSalesUnitPrice: from select2 data', salesUnitId, price);
@@ -988,7 +998,7 @@
if (data.sales_units && data.sales_units.length > 0) {
const salesUnitData = data.sales_units.find(su => su.id == salesUnitId);
if (salesUnitData) {
- const price = parseFloat(salesUnitData.actual_price || salesUnitData.price || 0);
+ const price = parsePrice(salesUnitData.actual_price || salesUnitData.price || 0);
if (price > 0) {
priceCache[cacheKey] = price;
console.log('getSalesUnitPrice: from API', salesUnitId, price);