feat(pos): добавлен полноценный интерфейс скидок в модальное окно продажи
- Добавлен API endpoint /pos/api/discounts/available/ для получения списка доступных скидок - Добавлен метод DiscountApplier.apply_manual_discount() для применения ручных скидок - Обновлен POS checkout для обработки manual_discount_id - Расширена секция скидок в модальном окне: * Отображение автоматических скидок (read-only) * Dropdown для выбора скидки вручную * Подробная детализация: подитог, общая скидка, скидки на позиции * Поле промокода с иконкой - Увеличен размер модального окна и изменено соотношение колонок (5/7) - Убрана вертикальная прокрутка из модального окна Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2294,6 +2294,8 @@ let paymentWidget = null;
|
||||
|
||||
// Переменные состояния скидок
|
||||
let appliedPromoCode = null; // примененный промокод
|
||||
let appliedManualDiscount = null; // выбранная вручную скидка
|
||||
let availableDiscounts = []; // список доступных скидок
|
||||
let cartDiscounts = {
|
||||
orderDiscount: null, // скидка на заказ
|
||||
itemDiscounts: [], // скидки на позиции
|
||||
@@ -2326,6 +2328,9 @@ document.getElementById('checkoutModal').addEventListener('show.bs.modal', async
|
||||
// Проверяем автоматические скидки
|
||||
await checkAutoDiscounts();
|
||||
|
||||
// Загружаем доступные скидки для ручного выбора
|
||||
await loadAvailableDiscounts();
|
||||
|
||||
// Применяем скидки к итоговой сумме
|
||||
const finalTotal = Math.max(0, totalAmount - cartDiscounts.totalDiscount);
|
||||
document.getElementById('checkoutFinalPrice').textContent = formatMoney(finalTotal) + ' руб.';
|
||||
@@ -2366,6 +2371,8 @@ function reinitPaymentWidget(mode) {
|
||||
// Сброс скидок
|
||||
function resetDiscounts() {
|
||||
appliedPromoCode = null;
|
||||
appliedManualDiscount = null;
|
||||
availableDiscounts = [];
|
||||
cartDiscounts = {
|
||||
orderDiscount: null,
|
||||
itemDiscounts: [],
|
||||
@@ -2379,8 +2386,13 @@ function resetDiscounts() {
|
||||
document.getElementById('promoCodeSuccess').style.display = 'none';
|
||||
document.getElementById('promoCodeSuccess').textContent = '';
|
||||
document.getElementById('removePromoBtn').style.display = 'none';
|
||||
document.getElementById('autoDiscounts').style.display = 'none';
|
||||
document.getElementById('autoDiscountsText').textContent = '';
|
||||
|
||||
// Новые элементы UI
|
||||
document.getElementById('autoDiscountsContainer').style.display = 'none';
|
||||
document.getElementById('autoDiscountsList').innerHTML = '';
|
||||
document.getElementById('manualDiscountContainer').style.display = 'none';
|
||||
document.getElementById('discountsSummary').style.display = 'none';
|
||||
document.getElementById('itemDiscountsBreakdown').innerHTML = '';
|
||||
}
|
||||
|
||||
// Проверить автоматические скидки
|
||||
@@ -2403,29 +2415,168 @@ async function checkAutoDiscounts() {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
items: items,
|
||||
customer_id: customer.id !== SYSTEM_CUSTOMER.id ? customer.id : null
|
||||
customer_id: customer.id !== SYSTEM_CUSTOMER.id ? customer.id : null,
|
||||
manual_discount_id: appliedManualDiscount?.id || null
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Сохраняем скидки
|
||||
cartDiscounts.totalDiscount = result.total_discount || 0;
|
||||
cartDiscounts.orderDiscount = result.order_discount;
|
||||
cartDiscounts.itemDiscounts = result.item_discounts || [];
|
||||
|
||||
// Показываем автоматические скидки
|
||||
if (result.order_discount && result.order_discount.discount_id) {
|
||||
document.getElementById('autoDiscounts').style.display = 'block';
|
||||
document.getElementById('autoDiscountsText').textContent =
|
||||
`${result.order_discount.discount_name}: -${result.order_discount.discount_amount.toFixed(2)} руб.`;
|
||||
}
|
||||
updateDiscountsUI(result);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при проверке автоматических скидок:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Обновить UI скидок
|
||||
function updateDiscountsUI(result) {
|
||||
const autoContainer = document.getElementById('autoDiscountsContainer');
|
||||
const autoList = document.getElementById('autoDiscountsList');
|
||||
const summaryDiv = document.getElementById('discountsSummary');
|
||||
const itemBreakdown = document.getElementById('itemDiscountsBreakdown');
|
||||
|
||||
// Очистка
|
||||
autoList.innerHTML = '';
|
||||
itemBreakdown.innerHTML = '';
|
||||
|
||||
let hasDiscounts = false;
|
||||
|
||||
// 1. Скидка на заказ (автоматическая)
|
||||
if (result.order_discount && result.order_discount.discount_id) {
|
||||
hasDiscounts = true;
|
||||
autoContainer.style.display = 'block';
|
||||
const div = document.createElement('div');
|
||||
div.className = 'd-flex justify-content-between';
|
||||
div.innerHTML = `
|
||||
<span>${result.order_discount.discount_name}</span>
|
||||
<span class="text-success">-${result.order_discount.discount_amount.toFixed(2)} руб.</span>
|
||||
`;
|
||||
autoList.appendChild(div);
|
||||
} else {
|
||||
autoContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
// 2. Скидки на позиции
|
||||
if (result.item_discounts && result.item_discounts.length > 0) {
|
||||
hasDiscounts = true;
|
||||
result.item_discounts.forEach(item => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'text-muted';
|
||||
div.innerHTML = `• ${item.discount_name}: -${item.discount_amount.toFixed(2)} руб.`;
|
||||
itemBreakdown.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Ручная скидка
|
||||
if (appliedManualDiscount) {
|
||||
hasDiscounts = true;
|
||||
document.getElementById('manualDiscountContainer').style.display = 'block';
|
||||
document.getElementById('manualDiscountName').textContent = appliedManualDiscount.name;
|
||||
document.getElementById('manualDiscountAmount').textContent =
|
||||
`-${appliedManualDiscount.amount.toFixed(2)} руб.`;
|
||||
} else {
|
||||
document.getElementById('manualDiscountContainer').style.display = 'none';
|
||||
}
|
||||
|
||||
// Показываем/скрываем summary
|
||||
if (hasDiscounts) {
|
||||
summaryDiv.style.display = 'block';
|
||||
document.getElementById('discountsSubtotal').textContent =
|
||||
(result.cart_subtotal || 0).toFixed(2) + ' руб.';
|
||||
document.getElementById('discountsTotalDiscount').textContent =
|
||||
'-' + (result.total_discount || 0).toFixed(2) + ' руб.';
|
||||
} else {
|
||||
summaryDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
// Обновляем итоговую цену
|
||||
const finalTotal = Math.max(0, (result.cart_subtotal || 0) - (result.total_discount || 0));
|
||||
document.getElementById('checkoutFinalPrice').textContent = formatMoney(finalTotal) + ' руб.';
|
||||
|
||||
// Пересчитываем виджет оплаты
|
||||
reinitPaymentWidget(document.getElementById('singlePaymentMode').classList.contains('active') ? 'single' : 'mixed');
|
||||
}
|
||||
|
||||
// Загрузить доступные скидки
|
||||
async function loadAvailableDiscounts() {
|
||||
try {
|
||||
let cartTotal = 0;
|
||||
cart.forEach((item) => {
|
||||
cartTotal += item.qty * item.price;
|
||||
});
|
||||
|
||||
const response = await fetch(`/pos/api/discounts/available/?cart_total=${cartTotal}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
availableDiscounts = result.order_discounts;
|
||||
renderDiscountsDropdown(result.order_discounts);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки скидок:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Отобразить список скидок в dropdown
|
||||
function renderDiscountsDropdown(discounts) {
|
||||
const list = document.getElementById('discountsDropdownList');
|
||||
list.innerHTML = '';
|
||||
|
||||
if (discounts.length === 0) {
|
||||
list.innerHTML = '<li><span class="dropdown-item-text small text-muted">Нет доступных скидок</span></li>';
|
||||
return;
|
||||
}
|
||||
|
||||
discounts.forEach(d => {
|
||||
const li = document.createElement('li');
|
||||
const valueText = d.discount_type === 'percentage' ? `${d.value}%` : `${d.value} руб.`;
|
||||
const minText = d.min_order_amount ? `(от ${d.min_order_amount} руб.)` : '';
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = '#';
|
||||
a.className = 'dropdown-item d-flex justify-content-between';
|
||||
a.innerHTML = `
|
||||
<span>${d.name}</span>
|
||||
<span class="text-success">${valueText} ${minText}</span>
|
||||
`;
|
||||
a.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
applyManualDiscount(d);
|
||||
};
|
||||
li.appendChild(a);
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
// Применить скидку вручную
|
||||
async function applyManualDiscount(discount) {
|
||||
// Рассчитываем сумму скидки на клиенте (для отображения до ответа сервера)
|
||||
let cartTotal = 0;
|
||||
cart.forEach((item) => {
|
||||
cartTotal += item.qty * item.price;
|
||||
});
|
||||
let discountAmount = discount.discount_type === 'percentage'
|
||||
? cartTotal * (discount.value / 100)
|
||||
: discount.value;
|
||||
|
||||
appliedManualDiscount = { ...discount, amount: discountAmount };
|
||||
await checkAutoDiscounts();
|
||||
await loadAvailableDiscounts();
|
||||
}
|
||||
|
||||
// Удалить ручную скидку
|
||||
document.getElementById('removeManualDiscountBtn').addEventListener('click', async () => {
|
||||
appliedManualDiscount = null;
|
||||
await checkAutoDiscounts();
|
||||
await loadAvailableDiscounts();
|
||||
});
|
||||
|
||||
// Применить промокод
|
||||
async function applyPromoCode() {
|
||||
const code = document.getElementById('promoCodeInput').value.trim().toUpperCase();
|
||||
@@ -2455,7 +2606,7 @@ async function applyPromoCode() {
|
||||
if (result.success) {
|
||||
appliedPromoCode = result.promo_code;
|
||||
|
||||
// Пересчитываем скидки с промокодом
|
||||
// Пересчитываем скидки с промокодом (updateDiscountsUI обновит UI)
|
||||
await recalculateDiscountsWithPromo(code);
|
||||
|
||||
// Показываем успех
|
||||
@@ -2464,9 +2615,6 @@ async function applyPromoCode() {
|
||||
document.getElementById('promoCodeSuccess').style.display = 'block';
|
||||
document.getElementById('promoCodeError').style.display = 'none';
|
||||
document.getElementById('removePromoBtn').style.display = 'inline-block';
|
||||
|
||||
// Обновляем итоговую сумму
|
||||
updateCheckoutTotalWithDiscounts();
|
||||
} else {
|
||||
// Показываем ошибку
|
||||
document.getElementById('promoCodeError').textContent = result.error || 'Неверный промокод';
|
||||
@@ -2511,6 +2659,7 @@ async function recalculateDiscountsWithPromo(promoCode) {
|
||||
cartDiscounts.totalDiscount = result.total_discount || 0;
|
||||
cartDiscounts.orderDiscount = result.order_discount;
|
||||
cartDiscounts.itemDiscounts = result.item_discounts || [];
|
||||
updateDiscountsUI(result);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при пересчёте скидок:', error);
|
||||
@@ -2539,12 +2688,11 @@ function updateCheckoutTotalWithDiscounts() {
|
||||
function removePromoCode() {
|
||||
appliedPromoCode = null;
|
||||
|
||||
// Пересчитываем без промокода
|
||||
// Пересчитываем без промокода (updateDiscountsUI обновит UI)
|
||||
recalculateDiscountsWithPromo(null).then(() => {
|
||||
document.getElementById('promoCodeInput').value = '';
|
||||
document.getElementById('removePromoBtn').style.display = 'none';
|
||||
document.getElementById('promoCodeSuccess').style.display = 'none';
|
||||
updateCheckoutTotalWithDiscounts();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2629,7 +2777,8 @@ async function handleCheckoutSubmit(paymentsData) {
|
||||
}),
|
||||
payments: paymentsData,
|
||||
notes: document.getElementById('orderNote').value.trim(),
|
||||
promo_code: appliedPromoCode || null
|
||||
promo_code: appliedPromoCode?.code || null,
|
||||
manual_discount_id: appliedManualDiscount?.id || null
|
||||
};
|
||||
|
||||
// Отправляем на сервер
|
||||
|
||||
@@ -282,18 +282,18 @@
|
||||
|
||||
<!-- Модалка: Продажа -->
|
||||
<div class="modal fade" id="checkoutModal" tabindex="-1" aria-labelledby="checkoutModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg" style="max-width: 70vw;">
|
||||
<div class="modal-content" style="max-height: 90vh; overflow: hidden;">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl" style="max-width: 85vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header py-2">
|
||||
<h5 class="modal-title" id="checkoutModalLabel">
|
||||
<i class="bi bi-cash-stack"></i> Продажа
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body py-2" style="overflow-y: auto;">
|
||||
<div class="modal-body py-2">
|
||||
<div class="row g-3">
|
||||
<!-- Левая колонка: состав заказа -->
|
||||
<div class="col-md-7">
|
||||
<div class="col-md-5">
|
||||
<!-- Информация о клиенте и баланс в одной строке -->
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col-auto">
|
||||
@@ -320,7 +320,7 @@
|
||||
<!-- Состав заказа -->
|
||||
<div class="mb-2">
|
||||
<strong class="small">Состав заказа</strong>
|
||||
<div class="border rounded p-2 mt-1" id="checkoutItems" style="max-height: 180px; overflow-y: auto; background: #f8f9fa; font-size: 0.9rem;">
|
||||
<div class="border rounded p-2 mt-1" id="checkoutItems" style="background: #f8f9fa; font-size: 0.9rem;">
|
||||
<!-- Заполняется из JS -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -333,7 +333,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Правая колонка: оплата -->
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-7">
|
||||
<div class="card mb-0">
|
||||
<div class="card-header bg-light py-2">
|
||||
<strong class="small">Оплата</strong>
|
||||
@@ -347,14 +347,45 @@
|
||||
|
||||
<!-- Скидки -->
|
||||
<div class="mb-2 pb-2 border-bottom">
|
||||
<small class="text-muted">Скидки:</small>
|
||||
<!-- Автоматические скидки -->
|
||||
<div id="autoDiscounts" class="small text-success mb-1" style="display: none;">
|
||||
<i class="bi bi-tag-fill"></i>
|
||||
<span id="autoDiscountsText"></span>
|
||||
<small class="text-muted d-block mb-1">Скидки</small>
|
||||
|
||||
<!-- Автоматические скидки (read-only) -->
|
||||
<div id="autoDiscountsContainer" class="mb-2" style="display: none;">
|
||||
<div class="alert alert-success py-1 px-2 mb-1" style="font-size: 0.85rem;">
|
||||
<i class="bi bi-magic me-1"></i>
|
||||
<strong>Автоматически:</strong>
|
||||
<div id="autoDiscountsList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Выбранная скидка вручную -->
|
||||
<div id="manualDiscountContainer" class="mb-2" style="display: none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="badge bg-info text-dark">
|
||||
<i class="bi bi-tag me-1"></i>
|
||||
<span id="manualDiscountName"></span>
|
||||
<span id="manualDiscountAmount" class="ms-1"></span>
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-link text-danger p-0" id="removeManualDiscountBtn">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown для выбора скидки -->
|
||||
<div class="dropdown mb-2">
|
||||
<button class="btn btn-sm btn-outline-primary w-100 dropdown-toggle" type="button"
|
||||
id="selectDiscountBtn" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-plus-circle me-1"></i> Добавить скидку
|
||||
</button>
|
||||
<ul class="dropdown-menu w-100" id="discountsDropdownList">
|
||||
<li><span class="dropdown-item-text small text-muted">Загрузка...</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Промокод -->
|
||||
<div class="input-group input-group-sm mt-1">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text"><i class="bi bi-ticket-perforated"></i></span>
|
||||
<input type="text" class="form-control" id="promoCodeInput"
|
||||
placeholder="Промокод" style="text-transform: uppercase;">
|
||||
<button class="btn btn-outline-secondary" type="button" id="applyPromoBtn">
|
||||
@@ -366,6 +397,21 @@
|
||||
</div>
|
||||
<div id="promoCodeError" class="text-danger small mt-1" style="display: none;"></div>
|
||||
<div id="promoCodeSuccess" class="text-success small mt-1" style="display: none;"></div>
|
||||
|
||||
<!-- Итоговая информация по скидкам -->
|
||||
<div id="discountsSummary" class="mt-2 pt-2 border-top" style="display: none;">
|
||||
<div class="d-flex justify-content-between small text-muted">
|
||||
<span>Подитог:</span>
|
||||
<span id="discountsSubtotal">0.00 руб.</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between small text-success">
|
||||
<span>Общая скидка:</span>
|
||||
<span id="discountsTotalDiscount">-0.00 руб.</span>
|
||||
</div>
|
||||
<div id="itemDiscountsBreakdown" class="mt-1 small text-muted">
|
||||
<!-- Скидки на позиции -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Переключатель режима -->
|
||||
|
||||
@@ -44,4 +44,6 @@ urlpatterns = [
|
||||
path('api/discounts/validate-promo/', views.validate_promo_code, name='validate-promo'),
|
||||
# Рассчитать скидки для корзины [POST]
|
||||
path('api/discounts/calculate/', views.calculate_cart_discounts, name='calculate-discounts'),
|
||||
# Получить список доступных скидок [GET]
|
||||
path('api/discounts/available/', views.get_available_discounts, name='available-discounts'),
|
||||
]
|
||||
@@ -1402,6 +1402,7 @@ def pos_checkout(request):
|
||||
],
|
||||
"notes": str (optional),
|
||||
"promo_code": str (optional) - Промокод для скидки
|
||||
"manual_discount_id": int (optional) - ID выбранной вручную скидки
|
||||
}
|
||||
"""
|
||||
from orders.models import Order, OrderItem, OrderStatus
|
||||
@@ -1423,6 +1424,7 @@ def pos_checkout(request):
|
||||
payments_data = body.get('payments', [])
|
||||
order_notes = body.get('notes', '')
|
||||
promo_code = body.get('promo_code') # Промокод для скидки
|
||||
manual_discount_id = body.get('manual_discount_id') # ID выбранной вручную скидки
|
||||
|
||||
if not customer_id:
|
||||
return JsonResponse({'success': False, 'error': 'Не указан клиент'}, status=400)
|
||||
@@ -1554,7 +1556,22 @@ def pos_checkout(request):
|
||||
order.calculate_total()
|
||||
|
||||
# 4. Применяем скидки
|
||||
if promo_code:
|
||||
if manual_discount_id:
|
||||
from discounts.services.applier import DiscountApplier
|
||||
from discounts.models import Discount
|
||||
|
||||
try:
|
||||
discount = Discount.objects.get(id=manual_discount_id, is_active=True)
|
||||
apply_result = DiscountApplier.apply_manual_discount(
|
||||
order=order,
|
||||
discount=discount,
|
||||
user=request.user
|
||||
)
|
||||
if not apply_result['success']:
|
||||
raise ValidationError(apply_result['error'])
|
||||
except Discount.DoesNotExist:
|
||||
pass
|
||||
elif promo_code:
|
||||
from discounts.services.applier import DiscountApplier
|
||||
apply_result = DiscountApplier.apply_promo_code(
|
||||
order=order,
|
||||
@@ -1815,3 +1832,85 @@ def calculate_cart_discounts(request):
|
||||
except Exception as e:
|
||||
logger.error(f'Ошибка при расчете скидок: {str(e)}', exc_info=True)
|
||||
return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def get_available_discounts(request):
|
||||
"""
|
||||
Возвращает список доступных скидок для ручного применения в POS.
|
||||
|
||||
Query params:
|
||||
- cart_total: сумма корзины (для фильтрации по min_order_amount)
|
||||
|
||||
Returns JSON:
|
||||
{
|
||||
'success': true,
|
||||
'order_discounts': [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Скидка 5%',
|
||||
'discount_type': 'percentage',
|
||||
'value': 5,
|
||||
'min_order_amount': 50
|
||||
},
|
||||
...
|
||||
],
|
||||
'auto_order_discount': {...} # если есть автоскидка на заказ
|
||||
}
|
||||
"""
|
||||
from discounts.services.calculator import DiscountCalculator
|
||||
|
||||
try:
|
||||
cart_total = request.GET.get('cart_total', '0')
|
||||
cart_total = Decimal(str(cart_total))
|
||||
|
||||
# Получаем НЕ автоматические скидки на заказ для ручного применения
|
||||
order_discounts = DiscountCalculator.get_available_discounts(
|
||||
scope='order',
|
||||
auto_only=False
|
||||
).filter(is_auto=False)
|
||||
|
||||
# Фильтруем по мин. сумме заказа
|
||||
result_discounts = []
|
||||
for d in order_discounts:
|
||||
if d.min_order_amount and cart_total < d.min_order_amount:
|
||||
continue
|
||||
result_discounts.append({
|
||||
'id': d.id,
|
||||
'name': d.name,
|
||||
'discount_type': d.discount_type,
|
||||
'value': float(d.value),
|
||||
'min_order_amount': float(d.min_order_amount) if d.min_order_amount else None
|
||||
})
|
||||
|
||||
# Получаем автоматическую скидку (только одну для отображения)
|
||||
auto_discounts = DiscountCalculator.get_available_discounts(
|
||||
scope='order',
|
||||
auto_only=True
|
||||
)
|
||||
|
||||
auto_discount_data = None
|
||||
for d in auto_discounts:
|
||||
if d.min_order_amount and cart_total < d.min_order_amount:
|
||||
continue
|
||||
# Рассчитываем сумму
|
||||
discount_amount = d.calculate_discount_amount(cart_total)
|
||||
auto_discount_data = {
|
||||
'id': d.id,
|
||||
'name': d.name,
|
||||
'discount_type': d.discount_type,
|
||||
'value': float(d.value),
|
||||
'discount_amount': float(discount_amount)
|
||||
}
|
||||
break
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'order_discounts': result_discounts,
|
||||
'auto_order_discount': auto_discount_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'Ошибка при получении скидок: {str(e)}', exc_info=True)
|
||||
return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
|
||||
|
||||
Reference in New Issue
Block a user