Реализована полноценная система оплаты для POS-терминала
Добавлена интеграция оплаты в POS с поддержкой одиночной и смешанной оплаты, работой с кошельком клиента и автоматическим созданием заказов. Backend изменения: - TransactionService: добавлены методы get_available_payment_methods() и create_multiple_payments() для фильтрации способов оплаты и атомарного создания нескольких платежей - POS API: новый endpoint pos_checkout() для создания заказов со статусом "Выполнен" с обработкой платежей, освобождением блокировок и очисткой корзины - Template tags: payment_tags.py для получения способов оплаты в шаблонах Frontend изменения: - PaymentWidget: переиспользуемый ES6 класс с поддержкой single/mixed режимов, автоматической валидацией и интеграцией с кошельком клиента - terminal.html: компактное модальное окно (70vw) с оптимизированной компоновкой, удален функционал скидок, добавлен показ баланса кошелька - terminal.js: динамическая загрузка PaymentWidget, интеграция с backend API, обработка успешной оплаты и ошибок Поддерживаемые способы оплаты: наличные, карта, онлайн, баланс счёта. Смешанная оплата позволяет комбинировать несколько способов в одной транзакции. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -274,121 +274,96 @@
|
||||
|
||||
<!-- Модалка: Продажа -->
|
||||
<div class="modal fade" id="checkoutModal" tabindex="-1" aria-labelledby="checkoutModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg" style="max-width: 70vw;">
|
||||
<div class="modal-content" style="max-height: 90vh; overflow: hidden;">
|
||||
<div class="modal-header py-2">
|
||||
<h5 class="modal-title" id="checkoutModalLabel">
|
||||
<i class="bi bi-cash-stack"></i> Продажа
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="modal-body py-2" style="overflow-y: auto;">
|
||||
<div class="row g-3">
|
||||
<!-- Левая колонка: состав заказа -->
|
||||
<div class="col-md-7">
|
||||
<!-- Информация о клиенте -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Клиент</label>
|
||||
<div class="d-flex gap-1 align-items-center">
|
||||
<button class="btn btn-outline-primary btn-sm d-flex align-items-center" id="checkoutCustomerSelectBtn">
|
||||
<i class="bi bi-person me-1"></i>
|
||||
<div class="d-flex flex-column align-items-start lh-1">
|
||||
<small class="text-muted" style="font-size: 0.65rem;">Клиент</small>
|
||||
<span id="checkoutCustomerSelectBtnText" class="fw-semibold">Выбрать</span>
|
||||
</div>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" id="checkoutResetCustomerBtn" title="Сброс на системного клиента" style="display: none;">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<!-- Информация о клиенте и баланс в одной строке -->
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col-auto">
|
||||
<label class="form-label fw-semibold small mb-1">Клиент</label>
|
||||
<div class="d-flex gap-1">
|
||||
<button class="btn btn-outline-primary btn-sm" id="checkoutCustomerSelectBtn" style="font-size: 0.85rem; padding: 0.25rem 0.5rem;">
|
||||
<i class="bi bi-person me-1"></i>
|
||||
<span id="checkoutCustomerSelectBtnText">Выбрать</span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" id="checkoutResetCustomerBtn" title="Сброс" style="display: none; font-size: 0.85rem; padding: 0.25rem 0.5rem;">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" id="checkoutWalletBalance" style="display: none;">
|
||||
<label class="form-label fw-semibold small mb-1">Баланс кошелька</label>
|
||||
<div class="alert alert-info py-1 px-2 mb-0" style="font-size: 0.9rem;">
|
||||
<i class="bi bi-wallet2"></i>
|
||||
<span id="checkoutWalletBalanceAmount">0.00</span> руб.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<strong>Состав заказа</strong>
|
||||
<div class="border rounded p-3 mt-2" id="checkoutItems" style="max-height: 240px; overflow-y: auto; background: #f8f9fa;">
|
||||
<!-- Состав заказа -->
|
||||
<div class="mb-2">
|
||||
<strong class="small">Состав заказа</strong>
|
||||
<div class="border rounded p-2 mt-1" id="checkoutItems" style="max-height: 180px; overflow-y: auto; background: #f8f9fa; font-size: 0.9rem;">
|
||||
<!-- Заполняется из JS -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Опции оплаты и комментарий -->
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="paymentMethod" class="form-label">Способ оплаты</label>
|
||||
<select class="form-select" id="paymentMethod">
|
||||
<option value="cash">Наличные</option>
|
||||
<option value="card">Карта</option>
|
||||
<option value="mixed">Смешанная оплата</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="orderNote" class="form-label">Комментарий</label>
|
||||
<input type="text" class="form-control" id="orderNote" placeholder="Примечание к заказу">
|
||||
</div>
|
||||
<!-- Комментарий к заказу -->
|
||||
<div class="mb-2">
|
||||
<label for="orderNote" class="form-label small mb-1">Комментарий</label>
|
||||
<input type="text" class="form-control form-control-sm" id="orderNote" placeholder="Примечание к заказу">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Правая колонка: ценообразование -->
|
||||
|
||||
<!-- Правая колонка: оплата -->
|
||||
<div class="col-md-5">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<strong>Ценообразование</strong>
|
||||
<div class="card mb-0">
|
||||
<div class="card-header bg-light py-2">
|
||||
<strong class="small">Оплата</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Базовая сумма -->
|
||||
<div class="mb-2">
|
||||
<small class="text-muted">Базовая сумма (корзина):</small>
|
||||
<div class="fw-bold" id="checkoutBasePrice">0.00 руб.</div>
|
||||
</div>
|
||||
|
||||
<!-- Скидка -->
|
||||
<div class="mb-2">
|
||||
<label for="discountType" class="form-label small">Скидка</label>
|
||||
<select class="form-select form-select-sm" id="discountType">
|
||||
<option value="none">Без скидки</option>
|
||||
<option value="percent">Процент (%)</option>
|
||||
<option value="amount">Сумма (руб.)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-2" id="discountValueBlock" style="display: none;">
|
||||
<label for="discountValue" class="form-label small">Значение скидки</label>
|
||||
<input type="number" class="form-control form-control-sm" id="discountValue"
|
||||
min="0" step="0.01" value="0" placeholder="0.00">
|
||||
</div>
|
||||
|
||||
<!-- Расчётная сумма со скидкой -->
|
||||
<div class="card-body p-2">
|
||||
<!-- Итого к оплате -->
|
||||
<div class="mb-2 pb-2 border-bottom">
|
||||
<small class="text-muted">Сумма со скидкой:</small>
|
||||
<div class="fw-bold text-success" id="checkoutDiscountedPrice">0.00 руб.</div>
|
||||
<small class="text-muted">Итого к оплате:</small>
|
||||
<div class="fw-bold text-success fs-5" id="checkoutFinalPrice">0.00 руб.</div>
|
||||
</div>
|
||||
|
||||
<!-- Ручная финальная сумма -->
|
||||
<div class="mb-0">
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="useManualPrice">
|
||||
<label class="form-check-label small" for="useManualPrice">
|
||||
Установить свою сумму (приоритет)
|
||||
</label>
|
||||
</div>
|
||||
<div id="manualPriceBlock" style="display: none;">
|
||||
<input type="number" class="form-control form-control-sm" id="manualPrice"
|
||||
min="0" step="0.01" placeholder="Введите сумму">
|
||||
|
||||
<!-- Переключатель режима -->
|
||||
<div class="mb-2">
|
||||
<label class="form-label fw-semibold small mb-1">Режим оплаты</label>
|
||||
<div class="btn-group w-100" role="group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-primary active"
|
||||
id="singlePaymentMode">
|
||||
Одним способом
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
id="mixedPaymentMode">
|
||||
Смешанная
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Итоговая сумма продажи -->
|
||||
<div class="alert alert-success mt-3 mb-0">
|
||||
<strong>Итого к оплате:</strong><br>
|
||||
<span class="fs-3" id="checkoutFinalPrice">0.00</span> руб.
|
||||
</div>
|
||||
|
||||
<!-- Контейнер для PaymentWidget -->
|
||||
<div id="paymentWidgetContainer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer py-2">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="button" class="btn btn-success btn-lg" id="confirmCheckoutBtn">
|
||||
<button type="button" class="btn btn-success" id="confirmCheckoutBtn">
|
||||
<i class="bi bi-check2-circle"></i> Подтвердить продажу
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user