diff --git a/myproject/orders/static/orders/js/payment_widget.js b/myproject/orders/static/orders/js/payment_widget.js index 5d51845..c12508c 100644 --- a/myproject/orders/static/orders/js/payment_widget.js +++ b/myproject/orders/static/orders/js/payment_widget.js @@ -126,8 +126,7 @@ export class PaymentWidget { method_id: null, method_code: null, method_name: null, - amount: remaining, - fixed: false + amount: remaining }; this.payments.push(row); @@ -139,16 +138,43 @@ export class PaymentWidget { if (!container) return; container.innerHTML = this.payments.map((payment, index) => { - if (payment.fixed) { - // Зафиксированная строка - return ` -
-
- - - ${payment.method_name}: - ${payment.amount.toFixed(2)} руб. - + // Редактируемая строка (без системы fixed/unfixed) + const methodsOptions = this.paymentMethods.map(m => + `` + ).join(''); + + return ` +
+
+
+ +
+
+
+ + руб. +
+ +
+
- `; - } else { - // Редактируемая строка - const methodsOptions = this.paymentMethods.map(m => - `` - ).join(''); - - return ` -
-
-
- -
-
-
- - руб. -
- -
-
- - -
-
-
- `; - } +
+ `; }).join(''); // Обновляем подсказку об остатке @@ -254,14 +228,8 @@ export class PaymentWidget { input.addEventListener('input', (e) => { const index = parseInt(e.target.dataset.index); this.payments[index].amount = parseFloat(e.target.value) || 0; - }); - }); - - // Обработчик подтверждения (галочка) - document.querySelectorAll('.confirm-payment-row').forEach(btn => { - btn.addEventListener('click', (e) => { - const index = parseInt(e.target.closest('button').dataset.index); - this.confirmPaymentRow(index); + // Real-time обновление остатка! + this.updateRemainingHint(); }); }); @@ -276,7 +244,10 @@ export class PaymentWidget { // Обработчик кнопки "Добавить еще" const addRowBtn = document.getElementById(`${this.containerId}-add-row-btn`); if (addRowBtn) { - addRowBtn.addEventListener('click', () => this.addPaymentRow()); + // Используем replaceWith для пересоздания элемента и удаления всех обработчиков + const newBtn = addRowBtn.cloneNode(true); + addRowBtn.replaceWith(newBtn); + newBtn.addEventListener('click', () => this.addPaymentRow()); } } @@ -331,50 +302,6 @@ export class PaymentWidget { // СТАРЫЕ МЕТОДЫ addPayment(), removePayment() и updatePaymentsList() УДАЛЕНЫ // Теперь используются новые методы для построчного UI - confirmPaymentRow(index) { - const payment = this.payments[index]; - const errorEl = document.querySelector(`.payment-error[data-index="${index}"]`); - - // Валидация - if (!payment.method_id) { - errorEl.textContent = 'Выберите способ оплаты'; - errorEl.style.display = 'block'; - return; - } - - if (!payment.amount || payment.amount <= 0) { - errorEl.textContent = 'Введите сумму больше 0'; - errorEl.style.display = 'block'; - return; - } - - // Проверка превышения остатка - const totalOtherPayments = this.payments - .filter((p, i) => i !== index && p.fixed) - .reduce((sum, p) => sum + p.amount, 0); - - if (totalOtherPayments + payment.amount > this.order.amount_due) { - const maxAmount = this.order.amount_due - totalOtherPayments; - errorEl.textContent = `Максимум: ${maxAmount.toFixed(2)} руб.`; - errorEl.style.display = 'block'; - return; - } - - // Проверка кошелька - if (payment.method_code === 'account_balance' && this.customer) { - if (payment.amount > this.customer.wallet_balance) { - errorEl.textContent = `Недостаточно средств (${this.customer.wallet_balance.toFixed(2)} руб.)`; - errorEl.style.display = 'block'; - return; - } - } - - // Все ОК - фиксируем - errorEl.style.display = 'none'; - payment.fixed = true; - this.renderPaymentRows(); - } - removePaymentRow(index) { this.payments.splice(index, 1); @@ -394,21 +321,27 @@ export class PaymentWidget { const totalPaid = this.getTotalPayments(); const remaining = this.order.amount_due - totalPaid; - if (remaining <= 0) { + if (totalPaid > this.order.amount_due) { + // Превышение суммы + const excess = totalPaid - this.order.amount_due; + hintEl.innerHTML = '⚠ Превышение: +' + + excess.toFixed(2) + ' руб.'; + if (addRowBtn) addRowBtn.style.display = 'none'; + } else if (remaining <= 0) { + // Точная оплата hintEl.innerHTML = '✅ Оплачено полностью: ' + this.order.amount_due.toFixed(2) + ' руб.'; - // СКРЫТЬ кнопку добавления if (addRowBtn) addRowBtn.style.display = 'none'; } else { + // Осталось оплатить hintEl.textContent = 'Осталось оплатить: ' + remaining.toFixed(2) + ' руб.'; - // ПОКАЗАТЬ кнопку добавления if (addRowBtn) addRowBtn.style.display = 'block'; } } getTotalPayments() { return this.payments - .filter(p => p.fixed) // Считаем только зафиксированные! + .filter(p => p.method_id) // Считать только если выбран способ оплаты .reduce((sum, p) => sum + p.amount, 0); } @@ -439,21 +372,31 @@ export class PaymentWidget { } else { // Смешанная оплата - const fixedPayments = this.payments.filter(p => p.fixed); + const validPayments = this.payments.filter(p => p.method_id && p.amount > 0); - if (fixedPayments.length === 0) { + if (validPayments.length === 0) { throw new Error('Добавьте хотя бы один платеж'); } + // Проверить незавершенные платежи + const incompletePayments = this.payments.filter(p => + (p.method_id && !p.amount) || (!p.method_id && p.amount) + ); + + if (incompletePayments.length > 0) { + throw new Error('Заполните все поля или удалите незавершенные строки'); + } + const total = this.getTotalPayments(); if (total > this.order.amount_due) { - throw new Error(`Сумма платежей превышает остаток к оплате (${this.order.amount_due.toFixed(2)} руб.)`); + const excess = total - this.order.amount_due; + throw new Error(`Сумма платежей превышает остаток к оплате на ${excess.toFixed(2)} руб.`); } // Проверка кошелька if (this.customer) { - const walletPayments = fixedPayments.filter(p => p.method_code === 'account_balance'); + const walletPayments = validPayments.filter(p => p.method_code === 'account_balance'); const walletTotal = walletPayments.reduce((sum, p) => sum + p.amount, 0); if (walletTotal > this.customer.wallet_balance) { @@ -481,9 +424,9 @@ export class PaymentWidget { notes: '' }]; } else { - // Смешанная оплата - берем только зафиксированные + // Смешанная оплата - берем только заполненные paymentsData = this.payments - .filter(p => p.fixed) + .filter(p => p.method_id && p.amount > 0) .map(p => ({ payment_method: p.method_code, amount: p.amount,