diff --git a/backend/procurement/urls_frontend.py b/backend/procurement/urls_frontend.py index 9f321963..f78f6049 100644 --- a/backend/procurement/urls_frontend.py +++ b/backend/procurement/urls_frontend.py @@ -22,6 +22,7 @@ urlpatterns = [ ), path('orders/create/', views.request_create, name='request_create'), path('orders//', views.request_detail, name='request_detail'), + path('orders//edit/', views.request_edit, name='request_edit'), # Оборудование path('equipment/', views.equipment_list, name='equipment_list'), diff --git a/backend/procurement/views_frontend.py b/backend/procurement/views_frontend.py index f3d00bd6..5dbb2c1b 100644 --- a/backend/procurement/views_frontend.py +++ b/backend/procurement/views_frontend.py @@ -143,6 +143,22 @@ def request_create(request): }) +@login_required +def request_edit(request, pk): + """Страница редактирования заявки. + + Данные загружаются через API: + - /api/v1/procurement/requests/{pk}/ — данные заявки + - /api/v1/departments/ — список отделов + Обновление через: + - PATCH /api/v1/procurement/requests/{pk}/ + """ + return render(request, 'procurement/request_form.html', { + 'page_title': 'Редактирование заявки', + 'request_id': pk, + }) + + @login_required def equipment_list(request): """Список оборудования. diff --git a/backend/templates/procurement/request_detail.html b/backend/templates/procurement/request_detail.html index de3f814f..da837f4f 100644 --- a/backend/templates/procurement/request_detail.html +++ b/backend/templates/procurement/request_detail.html @@ -197,7 +197,7 @@ - + Редактировать {% endif %} @@ -274,6 +274,96 @@ +{# ===== МОДАЛЬНЫЕ ОКНА ===== #} + +{# Модальное окно добавления позиции #} + + +{# Модальное окно одобрения #} + + +{# Модальное окно отклонения #} + + {% endblock %} {% block extra_js %} @@ -354,5 +444,130 @@ alert('Ошибка сети'); } }); + + // Добавление позиции + document.getElementById('saveItemBtn')?.addEventListener('click', async () => { + const form = document.getElementById('addItemForm'); + if (!form.checkValidity()) { + form.reportValidity(); + return; + } + + const formData = new FormData(form); + const data = { + name: formData.get('name'), + description: formData.get('description'), + quantity: parseFloat(formData.get('quantity')), + unit: formData.get('unit'), + estimated_unit_price: parseFloat(formData.get('estimated_unit_price')), + request: requestId + }; + + try { + const resp = await fetch('/api/v1/procurement/items/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '{{ csrf_token }}' + }, + body: JSON.stringify(data) + }); + + if (resp.ok) { + bootstrap.Modal.getInstance(document.getElementById('addItemModal')).hide(); + form.reset(); + window.location.reload(); + } else { + const err = await resp.json(); + alert(err.error || 'Ошибка добавления позиции'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } + }); + + // Удаление позиции + async function deleteItem(itemId) { + if (!confirm('Удалить эту позицию?')) return; + + try { + const resp = await fetch(`/api/v1/procurement/items/${itemId}/`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '{{ csrf_token }}' + } + }); + + if (resp.ok) { + window.location.reload(); + } else { + const err = await resp.json(); + alert(err.error || 'Ошибка удаления'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } + } + + // Одобрение заявки + document.getElementById('confirmApproveBtn')?.addEventListener('click', async () => { + const comment = document.getElementById('approveComment').value; + + try { + const resp = await fetch(`/api/v1/procurement/requests/${requestId}/approve/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '{{ csrf_token }}' + }, + body: JSON.stringify({ comment }) + }); + + if (resp.ok) { + bootstrap.Modal.getInstance(document.getElementById('approveModal')).hide(); + window.location.reload(); + } else { + const err = await resp.json(); + alert(err.error || 'Ошибка одобрения'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } + }); + + // Отклонение заявки + document.getElementById('confirmRejectBtn')?.addEventListener('click', async () => { + const comment = document.getElementById('rejectComment').value; + + if (!comment.trim()) { + alert('Укажите причину отклонения'); + return; + } + + try { + const resp = await fetch(`/api/v1/procurement/requests/${requestId}/reject/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '{{ csrf_token }}' + }, + body: JSON.stringify({ comment }) + }); + + if (resp.ok) { + bootstrap.Modal.getInstance(document.getElementById('rejectModal')).hide(); + window.location.reload(); + } else { + const err = await resp.json(); + alert(err.error || 'Ошибка отклонения'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } + }); {% endblock %} diff --git a/backend/templates/procurement/request_form.html b/backend/templates/procurement/request_form.html index 5ecc7527..e2e31933 100644 --- a/backend/templates/procurement/request_form.html +++ b/backend/templates/procurement/request_form.html @@ -29,7 +29,7 @@ -
+ {% csrf_token %}
@@ -48,6 +48,13 @@ maxlength="2000">
+
+ + +
+
- -
- - -
- -
- -
- - ₽ -
-
Можно указать позже
-
@@ -101,3 +93,187 @@ {% endblock %} + +{% block extra_js %} + +{% endblock %}