diff --git a/myproject/orders/templates/orders/order_list.html b/myproject/orders/templates/orders/order_list.html index 3fc71f7..2d32d46 100644 --- a/myproject/orders/templates/orders/order_list.html +++ b/myproject/orders/templates/orders/order_list.html @@ -8,6 +8,9 @@ {% endblock %} {% block content %} + +
+
@@ -242,6 +245,28 @@ (function() { const csrfToken = '{{ csrf_token }}'; + // Функция для показа Bootstrap alert + function showAlert(message, type = 'danger') { + const container = document.getElementById('js-alert-container'); + const alertDiv = document.createElement('div'); + alertDiv.className = `alert alert-${type} alert-dismissible fade show`; + alertDiv.setAttribute('role', 'alert'); + alertDiv.innerHTML = ` + ${message} + + `; + container.appendChild(alertDiv); + + // Авто-скрытие через 5 секунд + setTimeout(() => { + alertDiv.classList.remove('show'); + setTimeout(() => alertDiv.remove(), 150); + }, 5000); + + // Прокрутка к верху страницы + window.scrollTo({ top: 0, behavior: 'smooth' }); + } + async function updateStatus(orderNumber, statusId) { const body = new URLSearchParams({ status_id: statusId }).toString(); const resp = await fetch(`/orders/api/${orderNumber}/set-status/`, { @@ -278,6 +303,7 @@ select.addEventListener('change', async function() { const statusId = select.value || ''; const selectedOption = select.options[select.selectedIndex]; + const originalIndex = select.selectedIndex; select.disabled = true; try { @@ -294,10 +320,20 @@ select.style.display = 'none'; badge.style.display = 'inline-block'; } else { - alert(result.error || 'Не удалось обновить статус'); + // Показываем красивый Bootstrap alert + showAlert(result.error || 'Не удалось обновить статус', 'danger'); + // Возвращаем предыдущее значение + select.selectedIndex = originalIndex; + // Прячем select, показываем badge + select.style.display = 'none'; + badge.style.display = 'inline-block'; } } catch (e) { - alert('Ошибка сервера при обновлении статуса'); + showAlert('Ошибка сервера при обновлении статуса', 'danger'); + // Возвращаем предыдущее значение + select.selectedIndex = originalIndex; + select.style.display = 'none'; + badge.style.display = 'inline-block'; } finally { select.disabled = false; } diff --git a/myproject/orders/views.py b/myproject/orders/views.py index 6c19e1d..50a346f 100644 --- a/myproject/orders/views.py +++ b/myproject/orders/views.py @@ -684,6 +684,14 @@ def set_order_status(request, order_number): 'color': status.color } }) + except ValidationError as e: + # Ошибка валидации (например, запрет смены статуса для возвращённого заказа) + # Транзакция откатилась, статус НЕ изменился + # Извлекаем чистое сообщение без служебных элементов + error_message = str(e.message) if hasattr(e, 'message') else str(e) + if hasattr(e, 'messages'): + error_message = e.messages[0] if e.messages else str(e) + return JsonResponse({'success': False, 'error': error_message}, status=400) except ValueError as e: # Ошибка в сигналах (например, не удалось создать Sale) # Транзакция откатилась, статус НЕ изменился