Переезд на схему нового доступа
All checks were successful
Deploy MES Core / deploy (push) Successful in 11s

This commit is contained in:
2026-04-13 08:26:07 +03:00
parent ecc0193d0a
commit 69edd3fa97
5 changed files with 33 additions and 9 deletions

View File

@@ -90,7 +90,8 @@ TEMPLATES = [
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'shiftflow.context_processors.env_info', # Правильный путь 'shiftflow.context_processors.env_info',
'shiftflow.context_processors.authz_info',
], ],
}, },
}, },

View File

@@ -1,9 +1,19 @@
from django.conf import settings from django.conf import settings
from shiftflow.authz import get_user_roles, primary_role
def env_info(request): def env_info(request):
"""
Прокидываем ENV_TYPE во все шаблоны.
"""
return { return {
'ENV_TYPE': getattr(settings, 'ENV_TYPE', 'local') 'ENV_TYPE': getattr(settings, 'ENV_TYPE', 'local')
}
def authz_info(request):
roles = get_user_roles(getattr(request, 'user', None))
profile = getattr(getattr(request, 'user', None), 'profile', None)
return {
'user_roles': sorted(roles),
'user_role': primary_role(roles),
'is_readonly': bool(getattr(profile, 'is_readonly', False)) if profile else False,
} }

View File

@@ -4,7 +4,7 @@
<div class="card shadow border-secondary"> <div class="card shadow border-secondary">
<div class="card-header border-secondary py-3 d-flex justify-content-between align-items-center"> <div class="card-header border-secondary py-3 d-flex justify-content-between align-items-center">
<h3 class="text-accent mb-0"><i class="bi bi-building me-2"></i>Заказчики</h3> <h3 class="text-accent mb-0"><i class="bi bi-building me-2"></i>Заказчики</h3>
{% if user_role in 'admin,technologist' %} {% if user_role in 'admin,technologist,clerk,manager' %}
<button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#companyModal"> <button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#companyModal">
<i class="bi bi-plus-lg me-1"></i>Добавить заказчика <i class="bi bi-plus-lg me-1"></i>Добавить заказчика
</button> </button>

View File

@@ -229,7 +229,7 @@
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label">Склад</label> <label class="form-label">Склад</label>
<select class="form-select" name="location_id" required> <select class="form-select" name="location_id" required>
{% for loc in transfer_locations %} {% for loc in receipt_locations %}
<option value="{{ loc.id }}">{{ loc }}</option> <option value="{{ loc.id }}">{{ loc }}</option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -2938,9 +2938,8 @@ class MaterialUpsertView(LoginRequiredMixin, View):
class CompanyUpsertView(LoginRequiredMixin, View): class CompanyUpsertView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None) roles = get_user_roles(request.user)
role = profile.role if profile else ('admin' if request.user.is_superuser else 'operator') if not has_any_role(roles, ['admin', 'clerk', 'manager', 'technologist']):
if role not in ['admin', 'technologist']:
return JsonResponse({'error': 'forbidden'}, status=403) return JsonResponse({'error': 'forbidden'}, status=403)
company_id = request.POST.get('id') company_id = request.POST.get('id')
@@ -3991,6 +3990,7 @@ class WarehouseStocksView(LoginRequiredMixin, TemplateView):
allowed_transfer_locations = list(Location.objects.filter(id__in=allowed_loc_ids).order_by('name')) 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['transfer_locations'] = allowed_transfer_locations if allowed_transfer_locations is not None else locations
ctx['receipt_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['materials'] = Material.objects.select_related('category').all().order_by('full_name')
ctx['entities'] = ProductEntity.objects.all().order_by('drawing_number', 'name') ctx['entities'] = ProductEntity.objects.all().order_by('drawing_number', 'name')
@@ -4087,6 +4087,19 @@ class WarehouseReceiptCreateView(LoginRequiredMixin, View):
messages.error(request, 'Выбери склад.') messages.error(request, 'Выбери склад.')
return redirect(next_url) return redirect(next_url)
profile = getattr(request.user, 'profile', None)
role = primary_role(roles)
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(location_id) not in {int(x) for x in allowed_loc_ids}:
messages.error(request, 'Мастер может делать приход только на склад своего цеха.')
return redirect(next_url)
try: try:
qty = float(quantity_raw) qty = float(quantity_raw)
except ValueError: except ValueError: