+
-
+
+
+
+
+
@@ -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 += `
Раб. ч/день |
Раб. ч/нед. |
+ Комплекс |
Пояснение |
Комментарий |
`;
@@ -360,6 +583,7 @@ function renderTable() {
bodyHtml += `${info.weeklyTotal} | `;
}
+ bodyHtml += `${row.location_place_display || ''} | `;
bodyHtml += `${row.explanation || ''} | `;
bodyHtml += `${row.comment || ''} | `;
bodyHtml += ``;
@@ -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(),
diff --git a/dbapp/mainapp/views/errors_report.py b/dbapp/mainapp/views/errors_report.py
index bb4bc28..cd67d83 100644
--- a/dbapp/mainapp/views/errors_report.py
+++ b/dbapp/mainapp/views/errors_report.py
@@ -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,
)
diff --git a/dbapp/mainapp/views/satellite.py b/dbapp/mainapp/views/satellite.py
index 72d1815..2814569 100644
--- a/dbapp/mainapp/views/satellite.py
+++ b/dbapp/mainapp/views/satellite.py
@@ -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 "-"