feat: 生产订单新增导出 Excel 与打印功能

- 订单列表页「导出 Excel」(/orders/export): 导出全部生产订单(订单号/BOM/接单量/工厂/状态/经手人/下单时间/备注)

- 订单详情页「导出 Excel」(/order/<id>/export): 导出派工单, 含「订单信息」+「领料明细」双 sheet

- 订单详情页「打印」按钮: window.print() + @media print 样式(隐藏导航/按钮, 输入框转纯文本, 隐藏提示), 输出干净派工单
This commit is contained in:
2026-10-22 14:02:04 +08:00
parent 5a6f4ea4df
commit 099496dec9
4 changed files with 171 additions and 2 deletions
+93
View File
@@ -327,6 +327,99 @@ def export_ledger(target, rows):
wb.save(target)
def export_orders(target, data):
"""导出生产订单列表。
data: [{o, bom, factory}] (与 orders() 返回一致)。
"""
wb = Workbook()
ws = wb.active
ws.title = "生产订单"
head = ["订单号", "BOM型号", "BOM名称", "接单量", "生产工厂", "状态",
"经手人", "下单时间", "备注"]
ws.append(head)
for c in ws[1]:
c.font = Font(bold=True)
c.fill = PatternFill("solid", fgColor="DDEBF7")
for d in data:
o = d["o"]
bom = d["bom"]
fac = d["factory"]
status = "已领料" if o.status == "picked" else "待领料"
ws.append([
o.order_no,
bom.code if bom else "",
bom.name if bom else "",
o.qty,
(f"{fac.code}·{fac.name}" if fac else "未指定"),
status,
o.operator or "",
o.created_at.strftime("%Y-%m-%d %H:%M") if o.created_at else "",
o.remark or "",
])
if o.status == "picked":
ws.cell(row=ws.max_row, column=6).fill = PatternFill("solid", fgColor="C6EFCE")
else:
ws.cell(row=ws.max_row, column=6).fill = PatternFill("solid", fgColor="FFE699")
ws.freeze_panes = "A2"
wb.save(target)
def export_order_detail(target, o, bom, reqs, factory,
posted_total, planned_total, posted_count):
"""导出单个生产订单详情(派工单):订单信息 sheet + 领料明细 sheet。"""
wb = Workbook()
ws = wb.active
ws.title = "订单信息"
ws.append(["生产订单派工单"])
ws["A1"].font = Font(bold=True, size=14)
info = [
("订单号", o.order_no),
("BOM型号", (bom.code if bom else "-") + (f" · {bom.name}" if bom and bom.name else "")),
("接单量", o.qty),
("生产工厂", (f"{factory.code}·{factory.name}" if factory else "未指定(按汇总库存)")),
("经手人", o.operator or "-"),
("下单时间", o.created_at.strftime("%Y-%m-%d %H:%M") if o.created_at else ""),
("状态", "已领料" if o.status == "picked" else "待领料"),
("备注", o.remark or "-"),
("调入总览", f"已调入 {posted_count} 项 / 合计 {posted_total} 件;计划调入合计 {planned_total} 件"),
]
r = 3
for k, v in info:
ws.cell(row=r, column=1, value=k).font = Font(bold=True)
ws.cell(row=r, column=2, value=v)
r += 1
ws2 = wb.create_sheet("领料明细")
head = ["物料编号", "型号", "下单前库存", "需求数量", "缺口",
"调入数量", "状态", "工厂库存", "可替代料"]
ws2.append(head)
for c in ws2[1]:
c.font = Font(bold=True)
c.fill = PatternFill("solid", fgColor="DDEBF7")
for x in reqs:
subs = "、".join(
f"{s['pn']}·{s['stock']}{'✓' if s['available'] else '✗'}" for s in x["subs"]
) if x["subs"] else "—"
ws2.append([
x["m"].part_number,
x["m"].manufacturer or "-",
round(x["have"], 3),
round(x["need"], 3),
round(x["gap"], 3),
round(x["alloc"], 3),
"缺料" if x["short"] else "充足",
round(x["gap"] + x["alloc"], 3),
subs,
])
if x["short"]:
ws2.cell(row=ws2.max_row, column=7).fill = PatternFill("solid", fgColor="FFC7CE")
else:
ws2.cell(row=ws2.max_row, column=7).fill = PatternFill("solid", fgColor="C6EFCE")
ws2.freeze_panes = "A2"
wb.save(target)
def build_bom_template(path):
"""生成与真实 BOM 模板一致的格式(Altium/嘉立创导出):
每个 worksheet 一个 BOM, sheet 名即 BOM 型号; 列头 Item/Part Number/