- 多工厂(factory 表) + 顶栏工厂下拉过滤, 库存按厂聚合 - 厂间调拨(transfer): 源厂减+目标厂加, 总库存不变, 双向流水对账 - 替代料关系(substitute): 出库/BOM领料/生产领料缺料时提示可替代料 - 流水台账(ledger): 入出按日期合并折叠, 按日期导出同一 Excel - 生产订单增强: 编辑/删除(级联清关联流水)/调入数量/确认调入=厂间调拨 - 入库新增送货日期字段; 出库物料编码扫码定位 - 重写 README 补全全部模块; .gitignore 排除 dist_pkg/build_pkg/*.db - 停止跟踪 instance/app.db(运行时自动生成)
78 lines
2.9 KiB
HTML
78 lines
2.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}新建厂间调拨{% endblock %}
|
|
{% block content %}
|
|
|
|
<div class="toolbar">
|
|
<h3 style="margin:0">➡️ 新建厂间调拨</h3>
|
|
<a class="btn" href="{{ url_for('transfers') }}">← 返回调拨记录</a>
|
|
</div>
|
|
|
|
<div class="card form-card" style="max-width:560px">
|
|
<p class="small muted">从「调出工厂」扣减库存,同时向「调入工厂」增加库存,两笔流水自动生成、可追溯对账。系统会校验调出厂库存是否充足。</p>
|
|
<form method="post" id="tfForm">
|
|
<label>调出工厂(源厂)*</label>
|
|
<select name="from_factory_id" id="fromFac" onchange="refreshPreview()" required>
|
|
<option value="">-- 选择调出工厂 --</option>
|
|
{% for f in factories %}
|
|
<option value="{{ f.id }}" {% if current_fid == f.id %}selected{% endif %}>{{ f.code }} · {{ f.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label>调入工厂(目标厂)*</label>
|
|
<select name="to_factory_id" id="toFac" required>
|
|
<option value="">-- 选择调入工厂 --</option>
|
|
{% for f in factories %}
|
|
<option value="{{ f.id }}">{{ f.code }} · {{ f.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label>物料 *</label>
|
|
<select name="material_id" id="matSel" onchange="refreshPreview()" required>
|
|
<option value="">-- 选择物料 --</option>
|
|
{% for m in materials %}
|
|
<option value="{{ m.id }}">{{ m.part_number }} ({{ m.manufacturer or '-' }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label>数量 *</label>
|
|
<input name="qty" id="qty" type="number" step="0.01" min="0.01" oninput="refreshPreview()" required>
|
|
|
|
<label>经手人</label>
|
|
<input name="operator">
|
|
|
|
<label>业务日期</label>
|
|
<input name="occur_date" type="date" value="{{ today }}">
|
|
|
|
<label>备注</label>
|
|
<input name="remark">
|
|
|
|
<div id="preview" style="margin:10px 0"></div>
|
|
|
|
<div class="row-actions">
|
|
<button class="primary" type="submit">确认调拨</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
function refreshPreview(){
|
|
const mat = document.getElementById('matSel');
|
|
const from = document.getElementById('fromFac');
|
|
const qty = document.getElementById('qty');
|
|
const box = document.getElementById('preview');
|
|
if(!mat.value || !from.value || !qty.value || parseFloat(qty.value) <= 0){
|
|
box.innerHTML = ''; return;
|
|
}
|
|
const u = `/api/transfer/preview?material_id=${mat.value}&from_factory_id=${from.value}&qty=${encodeURIComponent(qty.value)}`;
|
|
fetch(u).then(r=>r.json()).then(d=>{
|
|
const stock = d.stock;
|
|
if(d.short){
|
|
box.innerHTML = `<div class="flash error">⚠ 源厂库存不足: 现有 <b>${stock}</b>, 需调拨 <b>${parseFloat(qty.value)}</b>, 提交将被拦截</div>`;
|
|
} else {
|
|
box.innerHTML = `<div class="flash ok">源厂当前库存: <b>${stock}</b>, 调拨后剩余 <b>${(stock - parseFloat(qty.value)).toFixed(2)}</b></div>`;
|
|
}
|
|
}).catch(()=>{ box.innerHTML=''; });
|
|
}
|
|
</script>
|
|
{% endblock %}
|