feat: 生产订单每个 BOM 生产数量限制为 1–9 的个位正整数

- 前端: 数量输入框改为 text/inputmode=numeric/maxlength=1,新增 capQty 实时拦截非个位(去非数字、限1位、超范围清空)
- 后端: 新增 _bom_qty_invalid 校验,order_new/order_edit 拒绝非个位数量并回显已填内容,提示「每个 BOM 的生产数量需为 1–9 的个位正整数」

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
This commit is contained in:
2026-10-26 10:51:55 +08:00
co-authored by WorkBuddy
parent 93f3ef458b
commit 38ee24f6ee
2 changed files with 29 additions and 2 deletions
+20
View File
@@ -1499,6 +1499,10 @@ def order_new():
factory_id = int(fid_raw) if fid_raw.isdigit() else None
if not pairs:
flash("请至少选择一个 BOM 并填写大于 0 的生产数量", "error")
elif _bom_qty_invalid(pairs):
flash("每个 BOM 的生产数量需为 1–9 的个位正整数", "error")
return render_template("order_form.html", boms=boms_all, factories=factories,
o=None, initial_lines=[[bid, qv] for bid, qv in pairs])
else:
order_no = request.form.get("order_no", "").strip() or _gen_order_no()
if db.session.scalar(db.select(ProdOrder).where(ProdOrder.order_no == order_no)):
@@ -1646,6 +1650,10 @@ def order_edit(oid):
flash("请至少选择一个 BOM 并填写大于 0 的生产数量", "error")
return render_template("order_form.html", boms=boms_all, factories=factories,
o=o, initial_lines=[[b.id, q] for b, q in order_bom_lines(o)])
if _bom_qty_invalid(pairs):
flash("每个 BOM 的生产数量需为 1–9 的个位正整数", "error")
return render_template("order_form.html", boms=boms_all, factories=factories,
o=o, initial_lines=[[bid, qv] for bid, qv in pairs])
old_no = o.order_no
order_no = request.form.get("order_no", "").strip() or o.order_no
if order_no != o.order_no and db.session.scalar(
@@ -1779,6 +1787,18 @@ def _parse_bom_pairs(form):
return pairs
def _bom_qty_invalid(pairs):
"""返回 pairs 中不符合『1–9 个位正整数』的 qty 列表; 空列表表示全部合法。
生产订单每个 BOM 的生产数量限定为个位正整数(1~9), 不允许 0、小数或超过个位。
"""
bad = []
for _, q in pairs:
if not (1 <= q <= 9 and float(q).is_integer()):
bad.append(q)
return bad
def _find_posted_flows(order_no, material_id):
"""返回某生产订单某物料已入账调入流水(源厂出库 + 生产厂入库)。"""
outs = db.session.scalars(db.select(Outbound).where(
+9 -2
View File
@@ -9,7 +9,7 @@
<input name="order_no" value="{{ o.order_no if o else '' }}" placeholder="如 WO20260911xxx">
<label>BOM 与生产数量 *</label>
<p class="small muted" style="margin:0 0 6px">可在同一订单中加入多个 BOM,每个 BOM 独立填写生产数量;点击「+ 添加 BOM」增加一行,不需要的行可删除。</p>
<p class="small muted" style="margin:0 0 6px">可在同一订单中加入多个 BOM,每个 BOM 独立填写生产数量(限 <b>1–9 的个位正整数</b>);点击「+ 添加 BOM」增加一行,不需要的行可删除。</p>
<div id="bomRows" class="bom-rows"></div>
<button type="button" class="btn" style="margin:2px 0 12px" onclick="addBomRow()">+ 添加 BOM</button>
@@ -36,12 +36,19 @@
<option value="">-- 选择 BOM --</option>
{% for b in boms %}<option value="{{ b.id }}">{{ b.code }}{% if b.name %} · {{ b.name }}{% endif %}</option>{% endfor %}
</select>
<input name="qty[]" type="number" step="0.01" min="0.01" required placeholder="生产数量" style="width:130px">
<input name="qty[]" type="text" inputmode="numeric" maxlength="1" required placeholder="数量 1-9" style="width:110px" oninput="capQty(this)">
<button type="button" class="link-danger" onclick="this.closest('.bom-row').remove();checkRows()">删除</button>
</div>
</template>
<script>
function capQty(el){
// 仅允许 1–9 的个位正整数: 去非数字、限制 1 位、超出范围清空
var v = (el.value || '').replace(/[^0-9]/g, '');
if (v.length > 1) v = v.slice(0, 1);
if (v !== '' && (parseInt(v, 10) < 1 || parseInt(v, 10) > 9)) v = '';
el.value = v;
}
function addBomRow(sel, qty){
var tpl = document.getElementById('bomTpl');
var row = tpl.content.firstElementChild.cloneNode(true);