- ${this.mode === 'mixed' ? this.renderPaymentsList() : ''}
-
-
-
-
-
-
-
- ${this.mode === 'mixed' ? `
-
-
- ` : ''}
- `;
+ `;
+
+ // Добавляем первую строку автоматически
+ this.addPaymentRow();
+ }
}
renderPaymentMethods() {
@@ -105,35 +113,182 @@ export class PaymentWidget {
}).join('');
}
- renderPaymentsList() {
- return `
-
-
-
- Платежи не добавлены
-
-
- `;
+ // renderPaymentsList() УДАЛЕН - больше не используется в новом UI
+
+ // ========== НОВЫЕ МЕТОДЫ ДЛЯ MIXED MODE ==========
+
+ addPaymentRow() {
+ const rowId = Date.now(); // уникальный ID для строки
+ const remaining = this.order.amount_due - this.getTotalPayments();
+
+ const row = {
+ id: rowId,
+ method_id: null,
+ method_code: null,
+ method_name: null,
+ amount: remaining,
+ fixed: false
+ };
+
+ this.payments.push(row);
+ this.renderPaymentRows();
}
- attachEvents() {
- // Выбор способа оплаты
- document.querySelectorAll(`#${this.containerId}-methods .payment-method-btn`).forEach(btn => {
- btn.addEventListener('click', () => this.selectPaymentMethod(btn));
+ renderPaymentRows() {
+ const container = document.getElementById(`${this.containerId}-payment-rows`);
+ if (!container) return;
+
+ container.innerHTML = this.payments.map((payment, index) => {
+ if (payment.fixed) {
+ // Зафиксированная строка
+ return `
+
+
+
+
+ ${payment.method_name}:
+ ${payment.amount.toFixed(2)} руб.
+
+
+
+
+ `;
+ } else {
+ // Редактируемая строка
+ const methodsOptions = this.paymentMethods.map(m =>
+ `
`
+ ).join('');
+
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+ }).join('');
+
+ // Обновляем подсказку об остатке
+ this.updateRemainingHint();
+
+ // Прикрепляем обработчики событий
+ this.attachPaymentRowEvents();
+ }
+
+ attachPaymentRowEvents() {
+ // Обработчик выбора способа оплаты
+ document.querySelectorAll('.payment-method-select').forEach(select => {
+ select.addEventListener('change', (e) => {
+ const index = parseInt(e.target.dataset.index);
+ const option = e.target.selectedOptions[0];
+
+ if (option.value) {
+ this.payments[index].method_id = option.value;
+ this.payments[index].method_code = option.dataset.code;
+ this.payments[index].method_name = option.dataset.name;
+
+ // Автофокус на поле суммы
+ const amountInput = document.querySelector(`.payment-amount-input[data-index="${index}"]`);
+ if (amountInput) amountInput.focus();
+
+ // Если кошелек - ограничить сумму
+ if (option.dataset.code === 'account_balance' && this.customer) {
+ const remaining = this.order.amount_due - this.getTotalPayments();
+ const maxAmount = Math.min(remaining, this.customer.wallet_balance);
+ amountInput.value = maxAmount.toFixed(2);
+ amountInput.max = maxAmount;
+ }
+ }
+ });
});
- // Добавить платеж (mixed mode)
- if (this.mode === 'mixed') {
- const addBtn = document.getElementById(`${this.containerId}-add-btn`);
- if (addBtn) {
- addBtn.addEventListener('click', () => this.addPayment());
- }
+ // Обработчик изменения суммы
+ document.querySelectorAll('.payment-amount-input').forEach(input => {
+ 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);
+ });
+ });
+
+ // Обработчик удаления (корзина)
+ document.querySelectorAll('.remove-payment-row').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const index = parseInt(e.target.closest('button').dataset.index);
+ this.removePaymentRow(index);
+ });
+ });
+
+ // Обработчик кнопки "Добавить еще"
+ const addRowBtn = document.getElementById(`${this.containerId}-add-row-btn`);
+ if (addRowBtn) {
+ addRowBtn.addEventListener('click', () => this.addPaymentRow());
+ }
+ }
+
+ // ========== КОНЕЦ НОВЫХ МЕТОДОВ ==========
+
+ attachEvents() {
+ // Выбор способа оплаты (только для single mode)
+ if (this.mode === 'single') {
+ document.querySelectorAll(`#${this.containerId}-methods .payment-method-btn`).forEach(btn => {
+ btn.addEventListener('click', () => this.selectPaymentMethod(btn));
+ });
}
- // Отправка
- // Кнопка "Оплатить" удалена - используется только основная кнопка "Подтвердить продажу" в модалке
+ // Для mixed mode обработчики событий прикрепляются в attachPaymentRowEvents()
+ // который вызывается из renderPaymentRows()
}
selectPaymentMethod(btnElement) {
@@ -170,102 +325,88 @@ export class PaymentWidget {
}
}
- addPayment() {
- if (!this.selectedMethod) {
- alert('Выберите способ оплаты');
+ // СТАРЫЕ МЕТОДЫ 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;
}
- const amountInput = document.getElementById(`${this.containerId}-amount`);
-
- const amount = parseFloat(amountInput.value);
-
- if (amount <= 0 || isNaN(amount)) {
- alert('Введите сумму больше 0');
+ if (!payment.amount || payment.amount <= 0) {
+ errorEl.textContent = 'Введите сумму больше 0';
+ errorEl.style.display = 'block';
return;
}
- // Проверка не превышает ли общая сумма amount_due
- const currentTotal = this.getTotalPayments();
- if (currentTotal + amount > this.order.amount_due) {
- alert(`Общая сумма платежей (${(currentTotal + amount).toFixed(2)}) превышает сумму к оплате (${this.order.amount_due.toFixed(2)})`);
+ // Проверка превышения остатка
+ 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;
}
- // Добавляем в список
- this.payments.push({
- method_id: this.selectedMethod.id,
- method_code: this.selectedMethod.code,
- method_name: this.selectedMethod.name,
- amount: amount,
- notes: '' // Примечания не используются в POS
- });
+ // Проверка кошелька
+ 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;
+ }
+ }
- // Обновляем UI
- this.updatePaymentsList();
-
- // Сбрасываем форму
- const remaining = this.order.amount_due - this.getTotalPayments();
- amountInput.value = remaining > 0 ? remaining.toFixed(2) : '0.00';
-
- // Снимаем выделение способа
- document.querySelectorAll(`#${this.containerId}-methods .payment-method-btn`).forEach(b => {
- b.classList.remove('active');
- });
- this.selectedMethod = null;
-
- const hint = document.getElementById(`${this.containerId}-hint`);
- hint.textContent = remaining > 0 ? `Осталось оплатить: ${remaining.toFixed(2)} руб.` : 'Оплачено полностью';
+ // Все ОК - фиксируем
+ errorEl.style.display = 'none';
+ payment.fixed = true;
+ this.renderPaymentRows();
}
- removePayment(index) {
+ removePaymentRow(index) {
this.payments.splice(index, 1);
- this.updatePaymentsList();
- // Обновляем подсказку
- const amountInput = document.getElementById(`${this.containerId}-amount`);
- const remaining = this.order.amount_due - this.getTotalPayments();
- amountInput.value = remaining > 0 ? remaining.toFixed(2) : '0.00';
-
- const hint = document.getElementById(`${this.containerId}-hint`);
- hint.textContent = remaining > 0 ? `Осталось оплатить: ${remaining.toFixed(2)} руб.` : '';
+ // Если удалили все строки - добавить одну пустую
+ if (this.payments.length === 0) {
+ this.addPaymentRow();
+ } else {
+ this.renderPaymentRows();
+ }
}
- updatePaymentsList() {
- const listContainer = document.getElementById(`${this.containerId}-payments-list`);
- if (!listContainer) return;
+ updateRemainingHint() {
+ const hintEl = document.getElementById(`${this.containerId}-remaining-hint`);
+ const addRowBtn = document.getElementById(`${this.containerId}-add-row-btn`);
+ if (!hintEl) return;
- if (this.payments.length === 0) {
- listContainer.innerHTML = '
Платежи не добавлены';
- return;
+ const totalPaid = this.getTotalPayments();
+ const remaining = this.order.amount_due - totalPaid;
+
+ 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';
}
-
- listContainer.innerHTML = this.payments.map((p, index) => `
-
-
- ${p.method_name}
- ${p.amount.toFixed(2)} руб.
-
-
-
- `).join('');
-
- // Прикрепляем обработчики удаления
- listContainer.querySelectorAll('.remove-payment-btn').forEach(btn => {
- btn.addEventListener('click', () => {
- const index = parseInt(btn.dataset.index);
- this.removePayment(index);
- });
- });
}
getTotalPayments() {
- return this.payments.reduce((sum, p) => sum + p.amount, 0);
+ return this.payments
+ .filter(p => p.fixed) // Считаем только зафиксированные!
+ .reduce((sum, p) => sum + p.amount, 0);
}
validate() {
@@ -295,7 +436,9 @@ export class PaymentWidget {
} else {
// Смешанная оплата
- if (this.payments.length === 0) {
+ const fixedPayments = this.payments.filter(p => p.fixed);
+
+ if (fixedPayments.length === 0) {
throw new Error('Добавьте хотя бы один платеж');
}
@@ -307,7 +450,7 @@ export class PaymentWidget {
// Проверка кошелька
if (this.customer) {
- const walletPayments = this.payments.filter(p => p.method_code === 'account_balance');
+ const walletPayments = fixedPayments.filter(p => p.method_code === 'account_balance');
const walletTotal = walletPayments.reduce((sum, p) => sum + p.amount, 0);
if (walletTotal > this.customer.wallet_balance) {
@@ -335,12 +478,14 @@ export class PaymentWidget {
notes: ''
}];
} else {
- // Смешанная оплата
- paymentsData = this.payments.map(p => ({
- payment_method: p.method_code,
- amount: p.amount,
- notes: p.notes || ''
- }));
+ // Смешанная оплата - берем только зафиксированные
+ paymentsData = this.payments
+ .filter(p => p.fixed)
+ .map(p => ({
+ payment_method: p.method_code,
+ amount: p.amount,
+ notes: ''
+ }));
}
this.onSubmit(paymentsData);