Files
Inventory-Management-System/templates/order_detail.html
T
star e9b605fca6 feat: 生产订单调入数量输入优化(去箭头 + 上下方向键行间导航)
- 调入数量输入框由 type=number 改为 type=text inputmode=decimal,去掉浏览器原生上下调节箭头,左右方向键在文本内移动光标
- 新增脚本:在调入数量输入框内按 ↑/↓ 在上一行/下一行同列输入框之间移动焦点(首末行不越界),跳转后光标置于文本末尾,便于逐行录入
2026-10-19 09:52:26 +08:00

116 lines
6.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}订单 {{ o.order_no }}{% endblock %}
{% block content %}
<div class="toolbar">
<h3 style="margin:0">生产订单 {{ o.order_no }}</h3>
<span class="badge {{ 'ok' if o.status=='picked' else 'warn' }}">{{ '已领料' if o.status=='picked' else '待领料' }}</span>
<a class="btn" href="{{ url_for('order_edit', oid=o.id) }}">编辑订单</a>
</div>
<div class="card" style="margin-bottom:16px">
<p class="small muted" style="margin-top:0"><b>下单信息(按下单时记录)</b></p>
<p>BOM: <b>{{ bom.code if bom else '-' }}</b>{% if bom and bom.name %} · {{ bom.name }}{% endif %} | 接单量: <b>{{ '%.2f'|format(o.qty) }}</b> | 生产工厂: <b>{% if factory %}{{ factory.code }}{% if factory.name %} · {{ factory.name }}{% endif %}{% else %}未指定(按汇总库存){% endif %}</b> | 经手人: {{ o.operator or '-' }}{% if o.created_at %} | 下单时间: {{ o.created_at.strftime('%Y-%m-%d %H:%M') }}{% endif %}</p>
{% if o.remark %}<p class="small muted">备注: {{ o.remark }}</p>{% endif %}
<p class="small muted" style="margin-bottom:0">提示:点击「编辑订单」保存后,该订单会冲销已产生的调入/领料流水并清空调入计划,自动回到「刚下单」状态(待领料、未调拨),可重新规划。</p>
</div>
<h3>领料需求与调入 (单套用量 × 接单量){% if factory %} | 按下单前库存核算{% endif %}</h3>
<p class="small muted" style="margin-top:-6px">库存口径:下方「下单前库存」为 <b>订单下单那一刻、所属工厂的库存快照</b>(按下单时间截断,已自动排除该订单自身后续的调入/领料及下单后发生的其他出入库),用于还原下单时的可供料情况。{% if not factory %}本单未指定生产工厂,按下单前全部工厂汇总库存展示。{% endif %}</p>
<form method="post" action="{{ url_for('order_alloc_save', oid=o.id) }}">
<table class="tbl">
<thead><tr>
<th>物料编号</th><th>型号</th><th>下单前库存</th>
<th>需求数量</th><th>缺口</th><th>调入数量</th><th>状态</th><th>工厂库存</th><th>可替代料</th>
</tr></thead>
<tbody>
{% for r in reqs %}
<tr class="{{ 'row-danger' if r.short else ('row-warn' if r.have < (r.m.safety_stock or 0)) }}">
<td><a href="{{ url_for('material_detail', mid=r.m.id) }}" title="进入工厂物料">{{ r.m.part_number }}</a></td>
<td>{{ r.m.manufacturer or '-' }}</td>
<td class="num">{{ '%.2f'|format(r.have) }}</td>
<td class="num">{{ '%.2f'|format(r.need) }}</td>
<td class="num">{{ '%.2f'|format(r.gap) }}</td>
<td class="num">
{% if r.posted %}
<div style="margin-bottom:3px"><span class="badge ok" title="已生成入库流水, 可直接修改后保存/确认">已调入</span></div>
<input name="alloc_{{ r.m.id }}" type="text" inputmode="decimal"
value="{{ '%.2f'|format(r.alloc) }}" style="width:96px">
{% else %}
<input name="alloc_{{ r.m.id }}" type="text" inputmode="decimal"
value="{{ '%.2f'|format(r.alloc) if r.alloc else '' }}"
placeholder="{{ '%.2f'|format(r.need) }}" style="width:96px">
{% endif %}
</td>
<td><span class="badge {{ 'danger' if r.short else 'ok' }}">{{ '缺料' if r.short else '充足' }}</span></td>
<td class="num">{{ '%.2f'|format(r.gap + r.alloc) }}</td>
<td class="small">
{% if r.subs %}
{% for s in r.subs %}
<span class="{{ 'badge ok' if s.available else 'badge danger' }}">{{ s.pn }}·{{ s.stock }}{% if s.available %}✓{% else %}✗{% endif %}</span>
{% endfor %}
{% else %}<span class="muted">—</span>{% endif %}
</td>
</tr>
{% else %}
<tr><td colspan="8" class="muted">该 BOM 没有组成物料</td></tr>
{% endfor %}
</tbody>
</table>
<div class="row-actions" style="flex-wrap:wrap; align-items:flex-end">
<label style="display:flex; flex-direction:column; gap:4px; font-size:13px">
调入来源工厂
<select name="from_factory_id" style="padding:6px 8px; min-width:160px">
{% for f in factories %}
{% if f.id != (o.factory_id or 0) %}
<option value="{{ f.id }}" {{ 'selected' if f.code=='SELF' else '' }}>{{ f.code }}({{ f.name or '—' }})</option>
{% endif %}
{% endfor %}
</select>
</label>
<button class="primary" type="submit">保存调入数量</button>
<button class="secondary" type="submit"
formaction="{{ url_for('order_alloc_post', oid=o.id) }}"
onclick="return confirm('确认将待调入数量从上方所选来源工厂调拨到生产工厂? 总库存不变, 仅厂间转移。');">确认调入(厂间调拨)</button>
</div>
<p class="small muted">「确认调入」是一次厂间调拨:从来源工厂出库减、生产工厂入库加,系统总库存不变。</p>
</form>
<div class="card" style="margin:14px 0">
<b>调入总览:</b> 已调入 <b>{{ posted_count }}</b> 项,合计 <b>{{ '%.2f'|format(posted_total) }}</b> 件;
计划调入合计 <b>{{ '%.2f'|format(planned_total) }}</b> 件。
</div>
{% if o.status != 'picked' %}
<form method="post" action="{{ url_for('order_pick', oid=o.id) }}" onsubmit="return confirm('确认按 BOM 生成领料出库? 库存不足将记为负并在看板预警。');">
<div class="row-actions"><button class="primary">确认领料出库</button></div>
</form>
{% else %}
<p class="flash ok">该订单已领料出库。</p>
{% endif %}
<script>
(function(){
// 调入数量输入框:上下方向键在「上一行/下一行」的同列输入框之间移动焦点
// (左右方向键保持默认行为:在文本内移动光标)
function allocInputs(){
return Array.prototype.slice.call(document.querySelectorAll('input[name^="alloc_"]'));
}
document.addEventListener('keydown', function(e){
if(e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
var t = e.target;
if(!t || t.tagName !== 'INPUT' || t.name.indexOf('alloc_') !== 0) return;
var inputs = allocInputs();
var idx = inputs.indexOf(t);
if(idx < 0) return;
var next;
if(e.key === 'ArrowUp'){ if(idx <= 0) return; next = inputs[idx - 1]; }
else { if(idx >= inputs.length - 1) return; next = inputs[idx + 1]; }
e.preventDefault();
next.focus();
try { var v = next.value; next.setSelectionRange(v.length, v.length); } catch(_){}
});
})();
</script>
{% endblock %}