добавил детальный вид итема, пока недопиленый
All checks were successful
Deploy MES Core / deploy (push) Successful in 8s

This commit is contained in:
tertelius
2026-03-29 02:49:28 +03:00
parent a4ba577206
commit b256bec04b
15 changed files with 539 additions and 226 deletions

View File

@@ -0,0 +1,120 @@
{% extends 'base.html' %}
{% block title %}ShiftFlow | {{ item.drawing_name|default:"Б/ч" }}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-10 col-xl-8">
<div class="card shadow bg-dark text-light border-secondary mb-4">
<div class="card-header border-secondary d-flex justify-content-between align-items-center py-3">
<h3 class="text-accent mb-0">
<i class="bi bi-info-circle me-2"></i>
Деталь: {{ item.drawing_name|default:"Без чертежа" }}
</h3>
<span class="badge bg-secondary opacity-75 fw-normal">ID: {{ item.id }}</span>
</div>
<form method="post" class="card-body p-4">
{% csrf_token %}
<div class="row g-3 mb-4 border-bottom border-secondary pb-4">
<div class="col-md-4 col-6">
<label class="small text-muted">Дата задания:</label>
<div class="fw-bold">{{ item.date|date:"d.m.Y" }}</div>
</div>
<div class="col-md-4 col-6">
<label class="small text-muted">Станок:</label>
<div class="fw-bold"><i class="bi bi-cpu me-1"></i>{{ item.machine.name }}</div>
</div>
<div class="col-md-4 col-12">
<label class="small text-muted">Сделка/Заказ:</label>
<div class="fw-bold text-accent fs-5">№ {{ item.deal.number }}</div>
</div>
<div class="col-md-12">
<label class="small text-muted">Материал / Габариты:</label>
<div class="fw-bold small">{{ item.material.name }} (s{{ item.size_value|default:"-" }})</div>
</div>
</div>
<div class="mb-4">
<h5 class="text-accent mb-3"><i class="bi bi-files me-2"></i>Файлы задания</h5>
<div class="d-flex gap-2">
{% if item.drawing_file %}
<a href="{{ item.drawing_file.url }}" target="_blank" class="btn btn-outline-info">
<i class="bi bi-file-earmark-code me-2"></i>Открыть DXF/STEP
</a>
{% else %}
<button type="button" class="btn btn-sm btn-outline-secondary p-1" disabled><i class="bi bi-file-earmark-code me-1"></i>DXF: нет файла</button>
{% endif %}
{% if item.extra_drawing %}
<a href="{{ item.extra_drawing.url }}" target="_blank" class="btn btn-outline-danger">
<i class="bi bi-file-pdf me-2"></i>Открыть Чертеж PDF
</a>
{% else %}
<button type="button" class="btn btn-sm btn-outline-secondary p-1" disabled><i class="bi bi-file-pdf me-1"></i>PDF: нет файла</button>
{% endif %}
</div>
</div>
<div class="mb-4">
<h5 class="text-accent mb-3"><i class="bi bi-vector-pen me-2"></i>Фактическое исполнение</h5>
<div class="row g-3">
<div class="col-md-4 col-6">
<label class="form-label small text-muted">Заказано штук (План):</label>
<input type="number" class="form-control form-control-lg fw-bold bg-secondary text-info" value="{{ item.quantity_plan }}" readonly disabled>
</div>
<div class="col-md-4 col-6">
<label for="id_quantity_fact" class="form-label small text-muted">Изготовлено штук (Факт):</label>
<input type="number" name="quantity_fact" id="id_quantity_fact" class="form-control form-control-lg bg-dark text-light border-secondary" value="{{ item.quantity_fact }}" min="0" required>
</div>
<div class="col-md-4 col-12 d-flex align-items-end">
<div class="form-check form-switch bg-dark border border-secondary p-3 rounded shadow-sm w-100 h-100 d-flex align-items-center justify-content-between">
<label class="form-check-label ms-1 text-light small" for="id_is_synced_1c">Списано в 1С?</label>
<input class="form-check-input form-switch-lg stop-prop" type="checkbox" name="is_synced_1c" id="id_is_synced_1c" {% if item.is_synced_1c %}checked{% endif %}>
</div>
</div>
</div>
</div>
<div class="mb-5">
<label for="id_status" class="form-label small text-muted">Текущий статус задания:</label>
<select name="status" id="id_status" class="form-select bg-dark text-light border-secondary form-select-lg">
{% for choice_val, choice_label in form.fields.status.choices %}
<option value="{{ choice_val }}" {% if item.status == choice_val %}selected{% endif %}>
{{ choice_label }}
</option>
{% endfor %}
</select>
</div>
<div class="card-footer border-secondary bg-dark p-0 pt-4 d-flex justify-content-between">
<a href="{% url 'registry' %}" class="btn btn-outline-secondary btn-lg px-4">
<i class="bi bi-arrow-left me-2"></i>В реестр
</a>
<button type="submit" class="btn btn-outline-accent btn-lg px-5 fw-bold">
<i class="bi bi-save me-2"></i>Сохранить изменения
</button>
</div>
{% if form.errors %}
<div class="alert alert-danger mt-4 small mb-0 p-3 shadow-sm border-danger">
<h6 class="fw-bold mb-2">Обнаружены ошибки:</h6>
<ul class="mb-0">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<li>{{ field|upper }}: {{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
{% endif %}
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,126 @@
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-10 col-xl-8">
<div class="card shadow-sm border-secondary mb-4">
<div class="card-header border-secondary d-flex justify-content-between align-items-center py-3">
<h3 class="text-accent mb-0"><i class="bi bi-info-circle me-2"></i>{{ item.drawing_name|default:"Без названия" }}</h3>
<span class="badge bg-secondary">Сделка № {{ item.deal.number }}</span>
</div>
<form method="post" id="mainForm" class="card-body p-4">
{% csrf_token %}
<div class="row g-3 mb-4 border-bottom border-secondary pb-3 text-body">
<div class="col-md-4">
<small class="text-muted d-block">Станок</small>
<strong>{{ item.machine.name }}</strong>
</div>
<div class="col-md-4">
<small class="text-muted d-block">Материал</small>
<strong>{{ item.material.name }}</strong>
</div>
<div class="col-md-4">
<small class="text-muted d-block">План</small>
<strong class="text-info fs-5">{{ item.quantity_plan }} шт.</strong>
</div>
</div>
<div class="mb-4 d-flex gap-2">
{% if item.drawing_file %}<a href="{{ item.drawing_file.url }}" target="_blank" class="btn btn-outline-info btn-sm">DXF</a>{% endif %}
{% if item.extra_drawing %}<a href="{{ item.extra_drawing.url }}" target="_blank" class="btn btn-outline-danger btn-sm">PDF</a>{% endif %}
</div>
<input type="hidden" name="status" id="id_status" value="{{ item.status }}">
{% if user_role in 'operator,master' %}
{% if item.status == 'work' %}
<div class="bg-body-tertiary p-3 rounded border mb-4 text-center">
<h5 class="mb-3">Закрыть задание:</h5>
<div class="btn-group btn-group-lg w-100">
<button type="button" class="btn btn-success" onclick="closeTask('done')">
<i class="bi bi-check-all"></i> Выполнено
</button>
<button type="button" class="btn btn-outline-warning" onclick="showPartial()">
Частично
</button>
</div>
<div id="partialInput" class="mt-3 d-none">
<label class="small text-muted">Сколько сделано?</label>
<input type="number" name="quantity_fact" id="id_quantity_fact" class="form-control form-control-lg text-center mx-auto" style="max-width: 200px;" value="{{ item.quantity_fact }}" max="{{ item.quantity_plan }}">
</div>
</div>
{% else %}
<div class="alert alert-success">Статус: {{ item.get_status_display }}. Сделано: {{ item.quantity_fact }} шт.</div>
<input type="hidden" name="quantity_fact" value="{{ item.quantity_fact }}">
{% endif %}
{% endif %}
{% if user_role == 'admin' %}
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="small text-muted">Статус задания (Админ)</label>
<select name="status" class="form-select border-secondary">
{% for val, name in form.fields.status.choices %}
<option value="{{ val }}" {% if item.status == val %}selected{% endif %}>{{ name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label class="small text-muted">Факт (шт)</label>
<input type="number" name="quantity_fact" class="form-control border-secondary" value="{{ item.quantity_fact }}">
</div>
</div>
{% endif %}
{% if user_role in 'admin,clerk' %}
{% if item.status == 'done' or item.quantity_fact > 0 %}
<div class="form-check form-switch p-3 rounded border border-warning mb-4 bg-body-tertiary d-flex justify-content-between align-items-center">
<label class="form-check-label fw-bold ms-2" for="sync1c">Списано в 1С</label>
<input class="form-check-input ms-0" style="width: 3em; height: 1.5em;" type="checkbox" name="is_synced_1c" id="sync1c" {% if item.is_synced_1c %}checked{% endif %}>
</div>
{% else %}
<div class="text-muted small mb-4"><i class="bi bi-info-circle me-1"></i>Списание будет доступно после выполнения.</div>
{% endif %}
{% if user_role == 'clerk' %}<input type="hidden" name="quantity_fact" value="{{ item.quantity_fact }}">{% endif %}
{% endif %}
<div class="d-flex justify-content-between mt-4">
<a href="{% url 'registry' %}" class="btn btn-outline-secondary">Назад</a>
<button type="submit" class="btn btn-outline-accent px-5 fw-bold">
<i class="bi bi-save me-2"></i>
{% if user_role in 'operator,master' %}Закрыть задание{% else %}Сохранить{% endif %}
</button>
</div>
</form>
</div>
</div>
</div>
<script>
function closeTask(status) {
document.getElementById('id_status').value = status;
// Если "Выполнено", автоматом ставим факт = плану
if(status === 'done') {
const factInput = document.getElementById('id_quantity_fact');
if(factInput) factInput.value = "{{ item.quantity_plan }}";
else {
let hiddenFact = document.createElement('input');
hiddenFact.type = 'hidden';
hiddenFact.name = 'quantity_fact';
hiddenFact.value = "{{ item.quantity_plan }}";
document.getElementById('mainForm').appendChild(hiddenFact);
}
}
document.getElementById('mainForm').submit();
}
function showPartial() {
document.getElementById('partialInput').classList.remove('d-none');
document.getElementById('id_status').value = 'part'; // Статус Частично
}
</script>
{% endblock %}

View File

@@ -1,65 +0,0 @@
{% extends 'base.html' %}
{% block content %}
{% include 'shiftflow/partials/_filter.html' %}
{% if not selected_machines %}
<div class="alert alert-warning text-center mt-4">
<i class="bi bi-info-circle me-2"></i> Станки не выбраны. Выберите станки в фильтре для отображения позиций.
</div>
{% else %}
{% if in_work is not None %}
<h6 class="text-primary mt-4 fw-bold"><i class="bi bi-play-circle me-2"></i>В РАБОТЕ</h6>
<div class="table-responsive rounded border shadow-sm mb-4">
<table class="table table-hover align-middle mb-0">
<thead class="table-custom-header small fw-bold text-uppercase">
<tr>
<th style="width: 80px;">Сделка</th>
<th>Деталь / Чертеж</th>
<th>Материал</th>
<th class="text-center">План</th>
<th class="text-center">Факт</th>
{% if user_role in 'admin,technologist,master,operator' %}
<th class="text-end">Действия</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for item in in_work %}
<tr>
<td class="small">{{ item.deal.number }}</td>
<td><strong>{{ item.drawing_name }}</strong> <small class="text-muted">({{ item.machine.name }})</small></td>
<td><span class="badge bg-secondary opacity-75">{{ item.material.name }}</span></td>
<td class="text-center fw-bold">{{ item.quantity_plan }}</td>
<td class="text-center" style="width: 100px;">
<input type="number" class="form-control form-control-sm text-center" value="{{ item.quantity_fact|default:0 }}" {% if user_role == 'clerk' %}readonly{% endif %}>
</td>
{% if user_role in 'admin,technologist,master,operator' %}
<td class="text-end">
<button class="btn btn-sm btn-outline-success"><i class="bi bi-check2-all"></i></button>
</td>
{% endif %}
</tr>
{% empty %}
<tr><td colspan="6" class="text-center py-4 text-muted small">Активных заданий нет</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% if backlog is not None %}
<h6 class="text-danger fw-bold"><i class="bi bi-exclamation-triangle me-2"></i>НЕДОДЕЛЫ (ХВОСТЫ)</h6>
<div class="table-responsive rounded border shadow-sm mb-4">
</div>
{% endif %}
{% if done_items is not None %}
<h6 class="text-success fw-bold"><i class="bi bi-check-circle me-2"></i>ЗАВЕРШЕНО</h6>
<div class="table-responsive rounded border shadow-sm">
</div>
{% endif %}
{% endif %}
{% endblock %}

View File

@@ -1,38 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="card shadow bg-dark text-light border-secondary">
<div class="card-header border-secondary d-flex justify-content-between align-items-center">
<h3 class="text-accent mb-0">Реестр деталей</h3>
{% if user_role == 'admin' or user_role == 'technologist' %}
<button class="btn btn-sm btn-outline-accent">+ Добавить заказ</button>
{% endif %}
</div>
<div class="card-body">
<table class="table table-dark table-hover">
<thead>
<tr class="table-custom-header">
<th>ID</th>
<th>Наименование</th>
<th>Кол-во</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.quantity }}</td>
<td><span class="badge bg-secondary">{{ item.status }}</span></td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center text-muted">Деталей пока нет</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@@ -1,12 +1,14 @@
{% extends 'base.html' %}
{% block content %}
<div class="text-center">
<h1 class="text-accent mb-4 display-3 fw-bold">
<i class="bi bi-gear-fill me-3"></i>ShiftFlow
</h1>
<a href="{% url 'login' %}" class="btn btn-lg btn-outline-accent px-5 py-3 fw-bold shadow">
ВОЙТИ В СИСТЕМУ
</a>
<div class="flex-center-center">
<div class="text-center">
<h1 class="text-accent mb-4 display-3 fw-bold">
<i class="bi bi-gear-fill me-3"></i>ShiftFlow
</h1>
<a href="{% url 'login' %}" class="btn btn-lg btn-outline-accent px-5 py-3 fw-bold shadow">
ВОЙТИ В СИСТЕМУ
</a>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,86 @@
{% extends 'base.html' %}
{% block content %}
<div class="card shadow border-secondary">
<div class="card-header border-secondary py-3">
<h3 class="text-accent mb-0"><i class="bi bi-list-task me-2"></i>Реестр заданий</h3>
</div>
<div class="card-body p-0">
<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>План / Факт</th>
<th>Материал</th>
<th class="text-center">Файлы</th>
<th class="text-center">1С</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr class="clickable-row" data-href="{% url 'item_detail' item.pk %}">
<td class="small">{{ item.date|date:"d.m.y" }}</td>
<td><span class="text-accent fw-bold">{{ item.deal.number }}</span></td>
<td><span class="badge bg-dark border border-secondary">{{ item.machine.name }}</span></td>
<td class="fw-bold">{{ item.drawing_name }}</td>
<td class="small">{{ item.size_value }}</td>
<td>
<span class="text-info fw-bold">{{ item.quantity_plan }}</span> /
<span class="text-success">{{ item.quantity_fact }}</span>
</td>
<td class="small text-muted">{{ item.material.name }}</td>
<td class="text-center">
{% if item.drawing_file %}
<a href="{{ item.drawing_file.url }}" target="_blank" class="btn btn-sm btn-outline-info p-1 stop-prop" title="DXF/STEP">
<i class="bi bi-file-earmark-code"></i>
</a>
{% endif %}
{% if item.extra_drawing %}
<a href="{{ item.extra_drawing.url }}" target="_blank" class="btn btn-sm btn-outline-danger p-1 stop-prop" title="Чертеж PDF">
<i class="bi bi-file-pdf"></i>
</a>
{% endif %}
</td>
<td class="text-center">
{% if item.is_synced_1c %}
<i class="bi bi-check-circle-fill text-success" title="Учтено"></i>
{% else %}
<i class="bi bi-clock-history text-muted" title="Ожидает"></i>
{% endif %}
</td>
<td>
<span class="badge {% if item.status == 'work' %}bg-primary{% elif item.status == 'done' %}bg-success{% else %}bg-secondary{% endif %}">
{{ item.get_status_display }}
</span>
</td>
</tr>
{% empty %}
<tr><td colspan="10" class="text-center p-5 text-muted">Заданий не найдено</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const rows = document.querySelectorAll(".clickable-row");
rows.forEach(row => {
row.addEventListener("click", function(e) {
// Если нажали на ссылку файла (класс stop-prop), не переходим на страницу деталей
if (e.target.closest('.stop-prop')) return;
// Иначе переходим по ссылке из data-href
window.location.href = this.dataset.href;
});
});
});
</script>
{% endblock %}