diff --git a/myproject/orders/services/draft_service.py b/myproject/orders/services/draft_service.py
index 7e60dd3..920d6c8 100644
--- a/myproject/orders/services/draft_service.py
+++ b/myproject/orders/services/draft_service.py
@@ -64,7 +64,7 @@ class DraftOrderService:
@staticmethod
def update_draft(order_id, user, data):
"""
- Обновляет существующий черновик заказа.
+ Обновляет существующий заказ (автосохранение).
Args:
order_id (int): ID заказа
@@ -76,14 +76,11 @@ class DraftOrderService:
Raises:
Order.DoesNotExist: Если заказ не найден
- ValidationError: Если заказ не является черновиком или данные невалидны
+ ValidationError: Если данные невалидны
"""
with transaction.atomic():
order = Order.objects.select_for_update().get(pk=order_id)
- if not order.is_draft():
- raise ValidationError("Можно обновлять только черновики заказов")
-
# Обновляем только переданные поля
# ForeignKey поля требуют специальной обработки
fk_fields = {
diff --git a/myproject/orders/static/orders/js/autosave.js b/myproject/orders/static/orders/js/autosave.js
index f985a29..dfd567b 100644
--- a/myproject/orders/static/orders/js/autosave.js
+++ b/myproject/orders/static/orders/js/autosave.js
@@ -18,7 +18,6 @@
// Состояние модуля
let autosaveTimer = null;
let isAutosaving = false;
- let isDraft = false;
let orderId = null;
/**
@@ -43,12 +42,6 @@
}
orderId = urlMatch[1];
- // Проверяем, является ли заказ черновиком
- isDraft = checkIfDraft();
- if (!isDraft) {
- return;
- }
-
// Инициализируем UI индикатора
initStatusIndicator();
@@ -56,25 +49,6 @@
attachEventListeners();
}
- /**
- * Проверяет, является ли заказ черновиком
- */
- function checkIfDraft() {
- // Проверяем через data-атрибут на форме
- const form = document.getElementById('order-form');
- if (form && form.dataset.isDraft === 'true') {
- return true;
- }
-
- // Проверяем через заголовок страницы
- const title = document.querySelector('h1');
- if (title && title.textContent.includes('черновик')) {
- return true;
- }
-
- return false;
- }
-
/**
* Создает индикатор статуса автосохранения
*/
diff --git a/myproject/orders/templates/orders/order_form.html b/myproject/orders/templates/orders/order_form.html
index 213c2a3..3363462 100644
--- a/myproject/orders/templates/orders/order_form.html
+++ b/myproject/orders/templates/orders/order_form.html
@@ -102,15 +102,6 @@
{{ title }}
-
-
-
- {{ form.status }}
- {% if form.status.errors %}
-
{{ form.status.errors }}
- {% endif %}
-
-
+
+
+
+ {{ form.status }}
+ {% if form.status.errors %}
+
{{ form.status.errors }}
+ {% endif %}
+
+
@@ -1674,12 +1676,12 @@ if (!document.getElementById('notification-styles')) {
-
-{% if is_draft %}
-
+
+{% if order %}
+
{% else %}
-
+
{% endif %}
{% endblock %}
diff --git a/myproject/orders/views.py b/myproject/orders/views.py
index 99e7528..91e3281 100644
--- a/myproject/orders/views.py
+++ b/myproject/orders/views.py
@@ -252,13 +252,6 @@ def autosave_draft_order(request, pk):
'error': 'Заказ не найден'
}, status=404)
- # Проверяем, что это черновик
- if not order.is_draft():
- return JsonResponse({
- 'success': False,
- 'error': 'Можно автосохранять только черновики'
- }, status=400)
-
# Используем DraftOrderService для обновления
order = DraftOrderService.update_draft(
order_id=pk,
diff --git a/myproject/products/models/kits.py b/myproject/products/models/kits.py
index ea4b7c5..733412a 100644
--- a/myproject/products/models/kits.py
+++ b/myproject/products/models/kits.py
@@ -135,6 +135,10 @@ class ProductKit(BaseProductEntity):
actual_price = item.product.actual_price or Decimal('0')
qty = item.quantity or Decimal('1')
total += actual_price * qty
+ elif item.variant_group:
+ actual_price = item.variant_group.price or Decimal('0')
+ qty = item.quantity or Decimal('1')
+ total += actual_price * qty
self.base_price = total
# Обновляем финальную цену
@@ -178,6 +182,10 @@ class ProductKit(BaseProductEntity):
actual_price = item.product.actual_price or Decimal('0')
qty = item.quantity or Decimal('1')
total += actual_price * qty
+ elif item.variant_group:
+ actual_price = item.variant_group.price or Decimal('0')
+ qty = item.quantity or Decimal('1')
+ total += actual_price * qty
self.base_price = total
# Устанавливаем финальную цену в поле price
diff --git a/myproject/products/templates/products/includes/select2-product-init.html b/myproject/products/templates/products/includes/select2-product-init.html
index b5b1760..c6ea803 100644
--- a/myproject/products/templates/products/includes/select2-product-init.html
+++ b/myproject/products/templates/products/includes/select2-product-init.html
@@ -21,6 +21,16 @@
function formatSelectSelection(item) {
if (!item.id) return item.text;
+ // Сохраняем данные о цене в атрибуты DOM элемента для доступа через jQuery .data()
+ if (item.element) {
+ if (item.price !== undefined) {
+ $(item.element).attr('data-price', item.price);
+ }
+ if (item.actual_price !== undefined) {
+ $(item.element).attr('data-actual_price', item.actual_price);
+ }
+ }
+
// Показываем только текст при выборе, цена будет обновляться в JavaScript
return item.text || item.id;
}
diff --git a/myproject/products/templates/products/productkit_create.html b/myproject/products/templates/products/productkit_create.html
index 6343dde..72ec6fe 100644
--- a/myproject/products/templates/products/productkit_create.html
+++ b/myproject/products/templates/products/productkit_create.html
@@ -545,6 +545,67 @@ document.addEventListener('DOMContentLoaded', function() {
calculateFinalPrice();
});
+ // Функция для получения цены группы вариантов
+ async function getVariantGroupPrice(selectElement) {
+ if (!selectElement) {
+ console.warn('getVariantGroupPrice: selectElement is null or undefined');
+ return 0;
+ }
+
+ const variantGroupId = parseInt(selectElement.value);
+
+ if (!selectElement.value || isNaN(variantGroupId) || variantGroupId <= 0) {
+ console.warn('getVariantGroupPrice: no valid variant group id', selectElement.value);
+ return 0;
+ }
+
+ // Если уже загружена в кэш - возвращаем
+ const cacheKey = `variant_${variantGroupId}`;
+ if (priceCache[cacheKey] !== undefined) {
+ const cachedPrice = parseFloat(priceCache[cacheKey]) || 0;
+ console.log('getVariantGroupPrice: from cache', variantGroupId, cachedPrice);
+ return cachedPrice;
+ }
+
+ // Пытаемся получить из Select2 option data
+ const selectedOption = $(selectElement).find('option:selected');
+ let priceData = selectedOption.data('actual_price') || selectedOption.data('price');
+ if (priceData) {
+ const price = parseFloat(priceData) || 0;
+ if (price > 0) {
+ priceCache[cacheKey] = price;
+ console.log('getVariantGroupPrice: from select2 data', variantGroupId, price);
+ return price;
+ }
+ }
+
+ // Если не нашли - загружаем через AJAX
+ try {
+ console.log('getVariantGroupPrice: fetching from API', variantGroupId);
+ const response = await fetch(
+ `{% url "products:api-search-products-variants" %}?id=variant_${variantGroupId}`,
+ { method: 'GET', headers: { 'Accept': 'application/json' } }
+ );
+ if (response.ok) {
+ 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);
+ if (price > 0) {
+ priceCache[cacheKey] = price;
+ console.log('getVariantGroupPrice: from API', variantGroupId, price);
+ }
+ return price;
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching variant group price:', error);
+ }
+
+ console.warn('getVariantGroupPrice: returning 0 for variant group', variantGroupId);
+ return 0;
+ }
+
// Функция для расчета финальной цены
async function calculateFinalPrice() {
// Получаем базовую цену (сумма всех компонентов)
@@ -555,21 +616,28 @@ document.addEventListener('DOMContentLoaded', function() {
const forms = formsContainer.querySelectorAll('.kititem-form');
for (const form of forms) {
const productSelect = form.querySelector('[name$="-product"]');
+ const variantGroupSelect = form.querySelector('[name$="-variant_group"]');
const quantity = parseFloat(form.querySelector('[name$="-quantity"]')?.value || '1');
const deleteCheckbox = form.querySelector('[name$="-DELETE"]');
if (deleteCheckbox && deleteCheckbox.checked) continue;
- // Пропускаем если товар не выбран
- if (!productSelect || !productSelect.value) continue;
-
// Пропускаем если количество не валидно
const validQuantity = quantity > 0 ? quantity : 1;
- // Получаем цену товара (асинхронно)
- const productPrice = await getProductPrice(productSelect);
- if (productPrice > 0) {
- newBasePrice += (productPrice * validQuantity);
+ // Проверяем товар
+ if (productSelect && productSelect.value) {
+ const productPrice = await getProductPrice(productSelect);
+ if (productPrice > 0) {
+ newBasePrice += (productPrice * validQuantity);
+ }
+ }
+ // Проверяем группу вариантов
+ else if (variantGroupSelect && variantGroupSelect.value) {
+ const variantPrice = await getVariantGroupPrice(variantGroupSelect);
+ if (variantPrice > 0) {
+ newBasePrice += (variantPrice * validQuantity);
+ }
}
}
}
diff --git a/myproject/products/templates/products/productkit_edit.html b/myproject/products/templates/products/productkit_edit.html
index 98e9fe1..f579276 100644
--- a/myproject/products/templates/products/productkit_edit.html
+++ b/myproject/products/templates/products/productkit_edit.html
@@ -526,6 +526,67 @@ document.addEventListener('DOMContentLoaded', function() {
calculateFinalPrice();
});
+ // Функция для получения цены группы вариантов
+ async function getVariantGroupPrice(selectElement) {
+ if (!selectElement) {
+ console.warn('getVariantGroupPrice: selectElement is null or undefined');
+ return 0;
+ }
+
+ const variantGroupId = parseInt(selectElement.value);
+
+ if (!selectElement.value || isNaN(variantGroupId) || variantGroupId <= 0) {
+ console.warn('getVariantGroupPrice: no valid variant group id', selectElement.value);
+ return 0;
+ }
+
+ // Если уже загружена в кэш - возвращаем
+ const cacheKey = `variant_${variantGroupId}`;
+ if (priceCache[cacheKey] !== undefined) {
+ const cachedPrice = parseFloat(priceCache[cacheKey]) || 0;
+ console.log('getVariantGroupPrice: from cache', variantGroupId, cachedPrice);
+ return cachedPrice;
+ }
+
+ // Пытаемся получить из Select2 option data
+ const selectedOption = $(selectElement).find('option:selected');
+ let priceData = selectedOption.data('actual_price') || selectedOption.data('price');
+ if (priceData) {
+ const price = parseFloat(priceData) || 0;
+ if (price > 0) {
+ priceCache[cacheKey] = price;
+ console.log('getVariantGroupPrice: from select2 data', variantGroupId, price);
+ return price;
+ }
+ }
+
+ // Если не нашли - загружаем через AJAX
+ try {
+ console.log('getVariantGroupPrice: fetching from API', variantGroupId);
+ const response = await fetch(
+ `{% url "products:api-search-products-variants" %}?id=variant_${variantGroupId}`,
+ { method: 'GET', headers: { 'Accept': 'application/json' } }
+ );
+ if (response.ok) {
+ 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);
+ if (price > 0) {
+ priceCache[cacheKey] = price;
+ console.log('getVariantGroupPrice: from API', variantGroupId, price);
+ }
+ return price;
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching variant group price:', error);
+ }
+
+ console.warn('getVariantGroupPrice: returning 0 for variant group', variantGroupId);
+ return 0;
+ }
+
// Функция для расчета финальной цены
async function calculateFinalPrice() {
// Получаем базовую цену (сумма всех компонентов)
@@ -536,21 +597,28 @@ document.addEventListener('DOMContentLoaded', function() {
const forms = formsContainer.querySelectorAll('.kititem-form');
for (const form of forms) {
const productSelect = form.querySelector('[name$="-product"]');
+ const variantGroupSelect = form.querySelector('[name$="-variant_group"]');
const quantity = parseFloat(form.querySelector('[name$="-quantity"]')?.value || '1');
const deleteCheckbox = form.querySelector('[name$="-DELETE"]');
if (deleteCheckbox && deleteCheckbox.checked) continue;
- // Пропускаем если товар не выбран
- if (!productSelect || !productSelect.value) continue;
-
// Пропускаем если количество не валидно
const validQuantity = quantity > 0 ? quantity : 1;
- // Получаем цену товара (асинхронно)
- const productPrice = await getProductPrice(productSelect);
- if (productPrice > 0) {
- newBasePrice += (productPrice * validQuantity);
+ // Проверяем товар
+ if (productSelect && productSelect.value) {
+ const productPrice = await getProductPrice(productSelect);
+ if (productPrice > 0) {
+ newBasePrice += (productPrice * validQuantity);
+ }
+ }
+ // Проверяем группу вариантов
+ else if (variantGroupSelect && variantGroupSelect.value) {
+ const variantPrice = await getVariantGroupPrice(variantGroupSelect);
+ if (variantPrice > 0) {
+ newBasePrice += (variantPrice * validQuantity);
+ }
}
}
}
diff --git a/myproject/products/templates/products/variantgroup_form.html b/myproject/products/templates/products/variantgroup_form.html
index 5fb9f70..c0e0c6d 100644
--- a/myproject/products/templates/products/variantgroup_form.html
+++ b/myproject/products/templates/products/variantgroup_form.html
@@ -222,7 +222,21 @@ document.addEventListener('DOMContentLoaded', function() {
window.initProductSelect2(productSelect, 'product', apiUrl);
// Обработчик события при выборе товара
- productSelect.addEventListener('select2:select', function(e) {
+ $(productSelect).on('select2:select', function(e) {
+ // Извлекаем числовой ID из формата "product_123"
+ let selectedValue = e.params.data.id;
+ if (typeof selectedValue === 'string' && selectedValue.startsWith('product_')) {
+ selectedValue = selectedValue.replace('product_', '');
+
+ // Создаем новую опцию с правильным ID
+ const selectedOption = $(this).find('option[value="' + e.params.data.id + '"]');
+ if (selectedOption.length > 0) {
+ // Обновляем существующую опцию
+ selectedOption.val(selectedValue);
+ // Устанавливаем новое значение
+ $(this).val(selectedValue);
+ }
+ }
updateRowData(row);
});
}
@@ -246,8 +260,9 @@ document.addEventListener('DOMContentLoaded', function() {
const skuMatch = text.match(/\(([^)]+)\)$/);
const sku = skuMatch ? skuMatch[1] : '-';
- // Получаем цену и статус через AJAX
- fetch(`{% url "products:api-search-products-variants" %}?id=${productSelect.value}`)
+ // Получаем цену и статус через AJAX (используем числовой ID)
+ const numericId = productSelect.value;
+ fetch(`{% url "products:api-search-products-variants" %}?id=${numericId}`)
.then(response => response.json())
.then(data => {
if (data.results && data.results.length > 0) {