Открыл админу изделия, доработал списание
All checks were successful
Deploy MES Core / deploy (push) Successful in 10s

This commit is contained in:
2026-04-07 12:57:43 +03:00
parent a238c83b04
commit 86215c9fa8
12 changed files with 206 additions and 23 deletions

View File

@@ -755,6 +755,26 @@ class MaintenanceStatusView(LoginRequiredMixin, View):
class MaintenanceView(LoginRequiredMixin, TemplateView):
template_name = 'shiftflow/maintenance.html'
def _server_log_path(self):
p = (django_settings.BASE_DIR / 'logs' / 'mes.log')
return p
def _read_tail(self, path, max_bytes: int = 20000) -> str:
try:
if not path.exists():
return ''
size = path.stat().st_size
start = max(0, size - max_bytes)
with path.open('rb') as f:
f.seek(start)
data = f.read()
try:
return data.decode('utf-8', errors='replace')
except Exception:
return str(data)
except Exception:
return ''
def dispatch(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
@@ -767,6 +787,10 @@ class MaintenanceView(LoginRequiredMixin, TemplateView):
context = super().get_context_data(**kwargs)
context['user_role'] = 'admin'
log_path = self._server_log_path()
context['log_path'] = str(log_path)
context['log_tail'] = self._read_tail(log_path)
# Подтягиваем текущие настройки генерации превью, чтобы отрисовать форму.
s = _get_dxf_preview_settings()
context['dxf_settings'] = s
@@ -817,9 +841,18 @@ class MaintenanceView(LoginRequiredMixin, TemplateView):
messages.success(request, 'Остановка запрошена.')
return redirect('maintenance')
if action == 'refresh_log':
return redirect('maintenance')
if action == 'clear_log':
# Очистка лог-файла последней задачи. Во время выполнения не трогаем,
# потому что процесс может держать открытый дескриптор файла.
try:
self._server_log_path().open('wb').close()
messages.success(request, 'Лог очищен.')
except Exception:
messages.error(request, 'Не удалось очистить лог.')
return redirect('maintenance')
if action == 'clear_dxf_job_log':
job = DxfPreviewJob.objects.order_by('-id').first()
if not job:
messages.info(request, 'Логов нет.')
@@ -831,9 +864,9 @@ class MaintenanceView(LoginRequiredMixin, TemplateView):
try:
_dxf_job_log_path(job.id).open('wb').close()
messages.success(request, 'Лог очищен.')
messages.success(request, 'Лог DXF-генерации очищен.')
except Exception:
messages.error(request, 'Не удалось очистить лог.')
messages.error(request, 'Не удалось очистить лог DXF-генерации.')
return redirect('maintenance')
if action != 'update_previews':
@@ -1177,6 +1210,7 @@ class MaterialCategoryUpsertView(LoginRequiredMixin, View):
category_id = request.POST.get('id')
name = (request.POST.get('name') or '').strip()
gost_standard = (request.POST.get('gost_standard') or '').strip()
form_factor = (request.POST.get('form_factor') or '').strip() or 'other'
if not name:
return JsonResponse({'error': 'name_required'}, status=400)
@@ -1188,6 +1222,8 @@ class MaterialCategoryUpsertView(LoginRequiredMixin, View):
category, _ = MaterialCategory.objects.get_or_create(name=name)
category.gost_standard = gost_standard
if form_factor in ['sheet', 'bar', 'other']:
category.form_factor = form_factor
category.save()
return JsonResponse({'id': category.id, 'label': category.name})
@@ -1809,6 +1845,25 @@ class ClosingView(LoginRequiredMixin, TemplateView):
messages.error(request, 'Выбери хотя бы один пункт сменки и режим закрытия (полностью/частично).')
return redirect(f"{reverse_lazy('closing')}?machine_id={machine_id}&material_id={material_id}")
if not consumptions:
messages.error(request, 'Заполни списание: укажи, какие единицы на складе использованы и в каком количестве.')
return redirect(f"{reverse_lazy('closing')}?machine_id={machine_id}&material_id={material_id}")
try:
apply_closing(
user_id=request.user.id,
machine_id=int(machine_id),
material_id=int(material_id),
item_actions=item_actions,
consumptions=consumptions,
remnants=remnants,
)
messages.success(request, 'Закрытие выполнено.')
except Exception as e:
messages.error(request, f'Ошибка закрытия: {e}')
return redirect(f"{reverse_lazy('closing')}?machine_id={machine_id}&material_id={material_id}")
class ProductsView(LoginRequiredMixin, TemplateView):
template_name = 'shiftflow/products.html'