Создали планирование
All checks were successful
Deploy MES Core / deploy (push) Successful in 12s

This commit is contained in:
2026-03-30 01:39:22 +03:00
parent 78d4a1a04f
commit c9ff66a36b
6 changed files with 1053 additions and 5 deletions

View File

@@ -1,11 +1,19 @@
from datetime import datetime
from django.shortcuts import redirect
from django.db.models import Case, ExpressionWrapper, F, IntegerField, Sum, Value, When
from django.db.models.functions import Coalesce
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy
from django.views.generic import TemplateView, ListView, UpdateView
from django.views import View
from django.views.generic import FormView, ListView, TemplateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils import timezone
from .models import Item, Machine
from warehouse.models import Material, MaterialCategory, SteelGrade
from .forms import ProductionTaskCreateForm
from .models import Company, Deal, Item, Machine, ProductionTask
# Класс главной страницы (роутер)
class IndexView(TemplateView):
@@ -208,6 +216,288 @@ class RegistryPrintView(LoginRequiredMixin, TemplateView):
return context
class PlanningView(LoginRequiredMixin, TemplateView):
template_name = 'shiftflow/planning.html'
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')
if role not in ['admin', 'technologist']:
return redirect('registry')
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
profile = getattr(self.request.user, 'profile', None)
role = profile.role if profile else ('admin' if self.request.user.is_superuser else 'operator')
context['user_role'] = role
tasks = ProductionTask.objects.select_related('deal', 'material').annotate(
done_qty=Coalesce(Sum('items__quantity_fact'), 0),
planned_qty=Coalesce(
Sum(
Case(
When(items__status__in=['work', 'leftover'], then=F('items__quantity_plan')),
default=Value(0),
output_field=IntegerField(),
)
),
0,
),
).annotate(
remaining_qty=ExpressionWrapper(
F('quantity_ordered') - F('done_qty') - F('planned_qty'),
output_field=IntegerField(),
)
)
context['tasks'] = tasks
context['machines'] = Machine.objects.all()
return context
class PlanningAddView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return redirect('planning')
task_id = request.POST.get('task_id')
machine_id = request.POST.get('machine_id')
qty_raw = request.POST.get('quantity_plan')
if not (task_id and task_id.isdigit() and machine_id and machine_id.isdigit() and qty_raw and qty_raw.isdigit()):
return redirect('planning')
qty = int(qty_raw)
if qty <= 0:
return redirect('planning')
Item.objects.create(
task_id=int(task_id),
machine_id=int(machine_id),
date=timezone.localdate(),
quantity_plan=qty,
quantity_fact=0,
status='work',
is_synced_1c=False,
)
return redirect('planning')
class ProductionTaskCreateView(LoginRequiredMixin, FormView):
template_name = 'shiftflow/task_create.html'
form_class = ProductionTaskCreateForm
success_url = reverse_lazy('planning')
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')
if role not in ['admin', 'technologist']:
return redirect('registry')
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
profile = getattr(self.request.user, 'profile', None)
role = profile.role if profile else ('admin' if self.request.user.is_superuser else 'operator')
context['user_role'] = role
context['companies'] = Company.objects.all().order_by('name')
context['material_categories'] = MaterialCategory.objects.all().order_by('name')
context['steel_grades'] = SteelGrade.objects.all().order_by('name')
return context
def form_valid(self, form):
task = ProductionTask(
deal=form.cleaned_data['deal'],
drawing_name=form.cleaned_data.get('drawing_name') or 'Б/ч',
size_value=form.cleaned_data['size_value'],
material=form.cleaned_data['material'],
quantity_ordered=form.cleaned_data['quantity_ordered'],
is_bend=form.cleaned_data.get('is_bend') or False,
)
if form.cleaned_data.get('drawing_file'):
task.drawing_file = form.cleaned_data['drawing_file']
if form.cleaned_data.get('extra_drawing'):
task.extra_drawing = form.cleaned_data['extra_drawing']
task.save()
return super().form_valid(form)
class DealDetailView(LoginRequiredMixin, View):
def get(self, request, pk, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
deal = get_object_or_404(Deal, pk=pk)
return JsonResponse({
'id': deal.id,
'number': deal.number,
'company_id': deal.company_id,
'description': deal.description or '',
})
class DealUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
deal_id = request.POST.get('id')
number = (request.POST.get('number') or '').strip()
description = (request.POST.get('description') or '').strip()
company_id = request.POST.get('company_id')
if not number:
return JsonResponse({'error': 'number_required'}, status=400)
if deal_id and str(deal_id).isdigit():
deal = get_object_or_404(Deal, pk=int(deal_id))
deal.number = number
else:
deal, _ = Deal.objects.get_or_create(number=number)
deal.description = description
if company_id and str(company_id).isdigit():
deal.company_id = int(company_id)
else:
deal.company_id = None
deal.save()
return JsonResponse({'id': deal.id, 'label': deal.number})
class MaterialDetailView(LoginRequiredMixin, View):
def get(self, request, pk, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
material = get_object_or_404(Material, pk=pk)
return JsonResponse({
'id': material.id,
'category_id': material.category_id,
'steel_grade_id': material.steel_grade_id,
'name': material.name,
'full_name': material.full_name,
})
class MaterialUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
material_id = request.POST.get('id')
category_id = request.POST.get('category_id')
steel_grade_id = request.POST.get('steel_grade_id')
name = (request.POST.get('name') or '').strip()
if not (category_id and str(category_id).isdigit() and name):
return JsonResponse({'error': 'invalid'}, status=400)
if material_id and str(material_id).isdigit():
material = get_object_or_404(Material, pk=int(material_id))
else:
material = Material()
material.category_id = int(category_id)
material.name = name
if steel_grade_id and str(steel_grade_id).isdigit():
material.steel_grade_id = int(steel_grade_id)
else:
material.steel_grade_id = None
material.save()
return JsonResponse({'id': material.id, 'label': material.full_name})
class CompanyUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
company_id = request.POST.get('id')
name = (request.POST.get('name') or '').strip()
description = (request.POST.get('description') or '').strip()
if not name:
return JsonResponse({'error': 'name_required'}, status=400)
if company_id and str(company_id).isdigit():
company = get_object_or_404(Company, pk=int(company_id))
company.name = name
else:
company, _ = Company.objects.get_or_create(name=name)
company.description = description
company.save()
return JsonResponse({'id': company.id, 'label': company.name})
class MaterialCategoryUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
category_id = request.POST.get('id')
name = (request.POST.get('name') or '').strip()
gost_standard = (request.POST.get('gost_standard') or '').strip()
if not name:
return JsonResponse({'error': 'name_required'}, status=400)
if category_id and str(category_id).isdigit():
category = get_object_or_404(MaterialCategory, pk=int(category_id))
category.name = name
else:
category, _ = MaterialCategory.objects.get_or_create(name=name)
category.gost_standard = gost_standard
category.save()
return JsonResponse({'id': category.id, 'label': category.name})
class SteelGradeUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator')
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403)
grade_id = request.POST.get('id')
name = (request.POST.get('name') or '').strip()
gost_standard = (request.POST.get('gost_standard') or '').strip()
if not name:
return JsonResponse({'error': 'name_required'}, status=400)
if grade_id and str(grade_id).isdigit():
grade = get_object_or_404(SteelGrade, pk=int(grade_id))
grade.name = name
else:
grade, _ = SteelGrade.objects.get_or_create(name=name)
grade.gost_standard = gost_standard
grade.save()
return JsonResponse({'id': grade.id, 'label': grade.name})
# Вьюха детального вида и редактирования
class ItemUpdateView(LoginRequiredMixin, UpdateView):
model = Item