229 lines
8.5 KiB
HTML
229 lines
8.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block body_class %}page-bg bg-devices{% endblock %}
|
|
{% block content %}
|
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
|
<h3 class="mb-0">Каталог</h3>
|
|
</div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Подбор картриджа по модели устройства</h5>
|
|
<form method="get" action="/catalog" class="row g-2 align-items-center">
|
|
<div class="col-md-6">
|
|
<select name="device" class="form-select">
|
|
<option value="">Выберите модель...</option>
|
|
{% for m, t in device_models %}
|
|
{% set label = (m ~ " (" ~ ("Принтер" if t == "printer" else "МФУ") ~ ")") %}
|
|
{% set key = m ~ "||" ~ t %}
|
|
<option value="{{ key }}" {% if selected_key == key %}selected{% endif %}>
|
|
{{ label }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button class="btn btn-primary w-100">Показать</button>
|
|
</div>
|
|
</form>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{% if selected_model %}
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Картриджи для выбранного устройства</h5>
|
|
{% if cartridge_rows %}
|
|
<table class="table table-striped mb-0">
|
|
<tr>
|
|
<th>Штрихкод</th>
|
|
<th>Модель</th>
|
|
<th>Остаток</th>
|
|
<th>Минимум</th>
|
|
</tr>
|
|
{% for b, m, q, minq in cartridge_rows %}
|
|
<tr>
|
|
<td>{{ b }}</td>
|
|
<td>{{ m }}</td>
|
|
<td>{{ q }}</td>
|
|
<td>{{ minq }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
{% else %}
|
|
<div class="alert alert-warning mb-0">Картриджи для выбранной модели не найдены.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Расходные материалы для выбранного устройства</h5>
|
|
{% if consumable_groups %}
|
|
{% for ctype, items in consumable_groups.items() %}
|
|
<div class="mb-3">
|
|
<div class="fw-semibold mb-2">{{ ctype }}</div>
|
|
<table class="table table-striped mb-0">
|
|
<tr>
|
|
<th>Штрихкод</th>
|
|
<th>Модель</th>
|
|
<th>Тип в складе</th>
|
|
<th>Остаток</th>
|
|
{% if session.get('role') == 'admin' %}<th>Действия</th>{% endif %}
|
|
</tr>
|
|
{% for item in items %}
|
|
<tr>
|
|
<td>{{ item.barcode or '—' }}</td>
|
|
<td>{{ item.model or '—' }}</td>
|
|
<td>{{ item.stype or '—' }}</td>
|
|
<td>{{ item.qty if item.qty is not none else '—' }}</td>
|
|
{% if session.get('role') == 'admin' and item.id %}
|
|
<td class="text-nowrap">
|
|
<form method="post" action="/catalog/consumables/delete" class="d-inline" onsubmit="return confirm('Удалить привязку расходника?')">
|
|
<input type="hidden" name="id" value="{{ item.id }}">
|
|
<input type="hidden" name="device" value="{{ selected_key }}">
|
|
<button class="btn btn-sm btn-outline-danger">Удалить</button>
|
|
</form>
|
|
</td>
|
|
{% elif session.get('role') == 'admin' %}
|
|
<td class="text-muted">Авто</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="alert alert-warning mb-0">Для выбранного устройства расходники не настроены.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% endif %}
|
|
|
|
{% if session.get('role') == 'admin' %}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Настройка соответствия (админ)</h5>
|
|
<form method="post" action="/catalog/map" class="row g-2 align-items-center">
|
|
<div class="col-md-5">
|
|
<select name="device" class="form-select">
|
|
<option value="">Модель устройства...</option>
|
|
{% for m, t in device_models %}
|
|
{% set label = (m ~ " (" ~ ("Принтер" if t == "printer" else "МФУ") ~ ")") %}
|
|
{% set key = m ~ "||" ~ t %}
|
|
<option value="{{ key }}" {% if selected_key == key %}selected{% endif %}>
|
|
{{ label }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<select name="cartridge_model" class="form-select">
|
|
<option value="">Модель картриджа...</option>
|
|
{% for m in cartridges %}
|
|
<option value="{{ m }}" {% if cartridge_model == m %}selected{% endif %}>{{ m }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-success">Сохранить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-3">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Расходники для устройств (админ)</h5>
|
|
<form method="post" action="/catalog/consumables/map" class="row g-2 align-items-center">
|
|
<div class="col-md-4">
|
|
<select name="device" class="form-select">
|
|
<option value="">Модель устройства...</option>
|
|
{% for m, t in device_models %}
|
|
{% set label = (m ~ " (" ~ ("Принтер" if t == "printer" else "МФУ") ~ ")") %}
|
|
{% set key = m ~ "||" ~ t %}
|
|
<option value="{{ key }}" {% if selected_key == key %}selected{% endif %}>
|
|
{{ label }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="d-flex gap-2">
|
|
<select class="form-select" data-consumable-type-select>
|
|
<option value="">Тип расходника...</option>
|
|
{% for t in consumable_types %}
|
|
<option value="{{ t }}">{{ t }}</option>
|
|
{% endfor %}
|
|
<option value="__custom__">Другое...</option>
|
|
</select>
|
|
<input class="form-control" placeholder="Свой тип" data-consumable-type-custom>
|
|
<input type="hidden" name="consumable_type" data-consumable-type-value>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select name="consumable_barcode" class="form-select" data-consumable-barcode-select>
|
|
<option value="">Расходный материал...</option>
|
|
{% for b, m, t, q in consumables %}
|
|
<option value="{{ b }}" data-consumable-type="{{ t }}">{{ m }} ({{ b }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-success">Сохранить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<script>
|
|
(function() {
|
|
const select = document.querySelector('[data-consumable-type-select]');
|
|
if (!select) return;
|
|
const custom = document.querySelector('[data-consumable-type-custom]');
|
|
const value = document.querySelector('[data-consumable-type-value]');
|
|
const barcodeSelect = document.querySelector('[data-consumable-barcode-select]');
|
|
|
|
function sync() {
|
|
const sel = select.value;
|
|
if (sel === '__custom__') {
|
|
custom.disabled = false;
|
|
value.value = (custom.value || '').trim();
|
|
} else if (sel) {
|
|
custom.disabled = true;
|
|
value.value = sel;
|
|
} else {
|
|
custom.disabled = false;
|
|
value.value = (custom.value || '').trim();
|
|
}
|
|
filterConsumables();
|
|
}
|
|
|
|
function filterConsumables() {
|
|
if (!barcodeSelect) return;
|
|
const selectedType = (value.value || '').trim();
|
|
const normSelected = selectedType.toLowerCase();
|
|
const options = Array.from(barcodeSelect.options);
|
|
let hasMatch = false;
|
|
options.forEach((opt, idx) => {
|
|
if (idx === 0) return; // placeholder
|
|
const optType = (opt.getAttribute('data-consumable-type') || '').trim();
|
|
const normOpt = optType.toLowerCase();
|
|
const show = !selectedType || normOpt === normSelected;
|
|
opt.hidden = !show;
|
|
opt.disabled = !show;
|
|
if (show) hasMatch = true;
|
|
});
|
|
if (!hasMatch) {
|
|
barcodeSelect.value = '';
|
|
}
|
|
}
|
|
|
|
select.addEventListener('change', sync);
|
|
custom.addEventListener('input', sync);
|
|
sync();
|
|
})();
|
|
</script>
|
|
{% endblock %}
|