- Информация о клиенте — левая колонка (col-md-6) - Операции с кошельком — правая колонка (col-md-6) - Баланс кошелька перенесён из таблицы в заголовок блока операций - История кошелька и история заказов остаются в полную ширину - Компактный вертикальный layout форм в правой колонке
360 lines
21 KiB
HTML
360 lines
21 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">
|
||
<!-- Пополнение -->
|
||
<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>
|
||
<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 mb-4"><i class="bi bi-plus-circle"></i> Пополнить кошелёк</button>
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<!-- Возврат / списание -->
|
||
<h6 class="text-danger mb-3 mt-4"><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">Макс: {{ 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 class="alert alert-info mb-0 mt-4">
|
||
<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 d-flex justify-content-between align-items-center">
|
||
<h5>История кошелька (последние 20)</h5>
|
||
<span class="badge bg-primary">{{ wallet_transactions|length }}</span>
|
||
</div>
|
||
<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 class="col-md-12">
|
||
<div class="card mb-4">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<h5>История заказов</h5>
|
||
<div>
|
||
<span class="badge bg-primary">{{ orders_page.paginator.count }}</span>
|
||
<a href="{% url 'orders:order-create' %}?customer={{ customer.pk }}" class="btn btn-sm btn-success ms-2">
|
||
<i class="bi bi-plus-circle"></i> Новый заказ
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<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 == 'draft' %}
|
||
<span class="badge bg-secondary">Черновик</span>
|
||
{% elif order.status == 'pending' %}
|
||
<span class="badge bg-warning">Ожидает</span>
|
||
{% elif order.status == 'in_production' %}
|
||
<span class="badge bg-info">В производстве</span>
|
||
{% elif order.status == 'ready' %}
|
||
<span class="badge bg-primary">Готов</span>
|
||
{% elif order.status == 'delivered' %}
|
||
<span class="badge bg-success">Доставлен</span>
|
||
{% elif order.status == 'cancelled' %}
|
||
<span class="badge bg-danger">Отменён</span>
|
||
{% else %}
|
||
<span class="badge bg-secondary">{{ order.get_status_display }}</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">Первая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ orders_page.previous_page_number }}">Предыдущая</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 }}">Следующая</a>
|
||
</li>
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ orders_page.paginator.num_pages }}">Последняя</a>
|
||
</li>
|
||
{% endif %}
|
||
</ul>
|
||
</nav>
|
||
{% endif %}
|
||
{% else %}
|
||
<p class="text-muted mb-0">У клиента пока нет заказов.</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %} |