Доделал журнал ошибок
This commit is contained in:
18
dbapp/mainapp/migrations/0029_dailyreport_location_place.py
Normal file
18
dbapp/mainapp/migrations/0029_dailyreport_location_place.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2025-12-16 06:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mainapp', '0028_remove_issue_type_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dailyreport',
|
||||
name='location_place',
|
||||
field=models.CharField(choices=[('kr', 'КР'), ('dv', 'ДВ')], default='kr', help_text='К какому комплексу принадлежит журнал', max_length=30, null=True, verbose_name='Комплекс'),
|
||||
),
|
||||
]
|
||||
@@ -30,6 +30,10 @@ class IssueType(models.Model):
|
||||
|
||||
class DailyReport(models.Model):
|
||||
"""Ежедневный отчёт"""
|
||||
PLACES = [
|
||||
("kr", "КР"),
|
||||
("dv", "ДВ")
|
||||
]
|
||||
date = models.DateField(
|
||||
unique=True,
|
||||
verbose_name="Дата",
|
||||
@@ -50,6 +54,14 @@ class DailyReport(models.Model):
|
||||
)
|
||||
explanation = models.TextField(blank=True, null=True, verbose_name='Пояснение')
|
||||
comment = models.TextField(blank=True, null=True, verbose_name='Комментарий')
|
||||
location_place = models.CharField(
|
||||
max_length=30,
|
||||
choices=PLACES,
|
||||
null=True,
|
||||
default="kr",
|
||||
verbose_name="Комплекс",
|
||||
help_text="К какому комплексу принадлежит журнал",
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создано")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлено")
|
||||
|
||||
@@ -128,7 +128,7 @@ class Band(models.Model):
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return f"{self.name}({int(self.border_start)}-{int(self.border_end)})МГц"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Диапазон"
|
||||
|
||||
@@ -29,43 +29,42 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:transponder_list' %}">Транспондеры</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'lyngsatapp:lyngsat_list' %}">Справочные данные</a>
|
||||
</li>
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:actions' %}">Действия</a>
|
||||
</li> -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:signal_marks' %}">Отметки сигналов</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:errors_report' %}">Журнал ошибок</a>
|
||||
</li>
|
||||
{% if user|has_perm:'kubsat_view' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:kubsat' %}">Кубсат</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mapsapp:3dmap' %}">3D карта</a>
|
||||
</li> -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mapsapp:2dmap' %}">Карта</a>
|
||||
|
||||
<!-- Дропдаун "Прочее" -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarOtherDropdown" role="button" data-bs-toggle="dropdown">
|
||||
Прочее
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{% url 'lyngsatapp:lyngsat_list' %}">Справочные данные</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'mainapp:signal_marks' %}">Отметки сигналов</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'mainapp:errors_report' %}">Журнал ошибок</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'mapsapp:2dmap' %}">Карта</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<!-- Дропдаун "Админ" (только для администраторов) -->
|
||||
{% if user.customuser.role == 'admin' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:user_permissions_list' %}">Разрешения</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user.customuser.role == 'admin' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'admin:index' %}">Админ панель</a>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarAdminDropdown" role="button" data-bs-toggle="dropdown">
|
||||
Админ
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{% url 'mainapp:user_permissions_list' %}">Разрешения</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'admin:index' %}">Админ панель</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<!-- Пользовательское меню -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarUserDropdown" role="button" data-bs-toggle="dropdown">
|
||||
{% if user.first_name and user.last_name %}
|
||||
{{ user.first_name }} {{ user.last_name }}
|
||||
{% elif user.get_full_name %}
|
||||
|
||||
@@ -44,8 +44,15 @@
|
||||
word-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
.filter-panel {
|
||||
margin-bottom: 15px;
|
||||
.btn-group .badge {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
font-size: 0.65rem;
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
.btn-group .btn {
|
||||
position: relative;
|
||||
}
|
||||
.btn-edit {
|
||||
padding: 2px 6px;
|
||||
@@ -78,23 +85,123 @@
|
||||
<div class="container-fluid mt-3">
|
||||
<h4>Журнал ошибок и неисправностей</h4>
|
||||
|
||||
<div class="filter-panel d-flex gap-3 align-items-end flex-wrap">
|
||||
<div>
|
||||
<label class="form-label">Дата с:</label>
|
||||
<input type="date" id="dateFrom" class="form-control form-control-sm">
|
||||
<!-- Toolbar -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<!-- Action buttons -->
|
||||
<div class="d-flex gap-2">
|
||||
{% if user|has_perm:'errors_report_create' %}
|
||||
<button class="btn btn-success btn-sm" onclick="openCreateModal()">
|
||||
<i class="bi bi-plus-lg"></i> Добавить запись
|
||||
</button>
|
||||
{% endif %}
|
||||
<!-- <button class="btn btn-primary btn-sm" onclick="loadData()">
|
||||
<i class="bi bi-arrow-clockwise"></i> Обновить
|
||||
</button> -->
|
||||
</div>
|
||||
|
||||
<!-- Filter Toggle Button -->
|
||||
<div>
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-bs-toggle="offcanvas"
|
||||
data-bs-target="#offcanvasFilters" aria-controls="offcanvasFilters">
|
||||
<i class="bi bi-funnel"></i> Фильтры
|
||||
<span id="filterCounter" class="badge bg-danger" style="display: none;">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Дата по:</label>
|
||||
<input type="date" id="dateTo" class="form-control form-control-sm">
|
||||
</div>
|
||||
|
||||
<!-- Offcanvas Filter Panel -->
|
||||
<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasFilters" aria-labelledby="offcanvasFiltersLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="offcanvasFiltersLabel">Фильтры</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<form id="filter-form">
|
||||
<!-- Date Filter -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Период
|
||||
</label>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Дата с:</label>
|
||||
<input type="date" id="dateFrom" name="date_from" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Дата по:</label>
|
||||
<input type="date" id="dateTo" name="date_to" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Location Filter -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Комплекс
|
||||
</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('locationFilter', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('locationFilter', false)">Снять</button>
|
||||
</div>
|
||||
<select id="locationFilter" name="location_place" class="form-select form-select-sm" multiple size="2">
|
||||
<option value="kr">КР</option>
|
||||
<option value="dv">ДВ</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Error Filter -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Ошибки
|
||||
</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('errorFilter', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('errorFilter', false)">Снять</button>
|
||||
</div>
|
||||
<select id="errorFilter" name="error_filter" class="form-select form-select-sm" multiple size="6">
|
||||
{% for e in errors %}
|
||||
<option value="{{ e.id }}">{{ e.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Malfunction Filter -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Неисправности
|
||||
</label>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('malfunctionFilter', true)">Выбрать</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="selectAllOptions('malfunctionFilter', false)">Снять</button>
|
||||
</div>
|
||||
<select id="malfunctionFilter" name="malfunction_filter" class="form-select form-select-sm" multiple size="6">
|
||||
{% for m in malfunctions %}
|
||||
<option value="{{ m.id }}">{{ m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Apply Filters and Reset Buttons -->
|
||||
<div class="d-grid gap-2 mt-4">
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="applyFilters()">
|
||||
Применить
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="resetFilters()">
|
||||
Сбросить
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm" onclick="loadData()">
|
||||
Применить
|
||||
</button>
|
||||
{% if user|has_perm:'errors_report_create' %}
|
||||
<button class="btn btn-success btn-sm" onclick="openCreateModal()">
|
||||
<i class="bi bi-plus-circle"></i> Создать
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
@@ -123,14 +230,21 @@
|
||||
<form id="editForm">
|
||||
<input type="hidden" id="editId">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Дата</label>
|
||||
<input type="date" id="editDate" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Время работы за день (ч)</label>
|
||||
<input type="number" step="0.01" min="0" id="editDailyHours" class="form-control" value="0">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Комплекс</label>
|
||||
<select id="editLocationPlace" class="form-select" required>
|
||||
<option value="kr">КР</option>
|
||||
<option value="dv">ДВ</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@@ -200,6 +314,21 @@ let editModal = null;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
editModal = new bootstrap.Modal(document.getElementById('editModal'));
|
||||
loadData();
|
||||
|
||||
// Добавляем обработчики для обновления счетчика фильтров
|
||||
const form = document.getElementById('filter-form');
|
||||
if (form) {
|
||||
const inputFields = form.querySelectorAll('input[type="date"], select');
|
||||
inputFields.forEach(input => {
|
||||
input.addEventListener('change', updateFilterCounter);
|
||||
});
|
||||
}
|
||||
|
||||
// Обновляем счетчик при открытии offcanvas
|
||||
const offcanvasElement = document.getElementById('offcanvasFilters');
|
||||
if (offcanvasElement) {
|
||||
offcanvasElement.addEventListener('show.bs.offcanvas', updateFilterCounter);
|
||||
}
|
||||
});
|
||||
|
||||
// Загрузка данных
|
||||
@@ -207,10 +336,33 @@ async function loadData() {
|
||||
const dateFrom = document.getElementById('dateFrom').value;
|
||||
const dateTo = document.getElementById('dateTo').value;
|
||||
|
||||
// Получаем выбранные значения из мультиселектов
|
||||
const locationSelect = document.getElementById('locationFilter');
|
||||
const locationPlaces = Array.from(locationSelect.selectedOptions).map(opt => opt.value);
|
||||
|
||||
const errorSelect = document.getElementById('errorFilter');
|
||||
const errorFilters = Array.from(errorSelect.selectedOptions).map(opt => opt.value);
|
||||
|
||||
const malfunctionSelect = document.getElementById('malfunctionFilter');
|
||||
const malfunctionFilters = Array.from(malfunctionSelect.selectedOptions).map(opt => opt.value);
|
||||
|
||||
let url = '{% url "mainapp:errors_report_api" %}?';
|
||||
if (dateFrom) url += `date_from=${dateFrom}&`;
|
||||
if (dateTo) url += `date_to=${dateTo}&`;
|
||||
|
||||
// Добавляем множественные значения
|
||||
locationPlaces.forEach(loc => {
|
||||
url += `location_place=${loc}&`;
|
||||
});
|
||||
|
||||
errorFilters.forEach(err => {
|
||||
url += `error_filter=${err}&`;
|
||||
});
|
||||
|
||||
malfunctionFilters.forEach(mal => {
|
||||
url += `malfunction_filter=${mal}&`;
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const result = await response.json();
|
||||
@@ -219,12 +371,82 @@ async function loadData() {
|
||||
currentColumns = result.columns;
|
||||
|
||||
renderTable();
|
||||
updateFilterCounter();
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки:', error);
|
||||
alert('Ошибка загрузки данных');
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для выбора/снятия всех опций в select
|
||||
function selectAllOptions(selectId, selectAll) {
|
||||
const selectElement = document.getElementById(selectId);
|
||||
if (selectElement) {
|
||||
for (let i = 0; i < selectElement.options.length; i++) {
|
||||
selectElement.options[i].selected = selectAll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Применить фильтры
|
||||
function applyFilters() {
|
||||
loadData();
|
||||
// Закрыть offcanvas
|
||||
const offcanvas = bootstrap.Offcanvas.getInstance(document.getElementById('offcanvasFilters'));
|
||||
if (offcanvas) {
|
||||
offcanvas.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Сбросить фильтры
|
||||
function resetFilters() {
|
||||
document.getElementById('dateFrom').value = '';
|
||||
document.getElementById('dateTo').value = '';
|
||||
|
||||
// Снимаем выбор со всех мультиселектов
|
||||
selectAllOptions('locationFilter', false);
|
||||
selectAllOptions('errorFilter', false);
|
||||
selectAllOptions('malfunctionFilter', false);
|
||||
|
||||
loadData();
|
||||
|
||||
// Закрыть offcanvas
|
||||
const offcanvas = bootstrap.Offcanvas.getInstance(document.getElementById('offcanvasFilters'));
|
||||
if (offcanvas) {
|
||||
offcanvas.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Обновить счетчик фильтров
|
||||
function updateFilterCounter() {
|
||||
let filterCount = 0;
|
||||
|
||||
// Проверяем даты
|
||||
if (document.getElementById('dateFrom').value) filterCount++;
|
||||
if (document.getElementById('dateTo').value) filterCount++;
|
||||
|
||||
// Проверяем мультиселекты
|
||||
const locationSelect = document.getElementById('locationFilter');
|
||||
if (locationSelect.selectedOptions.length > 0) filterCount++;
|
||||
|
||||
const errorSelect = document.getElementById('errorFilter');
|
||||
if (errorSelect.selectedOptions.length > 0) filterCount++;
|
||||
|
||||
const malfunctionSelect = document.getElementById('malfunctionFilter');
|
||||
if (malfunctionSelect.selectedOptions.length > 0) filterCount++;
|
||||
|
||||
// Отображаем счетчик
|
||||
const counterElement = document.getElementById('filterCounter');
|
||||
if (counterElement) {
|
||||
if (filterCount > 0) {
|
||||
counterElement.textContent = filterCount;
|
||||
counterElement.style.display = 'inline';
|
||||
} else {
|
||||
counterElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Получить номер ISO недели для даты
|
||||
function getWeekKey(dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
@@ -295,6 +517,7 @@ function renderTable() {
|
||||
topHtml += `
|
||||
<th rowspan="2" style="width: 80px;">Раб. ч/день</th>
|
||||
<th rowspan="2" style="width: 100px;">Раб. ч/нед.</th>
|
||||
<th rowspan="2" style="width: 80px;">Комплекс</th>
|
||||
<th rowspan="2" style="min-width: 150px;">Пояснение</th>
|
||||
<th rowspan="2" style="min-width: 150px;">Комментарий</th>
|
||||
`;
|
||||
@@ -360,6 +583,7 @@ function renderTable() {
|
||||
bodyHtml += `<td class="text-center align-middle weekly-hours-cell" rowspan="${info.rowspan}">${info.weeklyTotal}</td>`;
|
||||
}
|
||||
|
||||
bodyHtml += `<td class="text-center">${row.location_place_display || ''}</td>`;
|
||||
bodyHtml += `<td>${row.explanation || ''}</td>`;
|
||||
bodyHtml += `<td>${row.comment || ''}</td>`;
|
||||
bodyHtml += `</tr>`;
|
||||
@@ -384,6 +608,7 @@ function openCreateModal() {
|
||||
document.getElementById('editId').value = '';
|
||||
document.getElementById('editDate').value = new Date().toISOString().split('T')[0];
|
||||
document.getElementById('editDailyHours').value = 0;
|
||||
document.getElementById('editLocationPlace').value = 'kr';
|
||||
document.getElementById('editExplanation').value = '';
|
||||
document.getElementById('editComment').value = '';
|
||||
|
||||
@@ -408,6 +633,7 @@ async function openEditModal(id) {
|
||||
document.getElementById('editId').value = row.id;
|
||||
document.getElementById('editDate').value = row.date;
|
||||
document.getElementById('editDailyHours').value = row.daily_work_hours || 0;
|
||||
document.getElementById('editLocationPlace').value = row.location_place || 'kr';
|
||||
document.getElementById('editExplanation').value = row.explanation || '';
|
||||
document.getElementById('editComment').value = row.comment || '';
|
||||
|
||||
@@ -525,6 +751,7 @@ async function saveRecord() {
|
||||
id: document.getElementById('editId').value || null,
|
||||
date: date,
|
||||
daily_work_hours: parseFloat(document.getElementById('editDailyHours').value) || 0,
|
||||
location_place: document.getElementById('editLocationPlace').value,
|
||||
explanation: document.getElementById('editExplanation').value,
|
||||
comment: document.getElementById('editComment').value,
|
||||
downtimes: collectDowntimes(),
|
||||
|
||||
@@ -31,6 +31,9 @@ class ErrorsReportAPIView(View, LoginRequiredMixin, PermissionRequiredMixin):
|
||||
# Получаем параметры фильтрации
|
||||
date_from = request.GET.get('date_from')
|
||||
date_to = request.GET.get('date_to')
|
||||
location_places = request.GET.getlist('location_place')
|
||||
error_filters = request.GET.getlist('error_filter')
|
||||
malfunction_filters = request.GET.getlist('malfunction_filter')
|
||||
|
||||
reports = DailyReport.objects.all()
|
||||
|
||||
@@ -38,8 +41,16 @@ class ErrorsReportAPIView(View, LoginRequiredMixin, PermissionRequiredMixin):
|
||||
reports = reports.filter(date__gte=date_from)
|
||||
if date_to:
|
||||
reports = reports.filter(date__lte=date_to)
|
||||
if location_places:
|
||||
reports = reports.filter(location_place__in=location_places)
|
||||
|
||||
reports = reports.prefetch_related('downtime_periods', 'issue_marks__issue_type')
|
||||
# Фильтрация по ошибкам/неисправностям
|
||||
if error_filters:
|
||||
reports = reports.filter(issue_marks__issue_type_id__in=error_filters, issue_marks__is_present=True)
|
||||
if malfunction_filters:
|
||||
reports = reports.filter(issue_marks__issue_type_id__in=malfunction_filters, issue_marks__is_present=True)
|
||||
|
||||
reports = reports.prefetch_related('downtime_periods', 'issue_marks__issue_type').distinct()
|
||||
|
||||
# Получаем все типы ошибок/неисправностей
|
||||
issue_types = IssueType.objects.all().order_by('category', 'name')
|
||||
@@ -68,6 +79,8 @@ class ErrorsReportAPIView(View, LoginRequiredMixin, PermissionRequiredMixin):
|
||||
'weekly_work_hours': float(report.weekly_work_hours),
|
||||
'explanation': report.explanation or '',
|
||||
'comment': report.comment or '',
|
||||
'location_place': report.location_place or '',
|
||||
'location_place_display': report.get_location_place_display() if report.location_place else '',
|
||||
}
|
||||
|
||||
# Добавляем отметки по каждому типу
|
||||
@@ -129,6 +142,7 @@ class ErrorsReportSaveAPIView(View):
|
||||
report.daily_work_hours = Decimal(str(data.get('daily_work_hours', 0)))
|
||||
report.explanation = data.get('explanation', '')
|
||||
report.comment = data.get('comment', '')
|
||||
report.location_place = data.get('location_place', 'kr')
|
||||
report.save()
|
||||
except DailyReport.DoesNotExist:
|
||||
return JsonResponse({'success': False, 'error': 'Запись не найдена'}, status=404)
|
||||
@@ -139,6 +153,7 @@ class ErrorsReportSaveAPIView(View):
|
||||
daily_work_hours=Decimal(str(data.get('daily_work_hours', 0))),
|
||||
explanation=data.get('explanation', ''),
|
||||
comment=data.get('comment', ''),
|
||||
location_place=data.get('location_place', 'kr'),
|
||||
created_by=request.user if request.user.is_authenticated else None,
|
||||
)
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ class SatelliteListView(LoginRequiredMixin, View):
|
||||
processed_satellites = []
|
||||
for satellite in page_obj:
|
||||
# Get band names
|
||||
band_names = [band.name for band in satellite.band.all()]
|
||||
band_names = [f"{band.name}({int(band.border_start)}-{int(band.border_end)})" for band in satellite.band.all()]
|
||||
|
||||
# Get location_place display value
|
||||
location_place_display = dict(Satellite.PLACES).get(satellite.location_place, "-") if satellite.location_place else "-"
|
||||
|
||||
Reference in New Issue
Block a user