From fa881877d73e7ccd58802f50be6cb32ddeac6270 Mon Sep 17 00:00:00 2001 From: ackFromRedmi Date: Mon, 13 Apr 2026 08:10:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=B0=D0=BB=20=D0=BC=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D1=83=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=20?= =?UTF-8?q?=D0=BA=20=D0=BF=D1=83=D0=BD=D0=BA=D1=82=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txtсЫВпывмЫВ | Bin 0 -> 314 bytes .../templates/shiftflow/warehouse_stocks.html | 2 +- shiftflow/views.py | 135 +++++++++++------- 3 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 requirements.txtсЫВпывмЫВ diff --git a/requirements.txtсЫВпывмЫВ b/requirements.txtсЫВпывмЫВ new file mode 100644 index 0000000000000000000000000000000000000000..7301fc74bf499319f5f41b4f3f4e8c8b1094e5f6 GIT binary patch literal 314 zcmZ{f!485z3`FN_;-_F>2}Tdz{1Fs15XBX^;K!@4<)8;m(>?4=U#G8UsX=Eo>Q(Ec z9hGaPjIwvBh4yMys>5}2eT|aR!PV+ZO&~P7XoS_fH>mjIY}uQ@9jKa#&*tCct0J!1 zXWKJvJclKWFeV+z7fBi59>~!_@A)@!z`OOS`KCd4az=O9e)AaB(iVQ#sD~=_k)8=D I6w`Cp-@qm>C;$Ke literal 0 HcmV?d00001 diff --git a/shiftflow/templates/shiftflow/warehouse_stocks.html b/shiftflow/templates/shiftflow/warehouse_stocks.html index e30801e..c72b75b 100644 --- a/shiftflow/templates/shiftflow/warehouse_stocks.html +++ b/shiftflow/templates/shiftflow/warehouse_stocks.html @@ -224,7 +224,7 @@
diff --git a/shiftflow/views.py b/shiftflow/views.py index 23dd54f..0850c2b 100644 --- a/shiftflow/views.py +++ b/shiftflow/views.py @@ -966,9 +966,8 @@ class PlanningStagesView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/planning_stages.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', 'master', 'clerk', 'manager', 'observer']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'manager', 'observer', 'prod_head', 'director']): return redirect('registry') return super().dispatch(request, *args, **kwargs) @@ -1888,17 +1887,18 @@ 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', 'master', 'clerk', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director', 'observer']): 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) context['user_role'] = role + context['user_roles'] = sorted(roles) allowed_ws = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] context['allowed_workshop_ids'] = allowed_ws @@ -1923,17 +1923,18 @@ class DealPlanningView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/planning_deal.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', 'master', 'clerk']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director', 'observer']): 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) context['user_role'] = role + context['user_roles'] = sorted(roles) allowed_ws = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] context['allowed_workshop_ids'] = allowed_ws @@ -2525,17 +2526,17 @@ class CustomersView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/customers.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', 'master', 'clerk']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director', 'observer']): 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) context['user_role'] = role + context['user_roles'] = sorted(roles) companies = Company.objects.all().order_by('name') context['companies'] = companies @@ -2546,17 +2547,18 @@ class CustomerDealsView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/customer_deals.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', 'master', 'clerk']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director', 'observer']): 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) context['user_role'] = role + context['user_roles'] = sorted(roles) allowed_ws = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] context['allowed_workshop_ids'] = allowed_ws @@ -3296,17 +3298,17 @@ class DirectoriesView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/directories.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', 'master', 'clerk', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'observer', 'prod_head', 'director']): return redirect('registry') return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): ctx = 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) ctx['user_role'] = role + ctx['user_roles'] = sorted(roles) return ctx @@ -3314,26 +3316,25 @@ class LocationsCatalogView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/locations_catalog.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', 'master', 'clerk', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'observer', 'prod_head', 'director']): return redirect('registry') return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): ctx = 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) ctx['user_role'] = role - ctx['can_edit'] = role in ['admin', 'prod_head', 'director'] + ctx['user_roles'] = sorted(roles) + ctx['can_edit'] = has_any_role(roles, ['admin', 'prod_head', 'director']) ctx['locations'] = list(Location.objects.order_by('name')) return ctx 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', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'prod_head', 'director']): return redirect('locations_catalog') action = (request.POST.get('action') or '').strip() @@ -3377,17 +3378,17 @@ class WorkshopsCatalogView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/workshops_catalog.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', 'master', 'clerk', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'observer', 'prod_head', 'director']): return redirect('registry') return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): ctx = 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) ctx['user_role'] = role + ctx['user_roles'] = sorted(roles) workshops = list(Workshop.objects.select_related('location').order_by('id')) ws_ids = [int(w.id) for w in workshops] @@ -3414,12 +3415,12 @@ class MachinesCatalogView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/machines_catalog.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', 'master', 'clerk', 'prod_head', 'director']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'observer', 'prod_head', 'director']): return redirect('registry') - self.role = role - self.can_edit = role in ['admin', 'prod_head', 'director'] + self.roles = roles + self.role = primary_role(roles) + self.can_edit = has_any_role(roles, ['admin', 'prod_head', 'director']) return super().dispatch(request, *args, **kwargs) def _workshop_id(self): @@ -3889,17 +3890,18 @@ class WarehouseStocksView(LoginRequiredMixin, TemplateView): template_name = 'shiftflow/warehouse_stocks.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', 'master', 'clerk', 'observer']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'observer', 'prod_head', 'director']): return redirect('registry') return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): ctx = 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') + roles = get_user_roles(self.request.user) + role = primary_role(roles) ctx['user_role'] = role + ctx['user_roles'] = sorted(roles) ship_loc = ( Location.objects.filter( @@ -3975,8 +3977,20 @@ class WarehouseStocksView(LoginRequiredMixin, TemplateView): ctx['selected_kind'] = kind ctx['q'] = q - ctx['can_transfer'] = role in ['admin', 'technologist', 'master', 'clerk'] - ctx['can_receive'] = role in ['admin', 'technologist', 'master', 'clerk'] + ctx['can_transfer'] = has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director']) + ctx['can_receive'] = has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director']) + + allowed_transfer_locations = None + if role == 'master' and not has_any_role(roles, ['admin', 'technologist', 'clerk', 'prod_head', 'director']): + allowed_ws_ids = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] + if not allowed_ws_ids and profile: + user_machine_ids = list(profile.machines.values_list('id', flat=True)) + allowed_ws_ids = list(Machine.objects.filter(id__in=user_machine_ids).exclude(workshop_id__isnull=True).values_list('workshop_id', flat=True)) + allowed_loc_ids = list(Workshop.objects.filter(id__in=allowed_ws_ids).exclude(location_id__isnull=True).values_list('location_id', flat=True)) + if allowed_loc_ids: + allowed_transfer_locations = list(Location.objects.filter(id__in=allowed_loc_ids).order_by('name')) + + ctx['transfer_locations'] = allowed_transfer_locations if allowed_transfer_locations is not None else locations ctx['materials'] = Material.objects.select_related('category').all().order_by('full_name') ctx['entities'] = ProductEntity.objects.all().order_by('drawing_number', 'name') @@ -3991,8 +4005,9 @@ class WarehouseStocksView(LoginRequiredMixin, TemplateView): class WarehouseTransferCreateView(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', 'master', 'clerk']: + roles = get_user_roles(request.user) + role = primary_role(roles) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director']): return JsonResponse({'error': 'forbidden'}, status=403) stock_item_id = (request.POST.get('stock_item_id') or '').strip() @@ -4021,6 +4036,17 @@ class WarehouseTransferCreateView(LoginRequiredMixin, View): messages.error(request, 'Склад назначения должен отличаться от склада-источника.') return redirect(next_url) + if role == 'master' and not has_any_role(roles, ['admin', 'technologist', 'clerk', 'prod_head', 'director']): + allowed_ws_ids = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] + if not allowed_ws_ids and profile: + user_machine_ids = list(profile.machines.values_list('id', flat=True)) + allowed_ws_ids = list(Machine.objects.filter(id__in=user_machine_ids).exclude(workshop_id__isnull=True).values_list('workshop_id', flat=True)) + + allowed_loc_ids = list(Workshop.objects.filter(id__in=allowed_ws_ids).exclude(location_id__isnull=True).values_list('location_id', flat=True)) + if not allowed_loc_ids or int(to_location_id) not in {int(x) for x in allowed_loc_ids}: + messages.error(request, 'Мастер может перемещать только на склад своего цеха.') + return redirect(next_url) + tr = TransferRecord.objects.create( from_location_id=si.location_id, to_location_id=int(to_location_id), @@ -4044,9 +4070,8 @@ class WarehouseTransferCreateView(LoginRequiredMixin, View): class WarehouseReceiptCreateView(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', 'master', 'clerk']: + roles = get_user_roles(request.user) + if not has_any_role(roles, ['admin', 'technologist', 'master', 'clerk', 'prod_head', 'director']): return JsonResponse({'error': 'forbidden'}, status=403) next_url = (request.POST.get('next') or '').strip()