- 库存看板新增「BOM 物料齐套」标签页(仅 admin/manager), 逐棵 BOM 列出组成物料的 单套用量/当前在库/状态, 并给出整体齐套结论 - BOM 详情页组成物料表新增「当前在库」「状态」两列 - 抽出统一 _status_of() 判定函数, 红/黄/绿逻辑与看板一致 - 新增 tools/verify_bom_board.py 端到端验证脚本(已通过) - 新增使用教程 物料入库出库系统_使用教程.md - .gitignore 忽略 _backup/(旧 EXE 备份)
93 lines
3.6 KiB
HTML
93 lines
3.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}库存看板{% endblock %}
|
|
{% block content %}
|
|
<div class="stats">
|
|
<div class="stat"><div class="num">{{ rows|length }}</div><div class="lbl">物料种类</div></div>
|
|
<div class="stat warn"><div class="num">{{ low }}</div><div class="lbl">低于安全库存</div></div>
|
|
<div class="stat danger"><div class="num">{{ negative }}</div><div class="lbl">库存为负(缺料)</div></div>
|
|
</div>
|
|
|
|
<div class="tabs">
|
|
<button class="tab active" type="button" onclick="showTab('mat', this)">📦 库存预警</button>
|
|
{% if can_bom %}<button class="tab" type="button" onclick="showTab('bom', this)">🔧 BOM 物料齐套</button>{% endif %}
|
|
</div>
|
|
|
|
<!-- 标签一: 全部物料库存 -->
|
|
<div id="tab-mat">
|
|
<table class="tbl">
|
|
<thead>
|
|
<tr><th>物料编号</th><th>型号 / 规格</th><th>单位</th><th>在库</th><th>安全库存</th><th>状态</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in rows %}
|
|
<tr class="row-{{ r.status }}">
|
|
<td>{{ r.m.part_number }}</td>
|
|
<td>{{ r.m.manufacturer or '-' }}</td>
|
|
<td>{{ r.m.unit }}</td>
|
|
<td class="num">{{ '%.2f'|format(r.stock) }}</td>
|
|
<td class="num">{{ '%.2f'|format(r.m.safety_stock or 0) }}</td>
|
|
<td>
|
|
{% if r.status=='danger' %}<span class="badge danger">缺料</span>
|
|
{% elif r.status=='warn' %}<span class="badge warn">偏低</span>
|
|
{% else %}<span class="badge ok">正常</span>{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="6" class="muted">暂无物料, 请先到"物料"页面添加</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if can_bom %}
|
|
<!-- 标签二: BOM 物料齐套看板 -->
|
|
<div id="tab-bom" style="display:none">
|
|
{% for bb in bom_board %}
|
|
<div class="card" style="margin-bottom:16px">
|
|
<div class="card-head">
|
|
<strong>{{ bb.b.code }}</strong>
|
|
<span class="muted">{{ bb.b.name or '' }}</span>
|
|
{% if bb.status=='danger' %}<span class="badge danger">缺料</span>
|
|
{% elif bb.status=='warn' %}<span class="badge warn">部分偏低</span>
|
|
{% else %}<span class="badge ok">齐套</span>{% endif %}
|
|
</div>
|
|
<table class="tbl" style="box-shadow:none; margin:0">
|
|
<thead>
|
|
<tr><th>物料编号</th><th>型号 / 规格</th><th>单位</th><th>单套用量</th><th>当前在库</th><th>状态</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for it in bb.parts %}
|
|
<tr class="row-{{ it.status }}">
|
|
<td><a href="{{ url_for('material_detail', mid=it.m.id) }}">{{ it.m.part_number }}</a></td>
|
|
<td>{{ it.m.manufacturer or '-' }}</td>
|
|
<td>{{ it.m.unit }}</td>
|
|
<td class="num">{{ '%.4g'|format(it.qty) }}</td>
|
|
<td class="num">{{ '%.2f'|format(it.stock) }}</td>
|
|
<td>
|
|
{% if it.status=='danger' %}<span class="badge danger">缺料</span>
|
|
{% elif it.status=='warn' %}<span class="badge warn">偏低</span>
|
|
{% else %}<span class="badge ok">正常</span>{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="6" class="muted">该 BOM 暂无组成物料</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="muted">暂无 BOM,请先到 BOM 页面导入或新建。</p>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
function showTab(id, btn){
|
|
document.getElementById('tab-mat').style.display = id==='mat' ? 'block' : 'none';
|
|
document.getElementById('tab-bom').style.display = id==='bom' ? 'block' : 'none';
|
|
document.querySelectorAll('.tabs .tab').forEach(b=>b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
}
|
|
</script>
|
|
{% endblock %}
|