- Исправлен баг отображения статуса: теперь сравнение с order.status.code вместо order.status - Добавлена обработка отсутствующего статуса (показывает 'Без статуса') - Пагинация истории заказов: добавлен якорь #ordersHistoryCollapse ко всем ссылкам - Автооткрытие collapse при переходе по пагинации через JavaScript - Плавная прокрутка к секции истории после раскрытия collapse (событие shown.bs.collapse) - Пользователь остаётся в секции истории заказов при переходе между страницами
411 lines
24 KiB
HTML
411 lines
24 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}{{ customer.full_name }}{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<h1>Клиент: {{ customer.full_name }}</h1>
|
||
<div>
|
||
<a href="{% url 'customers:customer-update' customer.pk %}" class="btn btn-primary">Редактировать</a>
|
||
<a href="{% url 'customers:customer-delete' customer.pk %}" class="btn btn-danger">Удалить</a>
|
||
<a href="{% url 'customers:customer-list' %}" class="btn btn-secondary">Назад к списку</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<!-- Customer Info -->
|
||
<div class="col-md-6">
|
||
<div class="card mb-4">
|
||
<div class="card-header">
|
||
<h5>Информация о клиенте</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<table class="table table-borderless">
|
||
<tr>
|
||
<th>Имя:</th>
|
||
<td>{{ customer.full_name }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Email:</th>
|
||
<td>{{ customer.email|default:"Не указано" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Телефон:</th>
|
||
<td>{{ customer.phone|default:"Не указано" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Сумма покупок:</th>
|
||
<td>{{ customer.total_spent|floatformat:2 }} руб.</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Общий долг по активным заказам:</th>
|
||
<td>
|
||
{% if total_debt > 0 %}
|
||
<span class="text-danger fw-bold">{{ total_debt|floatformat:2 }} руб.</span>
|
||
<small class="text-muted">(Кол-во заказов: {{ active_orders_count }})</small>
|
||
{% else %}
|
||
<span class="text-success">0.00 руб.</span>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Заметки:</th>
|
||
<td>{{ customer.notes|default:"Нет" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Дата создания:</th>
|
||
<td>{{ customer.created_at|date:"d.m.Y H:i" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Дата обновления:</th>
|
||
<td>{{ customer.updated_at|date:"d.m.Y H:i" }}</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Операции с кошельком -->
|
||
<div class="col-md-6">
|
||
<div class="card mb-4">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<h5 class="mb-0">Операции с кошельком клиента</h5>
|
||
<span>
|
||
{% if customer.wallet_balance > 0 %}
|
||
<span class="badge bg-success" style="font-size: 1.1em;">{{ customer.wallet_balance|floatformat:2 }} руб.</span>
|
||
{% elif customer.wallet_balance == 0 %}
|
||
<span class="badge bg-secondary" style="font-size: 1.1em;">{{ customer.wallet_balance|floatformat:2 }} руб.</span>
|
||
{% else %}
|
||
<span class="badge bg-danger" style="font-size: 1.1em;">{{ customer.wallet_balance|floatformat:2 }} руб.</span>
|
||
{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<!-- Пополнение -->
|
||
<div class="col-md-6">
|
||
<h6 class="text-success mb-3"><i class="bi bi-plus-circle"></i> Пополнение</h6>
|
||
<form method="post" action="{% url 'customers:wallet-deposit' customer.pk %}">
|
||
{% csrf_token %}
|
||
<div class="mb-3">
|
||
<label for="wallet_deposit_amount" class="form-label">Сумма, руб.</label>
|
||
<input type="number"
|
||
step="0.01"
|
||
min="0.01"
|
||
class="form-control"
|
||
id="wallet_deposit_amount"
|
||
name="amount"
|
||
placeholder="0.00"
|
||
required>
|
||
<div style="height: 1.25rem;"></div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="wallet_deposit_description" class="form-label">Описание</label>
|
||
<textarea class="form-control"
|
||
id="wallet_deposit_description"
|
||
name="description"
|
||
rows="2"
|
||
placeholder="Подарок, компенсация..."
|
||
required></textarea>
|
||
</div>
|
||
<button type="submit" class="btn btn-success w-100"><i class="bi bi-plus-circle"></i> Пополнить</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Возврат / списание -->
|
||
<div class="col-md-6">
|
||
<h6 class="text-danger mb-3"><i class="bi bi-dash-circle"></i> Списание</h6>
|
||
<form method="post" action="{% url 'customers:wallet-withdraw' customer.pk %}">
|
||
{% csrf_token %}
|
||
<div class="mb-3">
|
||
<label for="wallet_withdraw_amount" class="form-label">Сумма, руб.</label>
|
||
<input type="number"
|
||
step="0.01"
|
||
min="0.01"
|
||
max="{{ customer.wallet_balance }}"
|
||
class="form-control"
|
||
id="wallet_withdraw_amount"
|
||
name="amount"
|
||
placeholder="0.00"
|
||
required>
|
||
<small class="text-muted d-block" style="height: 1.25rem; line-height: 1.25rem;">Макс: {{ customer.wallet_balance|floatformat:2 }} р.</small>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="wallet_withdraw_description" class="form-label">Описание</label>
|
||
<textarea class="form-control"
|
||
id="wallet_withdraw_description"
|
||
name="description"
|
||
rows="2"
|
||
placeholder="Возврат наличными..."
|
||
required></textarea>
|
||
</div>
|
||
<button type="submit" class="btn btn-danger w-100"><i class="bi bi-dash-circle"></i> Списать</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-info mb-0 mt-3">
|
||
<i class="bi bi-info-circle"></i> Все операции логируются в истории ниже.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- История транзакций кошелька -->
|
||
<div class="col-md-12">
|
||
<div class="card mb-4">
|
||
<div class="card-header">
|
||
<button class="btn btn-link w-100 text-start text-decoration-none p-0 d-flex justify-content-between align-items-center"
|
||
type="button"
|
||
data-bs-toggle="collapse"
|
||
data-bs-target="#walletHistoryCollapse"
|
||
aria-expanded="false"
|
||
aria-controls="walletHistoryCollapse">
|
||
<h5 class="mb-0">История кошелька (последние 20)</h5>
|
||
<div>
|
||
<span class="badge bg-primary me-2">{{ wallet_transactions|length }}</span>
|
||
<i class="bi bi-chevron-down"></i>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
<div class="collapse" id="walletHistoryCollapse">
|
||
<div class="card-body">
|
||
{% if wallet_transactions %}
|
||
<div class="table-responsive">
|
||
<table class="table table-striped table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>Дата</th>
|
||
<th>Тип</th>
|
||
<th>Сумма</th>
|
||
<th>Описание</th>
|
||
<th>Заказ</th>
|
||
<th>Создал</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for transaction in wallet_transactions %}
|
||
<tr>
|
||
<td><small>{{ transaction.created_at|date:"d.m.Y H:i" }}</small></td>
|
||
<td>
|
||
{% if transaction.transaction_type == 'deposit' %}
|
||
<span class="badge bg-success">Пополнение</span>
|
||
{% elif transaction.transaction_type == 'spend' %}
|
||
<span class="badge bg-danger">Списание</span>
|
||
{% else %}
|
||
<span class="badge bg-warning">Корректировка</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if transaction.transaction_type == 'deposit' or transaction.transaction_type == 'adjustment' and transaction.amount > 0 %}
|
||
<span class="text-success fw-bold">+{{ transaction.amount|floatformat:2 }} руб.</span>
|
||
{% else %}
|
||
<span class="text-danger fw-bold">-{{ transaction.amount|floatformat:2 }} руб.</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>{{ transaction.description|default:"-" }}</td>
|
||
<td>
|
||
{% if transaction.order %}
|
||
<a href="{% url 'orders:order-detail' transaction.order.order_number %}" class="btn btn-sm btn-outline-primary">
|
||
#{{ transaction.order.order_number }}
|
||
</a>
|
||
{% else %}
|
||
-
|
||
{% endif %}
|
||
</td>
|
||
<td><small>{{ transaction.created_by.username|default:"-" }}</small></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<p class="text-muted mb-0">История транзакций пуста.</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- История заказов -->
|
||
<div class="col-md-12">
|
||
<div class="card mb-4">
|
||
<div class="card-header">
|
||
<button class="btn btn-link w-100 text-start text-decoration-none p-0 d-flex justify-content-between align-items-center"
|
||
type="button"
|
||
data-bs-toggle="collapse"
|
||
data-bs-target="#ordersHistoryCollapse"
|
||
aria-expanded="false"
|
||
aria-controls="ordersHistoryCollapse">
|
||
<h5 class="mb-0">История заказов</h5>
|
||
<div>
|
||
<span class="badge bg-primary me-2">{{ orders_page.paginator.count }}</span>
|
||
<a href="{% url 'orders:order-create' %}?customer={{ customer.pk }}"
|
||
class="btn btn-sm btn-success me-2"
|
||
onclick="event.stopPropagation();">
|
||
<i class="bi bi-plus-circle"></i> Новый заказ
|
||
</a>
|
||
<i class="bi bi-chevron-down"></i>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
<div class="collapse" id="ordersHistoryCollapse">
|
||
<div class="card-body">
|
||
{% if orders_page %}
|
||
<div class="table-responsive">
|
||
<table class="table table-striped table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>№</th>
|
||
<th>Дата создания</th>
|
||
<th>Дата доставки</th>
|
||
<th>Статус</th>
|
||
<th>Оплата</th>
|
||
<th>Сумма</th>
|
||
<th>Оплачено</th>
|
||
<th>Остаток</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for order in orders_page %}
|
||
<tr>
|
||
<td><strong>#{{ order.order_number }}</strong></td>
|
||
<td><small>{{ order.created_at|date:"d.m.Y H:i" }}</small></td>
|
||
<td>
|
||
{% if order.delivery_date %}
|
||
<strong>{{ order.delivery_date|date:"d.m.Y" }}</strong>
|
||
{% if order.delivery_time %}
|
||
<br><small class="text-muted">{{ order.delivery_time }}</small>
|
||
{% endif %}
|
||
{% if order.is_delivery %}
|
||
<br><span class="badge bg-info">Доставка</span>
|
||
{% else %}
|
||
<br><span class="badge bg-secondary">Самовывоз</span>
|
||
{% endif %}
|
||
{% else %}
|
||
<span class="text-muted">Не указана</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if order.status %}
|
||
{% if order.status.code == 'draft' %}
|
||
<span class="badge bg-secondary">Черновик</span>
|
||
{% elif order.status.code == 'pending' %}
|
||
<span class="badge bg-warning">Ожидает</span>
|
||
{% elif order.status.code == 'in_production' %}
|
||
<span class="badge bg-info">В производстве</span>
|
||
{% elif order.status.code == 'ready' %}
|
||
<span class="badge bg-primary">Готов</span>
|
||
{% elif order.status.code == 'delivered' %}
|
||
<span class="badge bg-success">Доставлен</span>
|
||
{% elif order.status.code == 'cancelled' %}
|
||
<span class="badge bg-danger">Отменён</span>
|
||
{% else %}
|
||
<span class="badge bg-secondary">{{ order.status.name }}</span>
|
||
{% endif %}
|
||
{% else %}
|
||
<span class="badge bg-secondary">Без статуса</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if order.payment_status == 'paid' %}
|
||
<span class="badge bg-success">Оплачено</span>
|
||
{% elif order.payment_status == 'partial' %}
|
||
<span class="badge bg-warning">Частично</span>
|
||
{% else %}
|
||
<span class="badge bg-danger">Не оплачено</span>
|
||
{% endif %}
|
||
</td>
|
||
<td><strong>{{ order.total_amount|floatformat:2 }} руб.</strong></td>
|
||
<td>
|
||
{% if order.amount_paid > 0 %}
|
||
<span class="text-success">{{ order.amount_paid|floatformat:2 }} руб.</span>
|
||
{% else %}
|
||
<span class="text-muted">0.00 руб.</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if order.amount_due > 0 %}
|
||
<span class="text-danger fw-bold">{{ order.amount_due|floatformat:2 }} руб.</span>
|
||
{% else %}
|
||
<span class="text-success">0.00 руб.</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<a href="{% url 'orders:order-detail' order.order_number %}" class="btn btn-sm btn-outline-primary">
|
||
<i class="bi bi-eye"></i>
|
||
</a>
|
||
<a href="{% url 'orders:order-update' order.order_number %}" class="btn btn-sm btn-outline-secondary">
|
||
<i class="bi bi-pencil"></i>
|
||
</a>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Пагинация -->
|
||
{% if orders_page.has_other_pages %}
|
||
<nav aria-label="Навигация по заказам">
|
||
<ul class="pagination justify-content-center mt-3">
|
||
{% if orders_page.has_previous %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page=1#ordersHistoryCollapse">Первая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ orders_page.previous_page_number }}#ordersHistoryCollapse">Предыдущая</a>
|
||
</li>
|
||
{% endif %}
|
||
|
||
<li class="page-item active">
|
||
<span class="page-link">
|
||
Страница {{ orders_page.number }} из {{ orders_page.paginator.num_pages }}
|
||
</span>
|
||
</li>
|
||
|
||
{% if orders_page.has_next %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ orders_page.next_page_number }}#ordersHistoryCollapse">Следующая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ orders_page.paginator.num_pages }}#ordersHistoryCollapse">Последняя</a>
|
||
</li>
|
||
{% endif %}
|
||
</ul>
|
||
</nav>
|
||
{% endif %}
|
||
{% else %}
|
||
<p class="text-muted mb-0">У клиента пока нет заказов.</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Автооткрытие collapse при наличии якоря в URL
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const hash = window.location.hash;
|
||
if (hash === '#ordersHistoryCollapse') {
|
||
const collapseElement = document.getElementById('ordersHistoryCollapse');
|
||
if (collapseElement) {
|
||
const bsCollapse = new bootstrap.Collapse(collapseElement, {
|
||
show: true
|
||
});
|
||
// Прокручиваем к элементу после открытия collapse (с учетом анимации)
|
||
collapseElement.addEventListener('shown.bs.collapse', function() {
|
||
collapseElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}, { once: true });
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %} |