Feat: Add order items total sum display with real-time calculation

Добавлено отображение общей суммы всех товаров в заказе с автоматическим
обновлением в реальном времени при изменении количества, цены, добавлении
или удалении товаров.

Изменения:
- HTML: Добавлен блок отображения итоговой суммы товаров под списком позиций
- JavaScript: Реализованы функции calculateOrderItemsTotal() и updateOrderItemsTotal()
- Интеграция: Подключены слушатели событий на поля quantity и price
- Обновление суммы происходит при:
  * Изменении количества или цены товара
  * Добавлении новой позиции
  * Удалении позиции
  * Загрузке страницы

Сумма автоматически исключает удалённые формы и корректно обрабатывает
пустые значения. Форматирование: 2 знака после запятой.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 19:39:03 +03:00
parent 22bf7e137d
commit 436cac28ed

View File

@@ -255,6 +255,20 @@
{% endfor %}
</div>
<!-- Итоговая сумма товаров -->
<div id="order-items-total-section" class="border-top pt-3 mt-3 mb-3">
<div class="row align-items-center">
<div class="col">
<p class="mb-0 text-muted">Сумма товаров:</p>
</div>
<div class="col-auto">
<h5 class="mb-0 text-primary">
<span id="order-items-total-value">0.00</span> руб.
</h5>
</div>
</div>
</div>
<!-- Скрытый шаблон для новых форм -->
<template id="empty-form-template">
<div class="order-item-form border rounded p-3 mb-3" data-form-index="__prefix__">
@@ -592,9 +606,6 @@
</form>
</div>
<!-- Подключение модуля Select2 для поиска товаров/комплектов -->
<script src="{% static 'products/js/select2-product-search.js' %}"></script>
<script>
// Ждем пока jQuery загрузится
function initCustomerSelect2() {
@@ -904,10 +915,17 @@ document.addEventListener('DOMContentLoaded', function() {
toggleRecipientFields(); // Инициализация при загрузке
// Инициализация Select2 для поиска товаров/комплектов
function initOrderItemSelect2(element) {
// ВНИМАНИЕ: Эта функция будет вызвана ПОСЛЕ загрузки select2-product-search.js
window.initOrderItemSelect2 = function(element) {
const $element = $(element);
const formIndex = element.dataset.formIndex;
// Проверяем, что функция initProductSelect2 доступна
if (typeof window.initProductSelect2 !== 'function') {
console.error('window.initProductSelect2 is not defined. Make sure select2-product-search.js is loaded.');
return;
}
// Инициализируем Select2 с AJAX поиском
window.initProductSelect2(
element,
@@ -967,10 +985,40 @@ document.addEventListener('DOMContentLoaded', function() {
form.querySelector('[name$="-product_kit"]').value = '';
form.querySelector('[name$="-price"]').value = '';
});
};
// === РАСЧЁТ ИТОГОВОЙ СУММЫ ТОВАРОВ ===
function calculateOrderItemsTotal() {
// Собираем все видимые (не удалённые) формы товаров
const visibleForms = Array.from(document.querySelectorAll('.order-item-form'))
.filter(form => !form.classList.contains('deleted'));
let total = 0;
// Для каждого товара: количество × цена
visibleForms.forEach(form => {
const quantityField = form.querySelector('[name$="-quantity"]');
const priceField = form.querySelector('[name$="-price"]');
if (quantityField && priceField) {
const quantity = parseFloat(quantityField.value) || 0;
const price = parseFloat(priceField.value) || 0;
total += quantity * price;
}
});
return total;
}
// Инициализировать все существующие формы товаров
document.querySelectorAll('.select2-order-item').forEach(initOrderItemSelect2);
function updateOrderItemsTotal() {
const total = calculateOrderItemsTotal();
const totalElement = document.getElementById('order-items-total-value');
if (totalElement) {
// Форматируем до 2 знаков после запятой
totalElement.textContent = total.toFixed(2);
}
}
// Функция для инициализации отслеживания изменения цены
function initPriceTracking(form) {
@@ -1009,6 +1057,14 @@ document.addEventListener('DOMContentLoaded', function() {
originalPriceValue.textContent = originalPrice.toFixed(2);
}
}
// Добавляем слушатели для обновления итоговой суммы
priceField.addEventListener('input', updateOrderItemsTotal);
const quantityField = form.querySelector('[name$="-quantity"]');
if (quantityField) {
quantityField.addEventListener('input', updateOrderItemsTotal);
}
}
// Инициализация отслеживания цены для всех существующих форм
@@ -1042,7 +1098,11 @@ document.addEventListener('DOMContentLoaded', function() {
// Инициализируем Select2 для новой формы
const select2Element = newForm.querySelector('.select2-order-item');
select2Element.dataset.formIndex = formCount;
initOrderItemSelect2(select2Element);
if (typeof window.initOrderItemSelect2 === 'function') {
window.initOrderItemSelect2(select2Element);
} else {
console.error('window.initOrderItemSelect2 is not available');
}
// Инициализируем отслеживание цены
initPriceTracking(newForm);
@@ -1055,6 +1115,9 @@ document.addEventListener('DOMContentLoaded', function() {
console.log(`Added new form with index ${formCount}`);
// Обновляем итоговую сумму после добавления формы
updateOrderItemsTotal();
return newForm;
}
@@ -1077,10 +1140,14 @@ document.addEventListener('DOMContentLoaded', function() {
deleteCheckbox.checked = true;
form.classList.add('deleted');
console.log('Form marked for deletion');
// Обновляем итоговую сумму после удаления
updateOrderItemsTotal();
} else {
// Если форма новая, просто удаляем из DOM
form.remove();
console.log('Form removed from DOM');
// Обновляем итоговую сумму после удаления
updateOrderItemsTotal();
}
}
@@ -1100,6 +1167,9 @@ document.addEventListener('DOMContentLoaded', function() {
addNewForm();
}
// Инициализируем итоговую сумму при загрузке страницы
updateOrderItemsTotal();
// Валидация перед отправкой
document.getElementById('order-form').addEventListener('submit', function(e) {
const visibleForms = Array.from(container.querySelectorAll('.order-item-form'))
@@ -1676,6 +1746,21 @@ if (!document.getElementById('notification-styles')) {
</div>
</div>
<!-- Подключение модуля Select2 для поиска товаров/комплектов -->
<script src="{% static 'products/js/select2-product-search.js' %}"></script>
<script>
// Инициализируем все существующие Select2 для товаров после загрузки модуля
(function() {
if (typeof window.initOrderItemSelect2 === 'function') {
document.querySelectorAll('.select2-order-item').forEach(window.initOrderItemSelect2);
console.log('[Order Items] Select2 initialized for existing items');
} else {
console.error('[Order Items] window.initOrderItemSelect2 is not defined');
}
})();
</script>
<!-- Скрипты автосохранения и создания черновиков -->
{% if order %}
<!-- Автосохранение при редактировании заказа -->