Исправлена форма заказа: две колонки и корректная работа кнопки сохранения
- Разделен экран на две колонки: заказ слева, оплата справа - Форма оплаты вынесена за пределы основной формы заказа (устранена проблема вложенных форм) - Исправлен метод calculate_total() для сохранения итоговой суммы в БД - Добавлена модель Transaction для учета платежей и возвратов - Добавлена модель PaymentMethod для методов оплаты - Удалена старая модель Payment, заменена на Transaction - Добавлен TransactionService для управления транзакциями - Обновлен интерфейс форм оплаты для правой колонки - Кнопка 'Сохранить изменения' теперь работает корректно
This commit is contained in:
@@ -296,15 +296,7 @@
|
||||
<div class="card-body">
|
||||
<div class="row mb-2">
|
||||
<div class="col-6"><strong>Товары:</strong></div>
|
||||
<div class="col-6 text-end">
|
||||
{% with items_total=order.items.all|length %}
|
||||
{% if items_total > 0 %}
|
||||
{{ order.total_amount|floatformat:2 }} руб.
|
||||
{% else %}
|
||||
0.00 руб.
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="col-6 text-end">{{ order.subtotal|floatformat:2 }} руб.</div>
|
||||
</div>
|
||||
{% if order.is_delivery %}
|
||||
<div class="row mb-2">
|
||||
@@ -340,25 +332,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- История платежей -->
|
||||
{% if order.payments.all %}
|
||||
<!-- История транзакций -->
|
||||
{% if order.transactions.exists %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">История платежей</h5>
|
||||
<h5 class="mb-0">История транзакций</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for payment in order.payments.all %}
|
||||
{% for transaction in order.transactions.all|dictsortreversed:"transaction_date" %}
|
||||
<li class="list-group-item">
|
||||
<div><strong>{{ payment.amount|floatformat:2 }} руб.</strong></div>
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<strong class="{% if transaction.transaction_type == 'payment' %}text-success{% else %}text-danger{% endif %}">
|
||||
{% if transaction.transaction_type == 'refund' %}−{% else %}+{% endif %}{{ transaction.amount|floatformat:2 }} руб.
|
||||
</strong>
|
||||
</div>
|
||||
<div>
|
||||
{% if transaction.transaction_type == 'payment' %}
|
||||
<span class="badge bg-success">Платёж</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark">Возврат</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
{{ payment.payment_date|date:"d.m.Y H:i" }}<br>
|
||||
{{ payment.payment_method.name }}
|
||||
{% if payment.notes %}
|
||||
<br><em>{{ payment.notes }}</em>
|
||||
{{ transaction.transaction_date|date:"d.m.Y H:i" }}<br>
|
||||
{{ transaction.payment_method.name }}
|
||||
{% if transaction.notes or transaction.reason %}
|
||||
<br><em>{{ transaction.notes|default:transaction.reason }}</em>
|
||||
{% endif %}
|
||||
{% if payment.created_by %}
|
||||
<br>Принял: {{ payment.created_by.get_full_name }}
|
||||
{% if transaction.created_by %}
|
||||
<br>Кем: {{ transaction.created_by.get_short_name|default:transaction.created_by.username }}
|
||||
{% endif %}
|
||||
</small>
|
||||
</li>
|
||||
|
||||
@@ -114,91 +114,95 @@
|
||||
<form method="post" id="order-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Основная информация -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Основная информация</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.customer.id_for_label }}" class="form-label">
|
||||
Клиент <span class="text-danger">*</span>
|
||||
</label>
|
||||
{% if preselected_customer %}
|
||||
<select name="customer" class="form-select" id="id_customer">
|
||||
<option value="{{ preselected_customer.pk }}" selected data-name="{{ preselected_customer.name }}" data-phone="{{ preselected_customer.phone|default:'' }}" data-email="{{ preselected_customer.email|default:'' }}">
|
||||
{{ preselected_customer.name }}{% if preselected_customer.phone %} ({{ preselected_customer.phone }}){% endif %}
|
||||
</option>
|
||||
</select>
|
||||
{% else %}
|
||||
{{ form.customer }}
|
||||
{% endif %}
|
||||
{% if form.customer.errors %}
|
||||
<div class="text-danger">{{ form.customer.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Две колонки -->
|
||||
<div class="row">
|
||||
<!-- Левая колонка: Заказ -->
|
||||
<div class="col-lg-7">
|
||||
<!-- Основная информация -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Основная информация</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.status.id_for_label }}" class="form-label">
|
||||
Статус <span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.status }}
|
||||
{% if form.status.errors %}
|
||||
<div class="text-danger">{{ form.status.errors }}</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.customer.id_for_label }}" class="form-label">
|
||||
Клиент <span class="text-danger">*</span>
|
||||
</label>
|
||||
{% if preselected_customer %}
|
||||
<select name="customer" class="form-select" id="id_customer">
|
||||
<option value="{{ preselected_customer.pk }}" selected data-name="{{ preselected_customer.name }}" data-phone="{{ preselected_customer.phone|default:'' }}" data-email="{{ preselected_customer.email|default:'' }}">
|
||||
{{ preselected_customer.name }}{% if preselected_customer.phone %} ({{ preselected_customer.phone }}){% endif %}
|
||||
</option>
|
||||
</select>
|
||||
{% else %}
|
||||
{{ form.customer }}
|
||||
{% endif %}
|
||||
{% if form.customer.errors %}
|
||||
<div class="text-danger">{{ form.customer.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.status.id_for_label }}" class="form-label">
|
||||
Статус <span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.status }}
|
||||
{% if form.status.errors %}
|
||||
<div class="text-danger">{{ form.status.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Дата и время доставки -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Дата и время доставки</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_date.id_for_label }}" class="form-label">Дата</label>
|
||||
{{ form.delivery_date }}
|
||||
{% if form.delivery_date.errors %}
|
||||
<div class="text-danger">{{ form.delivery_date.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Дата и время доставки -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Дата и время доставки</h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_time_start.id_for_label }}" class="form-label">Время от</label>
|
||||
{{ form.delivery_time_start }}
|
||||
{% if form.delivery_time_start.errors %}
|
||||
<div class="text-danger">{{ form.delivery_time_start.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_time_end.id_for_label }}" class="form-label">Время до</label>
|
||||
{{ form.delivery_time_end }}
|
||||
{% if form.delivery_time_end.errors %}
|
||||
<div class="text-danger">{{ form.delivery_time_end.errors }}</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_date.id_for_label }}" class="form-label">Дата</label>
|
||||
{{ form.delivery_date }}
|
||||
{% if form.delivery_date.errors %}
|
||||
<div class="text-danger">{{ form.delivery_date.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_time_start.id_for_label }}" class="form-label">Время от</label>
|
||||
{{ form.delivery_time_start }}
|
||||
{% if form.delivery_time_start.errors %}
|
||||
<div class="text-danger">{{ form.delivery_time_start.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.delivery_time_end.id_for_label }}" class="form-label">Время до</label>
|
||||
{{ form.delivery_time_end }}
|
||||
{% if form.delivery_time_end.errors %}
|
||||
<div class="text-danger">{{ form.delivery_time_end.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Товары в заказе -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Товары в заказе</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Товары в заказе</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ formset.management_form }}
|
||||
<div id="order-items-container">
|
||||
{% for item_form in formset %}
|
||||
@@ -335,22 +339,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<button type="button" class="btn btn-success" id="add-item-btn">
|
||||
<i class="bi bi-plus-circle"></i> Добавить товар
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary ms-2" id="create-temp-kit-btn" data-bs-toggle="modal" data-bs-target="#tempKitModal">
|
||||
<i class="bi bi-flower1"></i> Создать и добавить комплект
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="add-item-btn">
|
||||
<i class="bi bi-plus-circle"></i> Добавить товар
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary ms-2" id="create-temp-kit-btn" data-bs-toggle="modal" data-bs-target="#tempKitModal">
|
||||
<i class="bi bi-flower1"></i> Создать и добавить комплект
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Доставка -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Доставка</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Скрытое поле для передачи значения is_delivery -->
|
||||
<!-- Доставка -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Доставка</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Скрытое поле для передачи значения is_delivery -->
|
||||
<div style="display: none;">
|
||||
<input type="checkbox"
|
||||
id="{{ form.is_delivery.id_for_label }}"
|
||||
@@ -375,8 +379,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Поля доставки (показываются только если выбрана доставка) -->
|
||||
<div id="delivery-mode-fields" style="display: block;">
|
||||
<!-- Поля доставки (показываются только если выбрана доставка) -->
|
||||
<div id="delivery-mode-fields" style="display: block;">
|
||||
<!-- Поля ввода адреса доставки -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@@ -549,167 +553,223 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="pickup-fields" style="display: none;">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.pickup_warehouse.id_for_label }}" class="form-label">
|
||||
Склад для самовывоза
|
||||
</label>
|
||||
{{ form.pickup_warehouse }}
|
||||
{% if form.pickup_warehouse.errors %}
|
||||
<div class="text-danger">{{ form.pickup_warehouse.errors }}</div>
|
||||
{% endif %}
|
||||
<!-- Поля самовывоза (показываются только если выбран самовывоз) -->
|
||||
<div class="row" id="pickup-fields" style="display: none;">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.pickup_warehouse.id_for_label }}" class="form-label">
|
||||
Склад для самовывоза
|
||||
</label>
|
||||
{{ form.pickup_warehouse }}
|
||||
{% if form.pickup_warehouse.errors %}
|
||||
<div class="text-danger">{{ form.pickup_warehouse.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительно -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-three-dots"></i> Дополнительно</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.is_anonymous }}
|
||||
<label class="form-check-label" for="{{ form.is_anonymous.id_for_label }}">
|
||||
Анонимная доставка
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.special_instructions.id_for_label }}" class="form-label">Особые пожелания</label>
|
||||
{{ form.special_instructions }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Конец левой колонки -->
|
||||
|
||||
|
||||
<!-- Оплата (смешанная оплата) -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-credit-card"></i> Оплата</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Блок кошелька клиента -->
|
||||
{% if order.customer %}
|
||||
<div class="alert alert-info mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong><i class="bi bi-wallet2"></i> Кошелёк клиента:</strong>
|
||||
{% if order.customer.wallet_balance > 0 %}
|
||||
<span class="text-success fw-bold">{{ order.customer.wallet_balance|floatformat:2 }} руб.</span>
|
||||
{% else %}
|
||||
<span class="text-muted">0.00 руб.</span>
|
||||
<!-- Правая колонка: Оплата -->
|
||||
<div class="col-lg-5">
|
||||
<!-- Оплата и возвраты -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-credit-card"></i> Оплата</h5>
|
||||
{% if order.pk and order.amount_paid > 0 %}
|
||||
<span class="badge bg-secondary">Доступно для возврата: {{ order.amount_paid|floatformat:2 }} руб.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Остаток к оплате:</strong>
|
||||
<span class="text-primary fw-bold">{{ order.amount_due|floatformat:2 }} руб.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Уже сохраненные платежи -->
|
||||
{% if order.pk and order.payments.exists %}
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-3"><i class="bi bi-check-circle"></i> Проведенные платежи</h6>
|
||||
{% for payment in order.payments.all %}
|
||||
<div class="border rounded p-3 mb-2 bg-light">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4">
|
||||
<small class="text-muted d-block">Способ оплаты</small>
|
||||
<strong>{{ payment.payment_method.name }}</strong>
|
||||
<!-- Блок статистики -->
|
||||
{% if order.pk %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body text-center p-2">
|
||||
<small class="text-muted d-block">Сумма заказа</small>
|
||||
<h6 class="mb-0 text-primary">{{ order.total_amount|floatformat:2 }} руб.</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted d-block">Сумма</small>
|
||||
<strong class="text-success">{{ payment.amount|floatformat:2 }} руб.</strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<small class="text-muted d-block">Примечания</small>
|
||||
<span class="text-muted">{{ payment.notes|default:"—" }}</span>
|
||||
</div>
|
||||
<div class="col-md-1 text-end">
|
||||
<form method="post" action="{% url 'orders:payment-delete' order.order_number payment.id %}" style="display: inline;">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm"
|
||||
onclick="return confirm('Удалить платеж {{ payment.payment_method.name }} на сумму {{ payment.amount|floatformat:2 }} руб.?');"
|
||||
title="Удалить платеж">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
<div class="col-md-6">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center p-2">
|
||||
<small class="text-muted d-block">Оплачено</small>
|
||||
<h6 class="mb-0 text-success">{{ order.amount_paid|floatformat:2 }} руб.</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Итоговая сумма платежей -->
|
||||
{% if order.pk %}
|
||||
<div class="border-top pt-3 mt-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<p class="mb-0 text-muted"><i class="bi bi-cash-stack"></i> Всего внесено:</p>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-{% if order.amount_due > 0 %}warning{% else %}success{% endif %}">
|
||||
<div class="card-body text-center p-2">
|
||||
<small class="text-muted d-block">Остаток</small>
|
||||
<h6 class="mb-0 text-{% if order.amount_due > 0 %}warning{% else %}success{% endif %}">{{ order.amount_due|floatformat:2 }} руб.</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if order.customer %}
|
||||
<div class="col-md-6">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center p-2">
|
||||
<small class="text-muted d-block"><i class="bi bi-wallet2"></i> Кошелёк</small>
|
||||
<h6 class="mb-0 text-info">{{ order.customer.wallet_balance|floatformat:2 }} руб.</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<h5 class="mb-0 text-success">
|
||||
{{ order.amount_paid|default:"0.00"|floatformat:2 }} руб.
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Форма добавления нового платежа (ОТДЕЛЬНАЯ форма внутри card-body) -->
|
||||
{% if order.pk %}
|
||||
<div class="border-top pt-3 mt-3">
|
||||
<h6 class="mb-3"><i class="bi bi-plus-circle"></i> Добавить новый платеж</h6>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма добавления платежа (ВНЕ основной формы, но сразу после секции Оплата) -->
|
||||
{% if order.pk %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<form method="post" action="{% url 'orders:payment-add' order.order_number %}" id="payment-add-form">
|
||||
{% csrf_token %}
|
||||
<div class="row align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Способ оплаты</label>
|
||||
<select name="payment_method" class="form-select" required>
|
||||
<option value="">---------</option>
|
||||
{% load orders_tags %}
|
||||
{% get_payment_methods as payment_methods %}
|
||||
{% for pm in payment_methods %}
|
||||
<option value="{{ pm.id }}">{{ pm.name }}</option>
|
||||
<!-- История транзакций -->
|
||||
{% if order.pk and order.transactions.exists %}
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted mb-2"><i class="bi bi-clock-history"></i> История транзакций</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th width="15%">Дата</th>
|
||||
<th width="10%">Тип</th>
|
||||
<th width="20%">Способ оплаты</th>
|
||||
<th width="12%" class="text-end">Сумма</th>
|
||||
<th width="30%">Примечания</th>
|
||||
<th width="8%">Кем</th>
|
||||
<th width="5%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for transaction in order.transactions.all|dictsortreversed:"transaction_date" %}
|
||||
<tr>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
{{ transaction.transaction_date|date:"d.m.Y H:i" }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{% if transaction.transaction_type == 'payment' %}
|
||||
<span class="badge bg-success">Платёж</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark">Возврат</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ transaction.payment_method.name }}</td>
|
||||
<td class="text-end">
|
||||
<strong class="{% if transaction.transaction_type == 'payment' %}text-success{% else %}text-danger{% endif %}">
|
||||
{% if transaction.transaction_type == 'refund' %}−{% else %}+{% endif %}{{ transaction.amount|floatformat:2 }}
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
{{ transaction.notes|default:transaction.reason|default:"—"|truncatewords:8 }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
{% if transaction.created_by %}
|
||||
{{ transaction.created_by.get_short_name|default:transaction.created_by.username }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</small>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<form method="post" action="{% url 'orders:transaction-delete' order.order_number transaction.id %}" style="display: inline;">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm"
|
||||
onclick="return confirm('⚠️ Удаление нарушает историю транзакций!\n\nВместо удаления рекомендуется создать возврат.\n\nУдалить транзакцию {{ transaction.get_transaction_type_display }} на сумму {{ transaction.amount|floatformat:2 }} руб.?');"
|
||||
title="Удалить транзакцию">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Сумма</label>
|
||||
<input type="number" name="amount" step="0.01" min="0.01" class="form-control" placeholder="0.00" required>
|
||||
{% elif order.pk %}
|
||||
<div class="alert alert-light mb-3">
|
||||
<i class="bi bi-info-circle"></i> Транзакции по заказу отсутствуют
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Примечания</label>
|
||||
<input type="text" name="notes" class="form-control" placeholder="Опционально">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="bi bi-plus-lg"></i> Добавить
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Формы добавления платежа и возврата -->
|
||||
{% if order.pk %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-tabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="payment-tab" data-bs-toggle="tab" data-bs-target="#payment-form" type="button" role="tab">
|
||||
<i class="bi bi-plus-circle"></i> Добавить платёж
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="refund-tab" data-bs-toggle="tab" data-bs-target="#refund-form" type="button" role="tab" {% if order.amount_paid <= 0 %}disabled{% endif %}>
|
||||
<i class="bi bi-arrow-return-left"></i> Создать возврат
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<!-- Форма добавления платежа -->
|
||||
<div class="tab-pane fade show active" id="payment-form" role="tabpanel">
|
||||
<div class="row align-items-end">
|
||||
<div class="col-12 mb-2">
|
||||
<p class="text-muted"><i class="bi bi-info-circle"></i> Используйте форму ниже после сохранения заказа</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма создания возврата -->
|
||||
<div class="tab-pane fade" id="refund-form" role="tabpanel">
|
||||
<div class="alert alert-warning mb-3">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
<strong>Возврат средств клиенту</strong><br>
|
||||
<small>Используйте форму ниже после сохранения заказа</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-three-dots"></i> Дополнительно</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.is_anonymous }}
|
||||
<label class="form-check-label" for="{{ form.is_anonymous.id_for_label }}">
|
||||
Анонимная доставка
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.special_instructions.id_for_label }}" class="form-label">Особые пожелания</label>
|
||||
{{ form.special_instructions }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Конец правой колонки -->
|
||||
</div>
|
||||
<!-- Конец двух колонок -->
|
||||
|
||||
<!-- Кнопки -->
|
||||
<div class="row">
|
||||
@@ -736,6 +796,172 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Формы добавления платежа и возврата (ВЫНЕСЕНЫ ЗА ПРЕДЕЛЫ ОСНОВНОЙ ФОРМЫ) -->
|
||||
{% if order.pk %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-7">
|
||||
<!-- Пустое место для выравнивания -->
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-tabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="payment-tab-real" data-bs-toggle="tab" data-bs-target="#payment-form-real" type="button" role="tab">
|
||||
<i class="bi bi-plus-circle"></i> Добавить платёж
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="refund-tab-real" data-bs-toggle="tab" data-bs-target="#refund-form-real" type="button" role="tab" {% if order.amount_paid <= 0 %}disabled{% endif %}>
|
||||
<i class="bi bi-arrow-return-left"></i> Создать возврат
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<!-- Форма добавления платежа -->
|
||||
<div class="tab-pane fade show active" id="payment-form-real" role="tabpanel">
|
||||
<form method="post" action="{% url 'orders:transaction-add-payment' order.order_number %}" id="payment-add-form">
|
||||
{% csrf_token %}
|
||||
<div class="row align-items-end">
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Способ оплаты <span class="text-danger">*</span></label>
|
||||
<select name="payment_method" class="form-select" required>
|
||||
<option value="">Выберите способ...</option>
|
||||
{% load orders_tags %}
|
||||
{% get_payment_methods as payment_methods %}
|
||||
{% for pm in payment_methods %}
|
||||
<option value="{{ pm.id }}">{{ pm.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label fw-bold">Сумма <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount" step="0.01" min="0.01"
|
||||
class="form-control" placeholder="0.00"
|
||||
{% if order.amount_due > 0 %}value="{{ order.amount_due|unlocalize }}"{% endif %} required>
|
||||
<span class="input-group-text">руб.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label class="form-label fw-bold">Остаток</label>
|
||||
<div class="form-control-plaintext fw-bold text-warning">
|
||||
{{ order.amount_due|floatformat:2 }} руб.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Примечания</label>
|
||||
<input type="text" name="notes" class="form-control" placeholder="Опционально">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="bi bi-check-lg"></i> Добавить платёж
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Форма создания возврата -->
|
||||
<div class="tab-pane fade" id="refund-form-real" role="tabpanel">
|
||||
<form method="post" action="{% url 'orders:transaction-add-refund' order.order_number %}" id="refund-add-form">
|
||||
{% csrf_token %}
|
||||
<div class="alert alert-warning mb-3">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
<strong>Возврат средств клиенту</strong><br>
|
||||
<small>Можно вернуть любую сумму до {{ order.amount_paid|floatformat:2 }} руб. Возврат на кошелёк произойдёт автоматически при выборе соответствующего способа оплаты.</small>
|
||||
</div>
|
||||
<div class="row align-items-end">
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Способ возврата <span class="text-danger">*</span></label>
|
||||
<select name="refund_payment_method" class="form-select" required>
|
||||
<option value="">Выберите способ...</option>
|
||||
{% load orders_tags %}
|
||||
{% get_payment_methods as payment_methods %}
|
||||
{% for pm in payment_methods %}
|
||||
<option value="{{ pm.id }}">{{ pm.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Сумма возврата <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<input type="number" name="refund_amount" step="0.01" min="0.01"
|
||||
max="{{ order.amount_paid|unlocalize }}" class="form-control"
|
||||
placeholder="0.00" required>
|
||||
<span class="input-group-text">руб.</span>
|
||||
</div>
|
||||
<small class="text-muted">Макс: {{ order.amount_paid|floatformat:2 }}</small>
|
||||
</div>
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Причина <span class="text-danger">*</span></label>
|
||||
<input type="text" name="refund_reason" class="form-control"
|
||||
placeholder="Укажите причину возврата" required>
|
||||
</div>
|
||||
<div class="col-12 mb-2">
|
||||
<label class="form-label fw-bold">Примечания</label>
|
||||
<input type="text" name="refund_notes" class="form-control" placeholder="Опционально">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-warning w-100 text-dark">
|
||||
<i class="bi bi-arrow-return-left"></i> Вернуть средства
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
// Добавляем логирование для кнопки сохранения
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const orderForm = document.getElementById('order-form');
|
||||
const submitButtons = orderForm.querySelectorAll('button[type="submit"]');
|
||||
|
||||
console.log('[FORM DEBUG] Форма найдена:', orderForm);
|
||||
console.log('[FORM DEBUG] Кнопки submit найдены:', submitButtons.length);
|
||||
|
||||
submitButtons.forEach((btn, index) => {
|
||||
console.log('[FORM DEBUG] Кнопка', index, ':', btn);
|
||||
console.log('[FORM DEBUG] Кнопка disabled?', btn.disabled);
|
||||
console.log('[FORM DEBUG] Кнопка type:', btn.type);
|
||||
console.log('[FORM DEBUG] Текст кнопки:', btn.textContent.trim());
|
||||
|
||||
// Проверяем disabled при каждом клике
|
||||
btn.addEventListener('click', function(e) {
|
||||
console.log('[FORM DEBUG] Клик по кнопке!');
|
||||
console.log('[FORM DEBUG] Event:', e);
|
||||
console.log('[FORM DEBUG] Кнопка disabled на момент клика?', this.disabled);
|
||||
console.log('[FORM DEBUG] DefaultPrevented?', e.defaultPrevented);
|
||||
console.log('[FORM DEBUG] Форма валидна?', orderForm.checkValidity());
|
||||
|
||||
// Проверяем, не блокирует ли что-то отправку
|
||||
if (this.disabled) {
|
||||
console.error('[FORM DEBUG] ОШИБКА: Кнопка заблокирована!');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}, true); // Используем capturing phase
|
||||
});
|
||||
|
||||
// Слушаем событие submit формы
|
||||
orderForm.addEventListener('submit', function(e) {
|
||||
console.log('[FORM DEBUG] Событие submit формы!');
|
||||
console.log('[FORM DEBUG] Event:', e);
|
||||
console.log('[FORM DEBUG] DefaultPrevented?', e.defaultPrevented);
|
||||
}, true);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Глобально определяем initOrderItemSelect2
|
||||
window.initOrderItemSelect2 = function(element) {
|
||||
|
||||
Reference in New Issue
Block a user