This commit is contained in:
2025-12-04 13:59:10 +03:00
parent 18a6c5fa05
commit 456ae0b742

View File

@@ -126,8 +126,7 @@ export class PaymentWidget {
method_id: null, method_id: null,
method_code: null, method_code: null,
method_name: null, method_name: null,
amount: remaining, amount: remaining
fixed: false
}; };
this.payments.push(row); this.payments.push(row);
@@ -139,26 +138,7 @@ export class PaymentWidget {
if (!container) return; if (!container) return;
container.innerHTML = this.payments.map((payment, index) => { container.innerHTML = this.payments.map((payment, index) => {
if (payment.fixed) { // Редактируемая строка (без системы fixed/unfixed)
// Зафиксированная строка
return `
<div class="payment-row mb-2 p-2 border rounded bg-light" data-index="${index}">
<div class="d-flex justify-content-between align-items-center">
<span>
<i class="bi bi-check-circle-fill text-success"></i>
<strong>${payment.method_name}:</strong>
<span class="text-success">${payment.amount.toFixed(2)} руб.</span>
</span>
<button type="button"
class="btn btn-sm btn-outline-danger remove-payment-row"
data-index="${index}">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
`;
} else {
// Редактируемая строка
const methodsOptions = this.paymentMethods.map(m => const methodsOptions = this.paymentMethods.map(m =>
`<option value="${m.id}" `<option value="${m.id}"
data-code="${m.code}" data-code="${m.code}"
@@ -180,7 +160,7 @@ export class PaymentWidget {
${methodsOptions} ${methodsOptions}
</select> </select>
</div> </div>
<div class="col-4"> <div class="col-5">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="number" <input type="number"
class="form-control payment-amount-input" class="form-control payment-amount-input"
@@ -194,12 +174,7 @@ export class PaymentWidget {
data-index="${index}" data-index="${index}"
style="display: none;"></small> style="display: none;"></small>
</div> </div>
<div class="col-3 text-end"> <div class="col-2 text-end">
<button type="button"
class="btn btn-sm btn-success confirm-payment-row me-1"
data-index="${index}">
<i class="bi bi-check-lg"></i>
</button>
<button type="button" <button type="button"
class="btn btn-sm btn-outline-danger remove-payment-row" class="btn btn-sm btn-outline-danger remove-payment-row"
data-index="${index}"> data-index="${index}">
@@ -209,7 +184,6 @@ export class PaymentWidget {
</div> </div>
</div> </div>
`; `;
}
}).join(''); }).join('');
// Обновляем подсказку об остатке // Обновляем подсказку об остатке
@@ -254,14 +228,8 @@ export class PaymentWidget {
input.addEventListener('input', (e) => { input.addEventListener('input', (e) => {
const index = parseInt(e.target.dataset.index); const index = parseInt(e.target.dataset.index);
this.payments[index].amount = parseFloat(e.target.value) || 0; this.payments[index].amount = parseFloat(e.target.value) || 0;
}); // Real-time обновление остатка!
}); this.updateRemainingHint();
// Обработчик подтверждения (галочка)
document.querySelectorAll('.confirm-payment-row').forEach(btn => {
btn.addEventListener('click', (e) => {
const index = parseInt(e.target.closest('button').dataset.index);
this.confirmPaymentRow(index);
}); });
}); });
@@ -276,7 +244,10 @@ export class PaymentWidget {
// Обработчик кнопки "Добавить еще" // Обработчик кнопки "Добавить еще"
const addRowBtn = document.getElementById(`${this.containerId}-add-row-btn`); const addRowBtn = document.getElementById(`${this.containerId}-add-row-btn`);
if (addRowBtn) { 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() УДАЛЕНЫ // СТАРЫЕ МЕТОДЫ addPayment(), removePayment() и updatePaymentsList() УДАЛЕНЫ
// Теперь используются новые методы для построчного UI // Теперь используются новые методы для построчного 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) { removePaymentRow(index) {
this.payments.splice(index, 1); this.payments.splice(index, 1);
@@ -394,21 +321,27 @@ export class PaymentWidget {
const totalPaid = this.getTotalPayments(); const totalPaid = this.getTotalPayments();
const remaining = this.order.amount_due - totalPaid; 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 = '<strong class="text-danger">⚠ Превышение: +' +
excess.toFixed(2) + ' руб.</strong>';
if (addRowBtn) addRowBtn.style.display = 'none';
} else if (remaining <= 0) {
// Точная оплата
hintEl.innerHTML = '<strong class="text-success">✅ Оплачено полностью: ' + hintEl.innerHTML = '<strong class="text-success">✅ Оплачено полностью: ' +
this.order.amount_due.toFixed(2) + ' руб.</strong>'; this.order.amount_due.toFixed(2) + ' руб.</strong>';
// СКРЫТЬ кнопку добавления
if (addRowBtn) addRowBtn.style.display = 'none'; if (addRowBtn) addRowBtn.style.display = 'none';
} else { } else {
// Осталось оплатить
hintEl.textContent = 'Осталось оплатить: ' + remaining.toFixed(2) + ' руб.'; hintEl.textContent = 'Осталось оплатить: ' + remaining.toFixed(2) + ' руб.';
// ПОКАЗАТЬ кнопку добавления
if (addRowBtn) addRowBtn.style.display = 'block'; if (addRowBtn) addRowBtn.style.display = 'block';
} }
} }
getTotalPayments() { getTotalPayments() {
return this.payments return this.payments
.filter(p => p.fixed) // Считаем только зафиксированные! .filter(p => p.method_id) // Считать только если выбран способ оплаты
.reduce((sum, p) => sum + p.amount, 0); .reduce((sum, p) => sum + p.amount, 0);
} }
@@ -439,21 +372,31 @@ export class PaymentWidget {
} else { } 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('Добавьте хотя бы один платеж'); 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(); const total = this.getTotalPayments();
if (total > this.order.amount_due) { 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) { 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); const walletTotal = walletPayments.reduce((sum, p) => sum + p.amount, 0);
if (walletTotal > this.customer.wallet_balance) { if (walletTotal > this.customer.wallet_balance) {
@@ -481,9 +424,9 @@ export class PaymentWidget {
notes: '' notes: ''
}]; }];
} else { } else {
// Смешанная оплата - берем только зафиксированные // Смешанная оплата - берем только заполненные
paymentsData = this.payments paymentsData = this.payments
.filter(p => p.fixed) .filter(p => p.method_id && p.amount > 0)
.map(p => ({ .map(p => ({
payment_method: p.method_code, payment_method: p.method_code,
amount: p.amount, amount: p.amount,