ДОбавил изделия и заполнение спецификции изделия
All checks were successful
Deploy MES Core / deploy (push) Successful in 3m27s
All checks were successful
Deploy MES Core / deploy (push) Successful in 3m27s
This commit is contained in:
283
shiftflow/templates/shiftflow/product_detail.html
Normal file
283
shiftflow/templates/shiftflow/product_detail.html
Normal file
@@ -0,0 +1,283 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card shadow border-secondary mb-3">
|
||||
<div class="card-header border-secondary py-3 d-flex flex-wrap gap-2 justify-content-between align-items-center">
|
||||
<div>
|
||||
<h3 class="text-accent mb-0"><i class="bi bi-diagram-3 me-2"></i>{{ entity }}</h3>
|
||||
<div class="small text-muted mt-1">{{ entity.get_entity_type_display }}</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#addComponentModal">
|
||||
<i class="bi bi-plus-lg me-1"></i>Добавить
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if parent_id %}
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'product_detail' parent_id %}">Назад</a>
|
||||
{% else %}
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'products' %}">Назад</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Тип</th>
|
||||
<th>Обозначение</th>
|
||||
<th>Наименование</th>
|
||||
<th>Заполнен</th>
|
||||
<th>Кол-во</th>
|
||||
<th data-sort="false"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ln in lines %}
|
||||
<tr class="product-row" role="button" data-info-url="{% url 'product_info' ln.child.id %}">
|
||||
<td class="small text-muted">{{ ln.child.get_entity_type_display }}</td>
|
||||
<td class="fw-bold">{{ ln.child.drawing_number|default:"—" }}</td>
|
||||
<td>{{ ln.child.name }}</td>
|
||||
<td>
|
||||
{% if ln.child.passport_filled %}
|
||||
<i class="bi bi-check-circle-fill text-success" title="Заполнено"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-circle text-muted" title="Не заполнено"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="max-width:220px;">
|
||||
<form method="post" class="d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
{% if parent_id %}<input type="hidden" name="parent" value="{{ parent_id }}">{% endif %}
|
||||
<input type="hidden" name="action" value="update_qty">
|
||||
<input type="hidden" name="bom_id" value="{{ ln.id }}">
|
||||
<input class="form-control form-control-sm bg-body text-body border-secondary" name="quantity" value="{{ ln.quantity }}" {% if not can_edit %}disabled{% endif %}>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="submit" {% if not can_edit %}disabled{% endif %}>OK</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
<a class="btn btn-outline-accent btn-sm" href="{% url 'product_detail' ln.child.id %}?parent={{ entity.id }}" onclick="event.stopPropagation();">Состав</a>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if parent_id %}<input type="hidden" name="parent" value="{{ parent_id }}">{% endif %}
|
||||
<input type="hidden" name="action" value="delete_line">
|
||||
<input type="hidden" name="bom_id" value="{{ ln.id }}">
|
||||
<button class="btn btn-outline-secondary btn-sm" type="submit" {% if not can_edit %}disabled{% endif %}>Удалить</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="5" class="text-center text-muted py-4">Пока нет компонентов</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="addComponentModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content border-secondary">
|
||||
<div class="modal-header border-secondary">
|
||||
<h5 class="modal-title">Добавить компонент</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="border border-secondary rounded p-3 mb-3">
|
||||
<div class="fw-bold mb-2">Найти существующее (обозначение / наименование)</div>
|
||||
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<input type="hidden" name="open" value="1">
|
||||
{% if parent_id %}<input type="hidden" name="parent" value="{{ parent_id }}">{% endif %}
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="q_dn" value="{{ q_dn }}" placeholder="Напр. УРН.01">
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="q_name" value="{{ q_name }}" placeholder="Напр. Кубик декоративный">
|
||||
</div>
|
||||
<div class="col-md-3 d-flex gap-2">
|
||||
<button class="btn btn-outline-secondary w-100" type="submit"><i class="bi bi-search me-1"></i>Поиск</button>
|
||||
<a class="btn btn-outline-secondary" href="{% url 'product_detail' entity.id %}?open=1{% if parent_id %}&parent={{ parent_id }}{% endif %}"><i class="bi bi-x-lg"></i></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-3">
|
||||
{% if searched %}
|
||||
{% if found %}
|
||||
<div class="d-flex flex-wrap gap-2 justify-content-between align-items-center">
|
||||
<div class="small text-muted">Найдено:</div>
|
||||
<div class="fw-bold">{{ found.get_entity_type_display }} | {{ found.drawing_number }} {{ found.name }}</div>
|
||||
</div>
|
||||
|
||||
<form method="post" class="row g-2 align-items-end mt-2">
|
||||
{% csrf_token %}
|
||||
{% if parent_id %}<input type="hidden" name="parent" value="{{ parent_id }}">{% endif %}
|
||||
<input type="hidden" name="action" value="add_existing">
|
||||
<input type="hidden" name="child_id" value="{{ found.id }}">
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Кол-во</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="quantity" value="1" required {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="col-md-9 d-flex justify-content-end">
|
||||
<button class="btn btn-outline-accent" type="submit" {% if not can_edit %}disabled{% endif %}>Добавить найденное</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="small text-muted">Не найдено. Можно создать новое ниже.</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="small text-muted">Введи обозначение и/или наименование и нажми «Поиск».</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border border-secondary rounded p-3">
|
||||
<div class="fw-bold mb-2">Создать новое и добавить</div>
|
||||
|
||||
<form method="post" id="createAndAddForm">
|
||||
{% csrf_token %}
|
||||
{% if parent_id %}<input type="hidden" name="parent" value="{{ parent_id }}">{% endif %}
|
||||
<input type="hidden" name="action" value="create_and_add">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="child_type" id="childType" required {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="assembly">Сборочная единица</option>
|
||||
<option value="part">Деталь</option>
|
||||
<option value="purchased">Покупное</option>
|
||||
<option value="casting">Литьё</option>
|
||||
<option value="outsourced">Аутсорс</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Кол-во</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="quantity" value="1" required {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{% if found %}{{ found.drawing_number }}{% else %}{{ q_dn }}{% endif %}" placeholder="Опционально" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{% if found %}{{ found.name }}{% else %}{{ q_name }}{% endif %}" required {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-12" id="plannedMaterialBlock">
|
||||
<label class="form-label">Материал (для детали)</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="planned_material_id" id="plannedMaterialSelect" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for m in materials %}
|
||||
<option value="{{ m.id }}">{{ m.full_name|default:m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
<button class="btn btn-outline-accent" type="submit" {% if not can_edit %}disabled{% endif %}>
|
||||
Создать и добавить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const typeSel = document.getElementById('childType');
|
||||
const block = document.getElementById('plannedMaterialBlock');
|
||||
const matSel = document.getElementById('plannedMaterialSelect');
|
||||
|
||||
function sync() {
|
||||
const t = (typeSel && typeSel.value) || '';
|
||||
const isPart = t === 'part';
|
||||
if (block) block.style.display = isPart ? '' : 'none';
|
||||
if (matSel) {
|
||||
matSel.disabled = !isPart;
|
||||
if (!isPart) matSel.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (typeSel) {
|
||||
typeSel.addEventListener('change', sync);
|
||||
sync();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer border-secondary">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="productInfoModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content border-secondary">
|
||||
<div class="modal-header border-secondary">
|
||||
<h5 class="modal-title">Информация о компоненте</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="productInfoBody">
|
||||
<div class="text-muted">Загрузка...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if request.GET.open == '1' %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const el = document.getElementById('addComponentModal');
|
||||
if (!el) return;
|
||||
const modal = new bootstrap.Modal(el);
|
||||
modal.show();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modalEl = document.getElementById('productInfoModal');
|
||||
const bodyEl = document.getElementById('productInfoBody');
|
||||
if (!modalEl || !bodyEl) return;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
|
||||
async function openInfo(url) {
|
||||
bodyEl.innerHTML = '<div class="text-muted">Загрузка...</div>';
|
||||
modal.show();
|
||||
try {
|
||||
const nextUrl = encodeURIComponent(window.location.pathname + window.location.search);
|
||||
const sep = url.includes('?') ? '&' : '?';
|
||||
const res = await fetch(url + sep + 'next=' + nextUrl, { credentials: 'same-origin' });
|
||||
bodyEl.innerHTML = await res.text();
|
||||
} catch (e) {
|
||||
bodyEl.innerHTML = '<div class="alert alert-danger">Не удалось загрузить информацию.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('tr.product-row[data-info-url]').forEach(tr => {
|
||||
tr.addEventListener('click', () => {
|
||||
const url = tr.getAttribute('data-info-url');
|
||||
if (!url) return;
|
||||
openInfo(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
160
shiftflow/templates/shiftflow/product_info_assembly.html
Normal file
160
shiftflow/templates/shiftflow/product_info_assembly.html
Normal file
@@ -0,0 +1,160 @@
|
||||
<div class="container-fluid p-0">
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Заполнено</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Чертёж (PDF)</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="pdf_main" accept="application/pdf" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.pdf_main %}
|
||||
<div class="small mt-1"><a href="{{ entity.pdf_main.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Маршрут</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="route_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for r in routes %}
|
||||
<option value="{{ r.id }}" {% if entity.route_id == r.id %}selected{% endif %}>{{ r.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Масса, кг</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="weight_kg" value="{% if passport and passport.weight_kg %}{{ passport.weight_kg }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Покрытие</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="coating" value="{% if passport %}{{ passport.coating }}{% endif %}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Цвет</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="coating_color" value="{% if passport %}{{ passport.coating_color }}{% endif %}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Площадь покрытия, м²</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="coating_area_m2" value="{% if passport and passport.coating_area_m2 %}{{ passport.coating_area_m2 }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Технические требования</label>
|
||||
<textarea class="form-control bg-body text-body border-secondary" name="technical_requirements" rows="4" {% if not can_edit %}disabled{% endif %}>{% if passport %}{{ passport.technical_requirements }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="mt-3 d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="create_route">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input class="form-control bg-body text-body border-secondary" name="route_name" placeholder="Новый маршрут">
|
||||
<button class="btn btn-outline-secondary" type="submit">Добавить маршрут</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="fw-bold mb-2">Сварные швы</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Наименование</th>
|
||||
<th>Катет, мм</th>
|
||||
<th>Длина, мм</th>
|
||||
<th>Кол-во</th>
|
||||
<th data-sort="false"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in welding_seams %}
|
||||
<tr>
|
||||
<td>{{ s.name }}</td>
|
||||
<td>{{ s.leg_mm }}</td>
|
||||
<td>{{ s.length_mm }}</td>
|
||||
<td>{{ s.quantity }}</td>
|
||||
<td class="text-end">
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="delete_weld_seam">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input type="hidden" name="seam_id" value="{{ s.id }}">
|
||||
<button class="btn btn-outline-secondary btn-sm" type="submit">Удалить</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Швы не добавлены</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="row g-2 mt-2">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="add_weld_seam">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="seam_name" placeholder="Напр. Шов по периметру">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Катет, мм</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="seam_leg_mm" inputmode="decimal" placeholder="4">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Длина, мм</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="seam_length_mm" inputmode="decimal" placeholder="120">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<label class="form-label">Кол-во</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="seam_quantity" value="1">
|
||||
</div>
|
||||
<div class="col-md-1 d-flex align-items-end justify-content-end">
|
||||
<button class="btn btn-outline-accent" type="submit">+</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
84
shiftflow/templates/shiftflow/product_info_casting.html
Normal file
84
shiftflow/templates/shiftflow/product_info_casting.html
Normal file
@@ -0,0 +1,84 @@
|
||||
<div class="container-fluid p-0">
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Заполнено</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Материал литья</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="casting_material" value="{% if passport %}{{ passport.casting_material }}{% endif %}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Масса, кг</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="mass_kg" value="{% if passport and passport.mass_kg %}{{ passport.mass_kg }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Маршрут</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="route_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for r in routes %}
|
||||
<option value="{{ r.id }}" {% if entity.route_id == r.id %}selected{% endif %}>{{ r.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Чертёж (PDF)</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="pdf_main" accept="application/pdf" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.pdf_main %}
|
||||
<div class="small mt-1"><a href="{{ entity.pdf_main.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Картинка</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="preview" accept="image/*" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.preview %}
|
||||
<div class="small mt-1"><a href="{{ entity.preview.url }}" target="_blank">Открыть текущую</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="mt-3 d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="create_route">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input class="form-control bg-body text-body border-secondary" name="route_name" placeholder="Новый маршрут">
|
||||
<button class="btn btn-outline-secondary" type="submit">Добавить маршрут</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
36
shiftflow/templates/shiftflow/product_info_external.html
Normal file
36
shiftflow/templates/shiftflow/product_info_external.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="container-fluid p-0">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Паспорт заполнен</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 d-flex align-items-end justify-content-end">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
76
shiftflow/templates/shiftflow/product_info_outsourced.html
Normal file
76
shiftflow/templates/shiftflow/product_info_outsourced.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<div class="container-fluid p-0">
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Заполнено</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Маршрут</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="route_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for r in routes %}
|
||||
<option value="{{ r.id }}" {% if entity.route_id == r.id %}selected{% endif %}>{{ r.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Чертёж/ТЗ (PDF)</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="pdf_main" accept="application/pdf" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.pdf_main %}
|
||||
<div class="small mt-1"><a href="{{ entity.pdf_main.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Технические требования</label>
|
||||
<textarea class="form-control bg-body text-body border-secondary" name="technical_requirements" rows="4" {% if not can_edit %}disabled{% endif %}>{% if passport %}{{ passport.technical_requirements }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Пояснения</label>
|
||||
<textarea class="form-control bg-body text-body border-secondary" name="notes" rows="3" {% if not can_edit %}disabled{% endif %}>{% if passport %}{{ passport.notes }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="mt-3 d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="create_route">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input class="form-control bg-body text-body border-secondary" name="route_name" placeholder="Новый маршрут">
|
||||
<button class="btn btn-outline-secondary" type="submit">Добавить маршрут</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
127
shiftflow/templates/shiftflow/product_info_part.html
Normal file
127
shiftflow/templates/shiftflow/product_info_part.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<div class="container-fluid p-0">
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Заполнено</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Материал заготовки</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="planned_material_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for m in materials %}
|
||||
<option value="{{ m.id }}" {% if entity.planned_material_id == m.id %}selected{% endif %}>{{ m.full_name|default:m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Маршрут</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="route_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for r in routes %}
|
||||
<option value="{{ r.id }}" {% if entity.route_id == r.id %}selected{% endif %}>{{ r.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Толщина, мм</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="thickness_mm" value="{% if passport and passport.thickness_mm %}{{ passport.thickness_mm }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Длина, мм</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="length_mm" value="{% if passport and passport.length_mm %}{{ passport.length_mm }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Масса, кг</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="mass_kg" value="{% if passport and passport.mass_kg %}{{ passport.mass_kg }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Длина реза, мм</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="cut_length_mm" value="{% if passport and passport.cut_length_mm %}{{ passport.cut_length_mm }}{% endif %}" inputmode="decimal" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Кол-во врезок</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="pierce_count" value="{% if passport and passport.pierce_count %}{{ passport.pierce_count }}{% endif %}" inputmode="numeric" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Чертёж (PDF)</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="pdf_main" accept="application/pdf" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.pdf_main %}
|
||||
<div class="small mt-1"><a href="{{ entity.pdf_main.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">DXF/IGES/STEP</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="dxf_file" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.dxf_file %}
|
||||
<div class="small mt-1"><a href="{{ entity.dxf_file.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Картинка</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="preview" accept="image/*" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.preview %}
|
||||
<div class="small mt-1"><a href="{{ entity.preview.url }}" target="_blank">Открыть текущую</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Гравировка</label>
|
||||
<textarea class="form-control bg-body text-body border-secondary" name="engraving" rows="2" {% if not can_edit %}disabled{% endif %}>{% if passport %}{{ passport.engraving }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Технические требования</label>
|
||||
<textarea class="form-control bg-body text-body border-secondary" name="technical_requirements" rows="4" {% if not can_edit %}disabled{% endif %}>{% if passport %}{{ passport.technical_requirements }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="mt-3 d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="create_route">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input class="form-control bg-body text-body border-secondary" name="route_name" placeholder="Новый маршрут">
|
||||
<button class="btn btn-outline-secondary" type="submit">Добавить маршрут</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
79
shiftflow/templates/shiftflow/product_info_purchased.html
Normal file
79
shiftflow/templates/shiftflow/product_info_purchased.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<div class="container-fluid p-0">
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="save">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<input class="form-control bg-body text-body border-secondary" value="{{ entity.get_entity_type_display }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="drawing_number" value="{{ entity.drawing_number }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Заполнен</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="passport_filled" id="pf" {% if entity.passport_filled %}checked{% endif %} {% if not can_edit %}disabled{% endif %}>
|
||||
<label class="form-check-label" for="pf">Заполнено</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="name" value="{{ entity.name }}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">ГОСТ/ТУ</label>
|
||||
<input class="form-control bg-body text-body border-secondary" name="gost" value="{% if passport %}{{ passport.gost }}{% endif %}" {% if not can_edit %}disabled{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Маршрут</label>
|
||||
<select class="form-select bg-body text-body border-secondary" name="route_id" {% if not can_edit %}disabled{% endif %}>
|
||||
<option value="">— не указано —</option>
|
||||
{% for r in routes %}
|
||||
<option value="{{ r.id }}" {% if entity.route_id == r.id %}selected{% endif %}>{{ r.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Чертёж/паспорт (PDF)</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="pdf_main" accept="application/pdf" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.pdf_main %}
|
||||
<div class="small mt-1"><a href="{{ entity.pdf_main.url }}" target="_blank">Открыть текущий</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Картинка</label>
|
||||
<input class="form-control bg-body text-body border-secondary" type="file" name="preview" accept="image/*" {% if not can_edit %}disabled{% endif %}>
|
||||
{% if entity.preview %}
|
||||
<div class="small mt-1"><a href="{{ entity.preview.url }}" target="_blank">Открыть текущую</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end mt-2">
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-accent" type="submit">Сохранить</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{% url 'product_info' entity.id %}" class="mt-3 d-flex gap-2 align-items-center">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="create_route">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<input class="form-control bg-body text-body border-secondary" name="route_name" placeholder="Новый маршрут">
|
||||
<button class="btn btn-outline-secondary" type="submit">Добавить маршрут</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
162
shiftflow/templates/shiftflow/products.html
Normal file
162
shiftflow/templates/shiftflow/products.html
Normal file
@@ -0,0 +1,162 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card shadow border-secondary mb-3">
|
||||
<div class="card-header border-secondary py-3 d-flex flex-wrap gap-2 justify-content-between align-items-center">
|
||||
<h3 class="text-accent mb-0"><i class="bi bi-diagram-3 me-2"></i>Изделия</h3>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#createProductModal">
|
||||
<i class="bi bi-plus-lg me-1"></i>Создать
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<form class="d-flex gap-2 align-items-center" method="get" action="{% url 'products' %}">
|
||||
<select class="form-select form-select-sm bg-body text-body border-secondary" name="entity_type" style="min-width: 220px;">
|
||||
<option value="" {% if not entity_type %}selected{% endif %}>Все типы</option>
|
||||
<option value="product" {% if entity_type == 'product' %}selected{% endif %}>Готовое изделие</option>
|
||||
<option value="assembly" {% if entity_type == 'assembly' %}selected{% endif %}>Сборочная единица</option>
|
||||
<option value="part" {% if entity_type == 'part' %}selected{% endif %}>Деталь</option>
|
||||
<option value="purchased" {% if entity_type == 'purchased' %}selected{% endif %}>Покупное</option>
|
||||
<option value="casting" {% if entity_type == 'casting' %}selected{% endif %}>Литьё</option>
|
||||
<option value="outsourced" {% if entity_type == 'outsourced' %}selected{% endif %}>Аутсорс</option>
|
||||
</select>
|
||||
|
||||
<input class="form-control form-control-sm bg-body text-body border-secondary" name="q" value="{{ q }}" placeholder="Поиск (обозначение/наименование)" style="min-width: 320px;">
|
||||
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="bi bi-search me-1"></i>Найти</button>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'products' %}"><i class="bi bi-arrow-counterclockwise me-1"></i>Сброс</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Тип</th>
|
||||
<th>Обозначение</th>
|
||||
<th>Наименование</th>
|
||||
<th>Материал</th>
|
||||
<th>Заполнен</th>
|
||||
<th data-sort="false"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in products %}
|
||||
<tr class="product-row" role="button" data-info-url="{% url 'product_info' p.id %}">
|
||||
<td class="small text-muted">{{ p.get_entity_type_display }}</td>
|
||||
<td class="fw-bold">{{ p.drawing_number|default:"—" }}</td>
|
||||
<td>{{ p.name }}</td>
|
||||
<td class="small text-muted">
|
||||
{% if p.planned_material_id %}
|
||||
{{ p.planned_material.full_name|default:p.planned_material.name }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if p.passport_filled %}
|
||||
<i class="bi bi-check-circle-fill text-success" title="Заполнено"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-circle text-muted" title="Не заполнено"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-outline-accent btn-sm" href="{% url 'product_detail' p.id %}" onclick="event.stopPropagation();">
|
||||
{% if p.entity_type == 'product' or p.entity_type == 'assembly' %}Состав{% else %}Открыть{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="5" class="text-center text-muted py-4">Пока ничего нет</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="createProductModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<form method="post" action="{% url 'products' %}" class="modal-content border-secondary">
|
||||
{% csrf_token %}
|
||||
<div class="modal-header border-secondary">
|
||||
<h5 class="modal-title">Создать изделие</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Тип</label>
|
||||
<select class="form-select" name="entity_type" required>
|
||||
<option value="product">Изделие</option>
|
||||
<option value="assembly">Сборочная единица</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Обозначение</label>
|
||||
<input class="form-control" name="drawing_number" placeholder="Напр. УРН.01">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Наименование</label>
|
||||
<input class="form-control" name="name" placeholder="Напр. Урна уличная" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer border-secondary">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-outline-accent">Создать</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="productInfoModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content border-secondary">
|
||||
<div class="modal-header border-secondary">
|
||||
<h5 class="modal-title">Информация о компоненте</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="productInfoBody">
|
||||
<div class="text-muted">Загрузка...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modalEl = document.getElementById('productInfoModal');
|
||||
const bodyEl = document.getElementById('productInfoBody');
|
||||
if (!modalEl || !bodyEl) return;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
|
||||
async function openInfo(url) {
|
||||
bodyEl.innerHTML = '<div class="text-muted">Загрузка...</div>';
|
||||
modal.show();
|
||||
try {
|
||||
const nextUrl = encodeURIComponent(window.location.pathname + window.location.search);
|
||||
const sep = url.includes('?') ? '&' : '?';
|
||||
const res = await fetch(url + sep + 'next=' + nextUrl, { credentials: 'same-origin' });
|
||||
bodyEl.innerHTML = await res.text();
|
||||
} catch (e) {
|
||||
bodyEl.innerHTML = '<div class="alert alert-danger">Не удалось загрузить информацию.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('tr.product-row[data-info-url]').forEach(tr => {
|
||||
tr.addEventListener('click', () => {
|
||||
const url = tr.getAttribute('data-info-url');
|
||||
if (!url) return;
|
||||
openInfo(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user