Добавил редактирование материалла на странице склада
All checks were successful
Deploy MES Core / deploy (push) Successful in 12s

This commit is contained in:
2026-04-23 08:49:26 +03:00
parent 963dc7105a
commit 6fd01c9a6e
5 changed files with 336 additions and 4 deletions

View File

@@ -4442,6 +4442,184 @@ class WarehouseStocksView(LoginRequiredMixin, TemplateView):
return ctx
class WarehouseStockItemUpdateView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)
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)
next_url = (request.POST.get('next') or '').strip()
if not next_url.startswith('/'):
next_url = reverse_lazy('warehouse_stocks')
stock_item_id = (request.POST.get('stock_item_id') or '').strip()
if not stock_item_id.isdigit():
messages.error(request, 'Не выбрана складская позиция.')
return redirect(next_url)
qty_raw = (request.POST.get('quantity') or '').strip().replace(',', '.')
len_raw = (request.POST.get('current_length') or '').strip().replace(',', '.')
wid_raw = (request.POST.get('current_width') or '').strip().replace(',', '.')
deal_id_raw = (request.POST.get('deal_id') or '').strip()
unique_id_raw = (request.POST.get('unique_id') or '').strip()
material_id_raw = (request.POST.get('material_id') or '').strip()
location_id_raw = (request.POST.get('location_id') or '').strip()
try:
qty = float(qty_raw)
except ValueError:
qty = 0.0
if qty <= 0:
messages.error(request, 'Количество должно быть больше 0.')
return redirect(next_url)
def parse_float_opt(s: str):
s = (s or '').strip().replace(',', '.')
if not s:
return None
try:
return float(s)
except ValueError:
return None
cur_len = parse_float_opt(len_raw)
cur_wid = parse_float_opt(wid_raw)
is_remnant = bool(request.POST.get('is_remnant'))
is_customer_supplied = bool(request.POST.get('is_customer_supplied'))
deal_id = None
if deal_id_raw.isdigit():
deal_id = int(deal_id_raw)
material_id = None
if material_id_raw.isdigit():
material_id = int(material_id_raw)
location_id = None
if location_id_raw.isdigit():
location_id = int(location_id_raw)
unique_id = unique_id_raw or None
with transaction.atomic():
si = (
StockItem.objects.select_for_update(of=('self',))
.select_related('material', 'material__category', 'location', 'deal')
.filter(id=int(stock_item_id), is_archived=False)
.first()
)
if not si:
messages.error(request, 'Складская позиция не найдена.')
return redirect(next_url)
if not getattr(si, 'material_id', None) or getattr(si, 'entity_id', None):
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 allowed_loc_ids and int(si.location_id) not in {int(x) for x in allowed_loc_ids}:
messages.error(request, 'Мастер может редактировать только позиции своего цеха.')
return redirect(next_url)
ship_loc = (
Location.objects.filter(
Q(name__icontains='отгруж')
| Q(name__icontains='Отгруж')
| Q(name__icontains='отгруз')
| Q(name__icontains='Отгруз')
)
.order_by('id')
.first()
)
ship_loc_id = ship_loc.id if ship_loc else None
if deal_id is not None:
ok = Deal.objects.filter(id=int(deal_id)).exists()
if not ok:
messages.error(request, 'Выбрана несуществующая сделка.')
return redirect(next_url)
if material_id is None:
messages.error(request, 'Выбери материал.')
return redirect(next_url)
mat = Material.objects.select_related('category').filter(id=int(material_id)).first()
if not mat:
messages.error(request, 'Материал не найден.')
return redirect(next_url)
if location_id is None:
messages.error(request, 'Выбери склад.')
return redirect(next_url)
loc = Location.objects.filter(id=int(location_id)).first()
if not loc:
messages.error(request, 'Склад не найден.')
return redirect(next_url)
if ship_loc_id and int(location_id) == int(ship_loc_id):
messages.error(request, 'Перенос на склад отгрузки выполняется через отгрузку/перемещение.')
return redirect(next_url)
ff = (getattr(getattr(mat, 'category', None), 'form_factor', '') or '').strip().lower()
if ff == 'sheet':
if cur_len is None or cur_wid is None:
messages.error(request, 'Для листового материала нужно заполнить длину и ширину (мм).')
return redirect(next_url)
elif ff == 'bar':
if cur_len is None:
messages.error(request, 'Для проката нужно заполнить длину (мм).')
return redirect(next_url)
cur_wid = None
if unique_id:
exists = StockItem.objects.filter(unique_id=unique_id).exclude(id=int(si.id)).exists()
if exists:
messages.error(request, 'Маркировка (ID) уже используется в другой позиции.')
return redirect(next_url)
si.quantity = float(qty)
si.current_length = cur_len
si.current_width = cur_wid
si.deal_id = deal_id
si.unique_id = unique_id
si.is_remnant = bool(is_remnant)
si.is_customer_supplied = bool(is_customer_supplied)
si.material_id = int(material_id)
si.location_id = int(location_id)
si.save(
update_fields=[
'quantity',
'current_length',
'current_width',
'deal',
'unique_id',
'is_remnant',
'is_customer_supplied',
'material',
'location',
]
)
messages.success(request, 'Позиция обновлена.')
return redirect(next_url)
class WarehouseTransferCreateView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
profile = getattr(request.user, 'profile', None)