Открыл мастеру возможность просмотра сделок и потребность в деталях, подправил окно редактирования позиции сделки, подправил работу фильтра
All checks were successful
Deploy MES Core / deploy (push) Successful in 10s
All checks were successful
Deploy MES Core / deploy (push) Successful in 10s
This commit is contained in:
@@ -25,9 +25,11 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
{% if user_role in 'admin,technologist' %}
|
||||
<button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#dealModal">
|
||||
<i class="bi bi-plus-lg me-1"></i>Создать сделку
|
||||
</button>
|
||||
{% endif %}
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'customers' %}">
|
||||
<i class="bi bi-arrow-left me-1"></i>К заказчикам
|
||||
</a>
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
<div class="card shadow border-secondary">
|
||||
<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>
|
||||
{% if user_role in 'admin,technologist' %}
|
||||
<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>Добавить заказчика
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card-body p-0">
|
||||
|
||||
@@ -7,12 +7,27 @@
|
||||
<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.task.drawing_name|default:"Без названия" }}</h3>
|
||||
<span class="badge bg-secondary">Сделка № {{ item.task.deal.number }}</span>
|
||||
<h3 class="text-accent mb-0">
|
||||
{% if user_role == 'operator' %}
|
||||
<i class="bi bi-info-circle me-2"></i>{{ item.task.drawing_name|default:"Без названия" }}
|
||||
{% else %}
|
||||
<a href="{% url 'task_items' item.task.id %}" class="text-decoration-none text-reset">
|
||||
<i class="bi bi-info-circle me-2"></i>{{ item.task.drawing_name|default:"Без названия" }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
{% if user_role == 'operator' %}
|
||||
<span class="badge bg-secondary">Сделка № {{ item.task.deal.number }}</span>
|
||||
{% else %}
|
||||
<a href="{% url 'planning_deal' item.task.deal.id %}" class="text-decoration-none">
|
||||
<span class="badge bg-secondary">Сделка № {{ item.task.deal.number }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form method="post" id="mainForm" class="card-body p-4">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
|
||||
{% if errors %}
|
||||
<div class="alert alert-danger mb-4">
|
||||
@@ -55,22 +70,12 @@
|
||||
{% 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 class="row g-3 text-start">
|
||||
<div class="col-md-4">
|
||||
<label class="small text-muted">Факт (шт)</label>
|
||||
<input type="number" name="quantity_fact" id="id_quantity_fact" class="form-control border-secondary" value="{{ item.quantity_fact }}" max="{{ item.quantity_plan }}">
|
||||
</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 class="row g-3 mt-3 text-start">
|
||||
<div class="col-md-4">
|
||||
<label class="small text-muted">Взятый материал</label>
|
||||
<input type="text" name="material_taken" class="form-control border-secondary" value="{{ item.material_taken }}" placeholder="Напр: 3 трубы по 12м" required>
|
||||
@@ -187,38 +192,25 @@
|
||||
{% 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>
|
||||
<a href="{{ back_url }}" class="btn btn-outline-secondary">Назад</a>
|
||||
<div class="d-flex gap-2">
|
||||
<input type="hidden" name="action" id="actionField" value="save">
|
||||
{% if item.status == 'work' %}
|
||||
<button type="submit" class="btn btn-success px-4" onclick="document.getElementById('actionField').value='close_done'">
|
||||
<i class="bi bi-check-all me-2"></i>Выполнено
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-warning px-4" onclick="document.getElementById('actionField').value='close_partial'">
|
||||
Частично
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-outline-accent px-4 fw-bold" onclick="document.getElementById('actionField').value='save'">
|
||||
<i class="bi bi-save me-2"></i>Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</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 = 'partial';
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -66,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 text-end mt-auto">
|
||||
<a href="{% url 'registry' %}" class="btn btn-outline-secondary btn-sm w-100" title="Сброс">
|
||||
<a href="{% url 'registry' %}?reset=1" class="btn btn-outline-secondary btn-sm w-100" id="registryResetBtn" title="Сброс">
|
||||
<i class="bi bi-arrow-counterclockwise me-1"></i>Сброс
|
||||
</a>
|
||||
</div>
|
||||
@@ -104,6 +104,10 @@
|
||||
function restoreFilters(){
|
||||
if (!form) return false;
|
||||
const qs = new URLSearchParams(window.location.search);
|
||||
if (qs.get('reset') === '1') {
|
||||
try { localStorage.removeItem('registry_filters'); } catch(_) {}
|
||||
return false;
|
||||
}
|
||||
if (qs.get('filtered') === '1') return false;
|
||||
let raw = null; try { raw = localStorage.getItem('registry_filters'); } catch(_){}
|
||||
if (!raw) return false;
|
||||
@@ -128,6 +132,14 @@
|
||||
|
||||
if (form){
|
||||
form.addEventListener('change', saveFilters, true);
|
||||
|
||||
const resetBtn = document.getElementById('registryResetBtn');
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', function () {
|
||||
try { localStorage.removeItem('registry_filters'); } catch(_) {}
|
||||
});
|
||||
}
|
||||
|
||||
restoreFilters();
|
||||
}
|
||||
});
|
||||
|
||||
94
shiftflow/templates/shiftflow/partials/_items_table.html
Normal file
94
shiftflow/templates/shiftflow/partials/_items_table.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<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 style="width: 160px;">Прогресс</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.task.deal.number|default:"-" }}</span></td>
|
||||
<td><span class="badge bg-dark border border-secondary">{{ item.machine.name }}</span></td>
|
||||
<td class="fw-bold">{{ item.task.drawing_name|default:"Б/ч" }}</td>
|
||||
<td class="small">{{ item.task.size_value|default:"-" }}</td>
|
||||
|
||||
<td>
|
||||
<div class="progress bg-secondary-subtle border border-secondary sf-item-progress"
|
||||
style="height: 10px;"
|
||||
data-fact-width="{{ item.fact_width|default:0 }}"
|
||||
title="Факт: {{ item.fact_pct|default:0 }}%">
|
||||
<div class="progress-bar {{ item.fact_bar_class|default:'bg-warning' }} sf-item-progress-bar"></div>
|
||||
</div>
|
||||
</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.task.material.full_name|default:item.task.material.name|default:"-" }}</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if item.task.drawing_file %}
|
||||
<a href="{{ item.task.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.task.extra_drawing %}
|
||||
<a href="{{ item.task.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{% elif item.status == 'partial' %}bg-success-subtle text-success-emphasis border border-success-subtle{% else %}bg-secondary{% endif %}">
|
||||
{{ item.get_status_display }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="11" class="text-center p-5 text-muted">Заданий не найдено</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.querySelectorAll(".clickable-row").forEach(row => {
|
||||
row.addEventListener("click", function(e) {
|
||||
if (e.target.closest('.stop-prop')) return;
|
||||
window.location.href = this.dataset.href;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.sf-item-progress').forEach(function (el) {
|
||||
const w = parseInt(el.getAttribute('data-fact-width') || '0', 10) || 0;
|
||||
const bar = el.querySelector('.sf-item-progress-bar');
|
||||
if (bar) bar.style.width = `${w}%`;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -19,9 +19,11 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if user_role in 'admin,technologist' %}
|
||||
<button type="button" class="btn btn-outline-accent btn-sm" data-bs-toggle="modal" data-bs-target="#dealModal" data-mode="create">
|
||||
<i class="bi bi-plus-lg me-1"></i>Добавить сделку
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card-body p-0">
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'planning' %}">
|
||||
<i class="bi bi-arrow-left me-1"></i>Назад
|
||||
</a>
|
||||
{% if user_role in 'admin,technologist' %}
|
||||
<a class="btn btn-outline-accent btn-sm" href="{% url 'task_add' %}?deal={{ deal.id }}&next={% url 'planning_deal' deal.id %}">
|
||||
<i class="bi bi-plus-lg me-1"></i>Добавить деталь
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,6 +74,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{% if user_role in 'admin,technologist' %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-accent btn-sm"
|
||||
@@ -83,6 +86,7 @@
|
||||
>
|
||||
<i class="bi bi-plus-lg me-1"></i>В план
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
|
||||
@@ -13,81 +13,6 @@
|
||||
{% endif %}
|
||||
</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.task.deal.number|default:"-" }}</span></td>
|
||||
<td><span class="badge bg-dark border border-secondary">{{ item.machine.name }}</span></td>
|
||||
<td class="fw-bold">{{ item.task.drawing_name|default:"Б/ч" }}</td>
|
||||
<td class="small">{{ item.task.size_value|default:"-" }}</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.task.material.full_name|default:item.task.material.name|default:"-" }}</td>
|
||||
<td class="text-center">
|
||||
{% if item.task.drawing_file %}
|
||||
<a href="{{ item.task.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.task.extra_drawing %}
|
||||
<a href="{{ item.task.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{% elif item.status == 'partial' %}bg-success-subtle text-success-emphasis border border-success-subtle{% 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>
|
||||
{% include 'shiftflow/partials/_items_table.html' with items=items %}
|
||||
</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 %}
|
||||
@@ -16,77 +16,6 @@
|
||||
</a>
|
||||
</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 it in items %}
|
||||
<tr class="clickable-row" data-href="{% url 'item_detail' it.pk %}">
|
||||
<td class="small">{{ it.date|date:"d.m.y" }}</td>
|
||||
<td><span class="text-accent fw-bold">{{ it.task.deal.number|default:"-" }}</span></td>
|
||||
<td><span class="badge bg-dark border border-secondary">{{ it.machine.name }}</span></td>
|
||||
<td class="fw-bold">{{ it.task.drawing_name|default:"Б/ч" }}</td>
|
||||
<td class="small">{{ it.task.size_value|default:"-" }}</td>
|
||||
<td>
|
||||
<span class="text-info fw-bold">{{ it.quantity_plan }}</span> /
|
||||
<span class="text-success">{{ it.quantity_fact }}</span>
|
||||
</td>
|
||||
<td class="small text-muted">{{ it.task.material.full_name|default:it.task.material.name|default:"-" }}</td>
|
||||
<td class="text-center">
|
||||
{% if it.task.drawing_file %}
|
||||
<a href="{{ it.task.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 it.task.extra_drawing %}
|
||||
<a href="{{ it.task.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 it.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 it.status == 'work' %}bg-primary{% elif it.status == 'done' %}bg-success{% elif it.status == 'partial' %}bg-success-subtle text-success-emphasis border border-success-subtle{% else %}bg-secondary{% endif %}">{{ it.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>
|
||||
{% include 'shiftflow/partials/_items_table.html' with items=items %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const rows = document.querySelectorAll(".clickable-row");
|
||||
rows.forEach(row => {
|
||||
row.addEventListener("click", function(e) {
|
||||
if (e.target.closest('.stop-prop')) return;
|
||||
window.location.href = this.dataset.href;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user