Corp/backend/templates/procurement/request_detail.html
Игорь Надеин 53a8f0e6b4 feat: add procurement frontend templates and styles
Stage 6 - Frontend Implementation:
- Add procurement.css with iOS-style components
- Add dashboard, request_list, request_detail, request_form templates
- Add equipment_list, equipment_detail templates
- Add status badges and filters partials
- Add JS handlers for dashboard, requests and equipment
- Add procurement link to sidebar navigation
- Configure separate URL namespaces (procurement for API, procurement_frontend for views)

All 66 tests passing.
2025-12-09 12:03:58 +03:00

274 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% load static %}
{% block title %}Заявка #{{ request_obj.id }}{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/components/page-header.css' %}">
<link rel="stylesheet" href="{% static 'css/components/procurement.css' %}">
{% endblock %}
{% block content %}
<div class="container">
{# ===== Хлебные крошки ===== #}
<nav aria-label="breadcrumb" class="mb-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'procurement_frontend:dashboard' %}">Закупки</a></li>
<li class="breadcrumb-item"><a href="{% url 'procurement_frontend:request_list' %}">Заявки</a></li>
<li class="breadcrumb-item active" aria-current="page">#{{ request_obj.id }}</li>
</ol>
</nav>
{# ===== Заголовок ===== #}
<div class="card mb-4">
<div class="card-body">
<div class="d-flex align-items-start gap-3">
<div class="proc-urgency-icon {{ request_obj.urgency }}" style="width: 48px; height: 48px; font-size: 1.25rem;">
{% if request_obj.urgency == 'critical' %}
<i class="bi-exclamation-triangle-fill"></i>
{% elif request_obj.urgency == 'high' %}
<i class="bi-arrow-up-circle"></i>
{% elif request_obj.urgency == 'medium' %}
<i class="bi-dash-circle"></i>
{% else %}
<i class="bi-arrow-down-circle"></i>
{% endif %}
</div>
<div class="flex-grow-1">
<h3 class="mb-1">{{ request_obj.title }}</h3>
<div class="d-flex flex-wrap gap-3 text-muted small">
<span><i class="bi-building me-1"></i>{{ request_obj.department.name }}</span>
<span><i class="bi-person me-1"></i>{{ request_obj.requestor.get_full_name }}</span>
<span><i class="bi-calendar me-1"></i>{{ request_obj.created_at|date:"d.m.Y H:i" }}</span>
</div>
</div>
<div class="text-end">
{% include "procurement/_status_badge.html" with status=request_obj.status %}
<div class="mt-2 h4 mb-0">{{ request_obj.estimated_cost|floatformat:0 }} ₽</div>
</div>
</div>
</div>
</div>
<div class="row g-4">
{# ===== Основная информация ===== #}
<div class="col-lg-8">
{# Описание #}
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi-text-paragraph me-2"></i>Описание</h5>
</div>
<div class="card-body">
{% if request_obj.description %}
<p class="mb-0" style="white-space: pre-wrap;">{{ request_obj.description }}</p>
{% else %}
<p class="text-muted mb-0">Описание не указано</p>
{% endif %}
</div>
</div>
{# Позиции заявки #}
<div class="card mb-4">
<div class="card-header d-flex align-items-center justify-content-between">
<h5 class="mb-0"><i class="bi-list-check me-2"></i>Позиции ({{ request_obj.items.count }})</h5>
{% if can_edit %}
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#addItemModal">
<i class="bi-plus-lg"></i> Добавить
</button>
{% endif %}
</div>
<div class="card-body p-0">
{% if request_obj.items.exists %}
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Наименование</th>
<th class="text-center" style="width: 80px;">Кол-во</th>
<th class="text-end" style="width: 120px;">Цена</th>
<th class="text-end" style="width: 120px;">Сумма</th>
<th style="width: 60px;"></th>
</tr>
</thead>
<tbody>
{% for item in request_obj.items.all %}
<tr>
<td>
<div class="fw-medium">{{ item.name }}</div>
{% if item.specifications %}
<small class="text-muted">{{ item.specifications|truncatechars:50 }}</small>
{% endif %}
</td>
<td class="text-center">{{ item.quantity }}</td>
<td class="text-end">{{ item.unit_price|floatformat:0 }} ₽</td>
<td class="text-end fw-medium">{{ item.total_price|floatformat:0 }} ₽</td>
<td>
{% if can_edit %}
<button class="btn btn-sm btn-link text-danger p-0" title="Удалить">
<i class="bi-trash"></i>
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot class="table-light">
<tr>
<td colspan="3" class="text-end fw-bold">Итого:</td>
<td class="text-end fw-bold">{{ request_obj.total_amount|floatformat:0 }} ₽</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
{% else %}
<div class="text-center py-4 text-muted">
<i class="bi-inbox fs-3 d-block mb-2"></i>
<p class="mb-0">Позиции не добавлены</p>
</div>
{% endif %}
</div>
</div>
{# История согласования #}
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi-clock-history me-2"></i>История согласования</h5>
</div>
<div class="card-body">
{% if request_obj.approvals.exists %}
<div class="timeline">
{% for approval in request_obj.approvals.all %}
<div class="d-flex gap-3 mb-3">
<div class="flex-shrink-0">
{% if approval.status == 'approved' %}
<span class="badge bg-success rounded-circle p-2"><i class="bi-check"></i></span>
{% elif approval.status == 'rejected' %}
<span class="badge bg-danger rounded-circle p-2"><i class="bi-x"></i></span>
{% else %}
<span class="badge bg-secondary rounded-circle p-2"><i class="bi-hourglass"></i></span>
{% endif %}
</div>
<div>
<div class="fw-medium">{{ approval.approver.get_full_name }}</div>
<div class="text-muted small">
{% if approval.decided_at %}
{{ approval.decided_at|date:"d.m.Y H:i" }}
{% else %}
Ожидает решения
{% endif %}
</div>
{% if approval.comment %}
<div class="mt-1 small">{{ approval.comment }}</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted mb-0">Согласования ещё не начаты</p>
{% endif %}
</div>
</div>
</div>
{# ===== Боковая панель ===== #}
<div class="col-lg-4">
{# Действия #}
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi-gear me-2"></i>Действия</h5>
</div>
<div class="card-body d-grid gap-2">
{% if can_edit and request_obj.status == 'draft' %}
<button class="btn btn-primary" id="submitRequest">
<i class="bi-send me-2"></i>Отправить на согласование
</button>
<a href="#" class="btn btn-outline-secondary">
<i class="bi-pencil me-2"></i>Редактировать
</a>
{% endif %}
{% if can_approve and request_obj.status == 'pending' %}
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#approveModal">
<i class="bi-check-lg me-2"></i>Одобрить
</button>
<button class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#rejectModal">
<i class="bi-x-lg me-2"></i>Отклонить
</button>
{% endif %}
<a href="{% url 'procurement_frontend:request_list' %}" class="btn btn-outline-secondary">
<i class="bi-arrow-left me-2"></i>К списку
</a>
</div>
</div>
{# Информация #}
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi-info-circle me-2"></i>Информация</h5>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted">Срочность</span>
<span>
{% if request_obj.urgency == 'critical' %}🔴 Критическая
{% elif request_obj.urgency == 'high' %}🟠 Высокая
{% elif request_obj.urgency == 'medium' %}🟡 Средняя
{% else %}🟢 Низкая{% endif %}
</span>
</li>
{% if request_obj.desired_date %}
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted">Желаемая дата</span>
<span>{{ request_obj.desired_date|date:"d.m.Y" }}</span>
</li>
{% endif %}
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted">Создано</span>
<span>{{ request_obj.created_at|date:"d.m.Y H:i" }}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted">Обновлено</span>
<span>{{ request_obj.updated_at|date:"d.m.Y H:i" }}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Отправка на согласование
document.getElementById('submitRequest')?.addEventListener('click', async () => {
if (!confirm('Отправить заявку на согласование?')) return;
try {
const resp = await fetch('/api/procurement/requests/{{ request_obj.id }}/submit/', {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}'
}
});
if (resp.ok) {
window.location.reload();
} else {
const err = await resp.json();
alert(err.error || 'Ошибка отправки');
}
} catch (e) {
console.error(e);
alert('Ошибка сети');
}
});
</script>
{% endblock %}