完善采购审批流程、企业微信通知和采购专用章

This commit is contained in:
reddaniel110
2026-09-16 11:45:21 +08:00
parent 4876d8805d
commit bf952196fa
58 changed files with 3451 additions and 1909 deletions
+3 -154
View File
@@ -1,156 +1,5 @@
# -*- coding: utf-8 -*-
"""模拟完整流程:停用/启用/删除 + 主管审核加章,并导出带章 PDF 渲染成 PNG。
用法:先启动服务(python app.py 8099),再 python test_stamp_toggle.py 8099
"""
import os
import re
import sqlite3
import sys
import requests
PORT = sys.argv[1] if len(sys.argv) > 1 else "8099"
BASE = "http://127.0.0.1:%s" % PORT
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "out")
os.makedirs(OUT, exist_ok=True)
DB = os.path.join(os.path.dirname(HERE), "po_app", "instance", "po.db")
FAILS = []
def check(label, cond, extra=""):
print((" OK " if cond else " FAIL ") + label + (" " + str(extra) if extra else ""))
if not cond:
FAILS.append(label)
def new_sess(u, p):
s = requests.Session()
r = s.post(BASE + "/login", data={"username": u, "password": p})
assert r.status_code == 200, r.status_code
return s
def flash_of(html):
return " | ".join(re.sub(r"<[^>]+>", "", m.group(1)).strip()
for m in re.finditer(r'class="flash[^"]*"[^>]*>(.*?)</div>',
html, re.S))
def row_html(html, key):
"""取出包含指定关键字的那个 <tr>...</tr>。"""
for m in re.finditer(r"<tr[^>]*>(.*?)</tr>", html, re.S):
if key in m.group(1):
return m.group(0)
return ""
def dbq(sql, args=()):
con = sqlite3.connect(DB)
cur = con.cursor()
cur.execute(sql, args)
r = cur.fetchone()
con.close()
return r
def main():
admin = new_sess("admin", "admin123")
print("\n[1] 供应商:停用 → 整行置灰 → 启用 → 彻底删除")
admin.post(BASE + "/suppliers",
data={"code": "S-T1", "name": u"测试供应商T1", "contact": u"测试联系人"})
sid = dbq("SELECT id FROM suppliers WHERE code='S-T1'")[0]
h = admin.get(BASE + "/suppliers").text
check("S-T1 已创建", "S-T1" in h)
check("初始为启用态(未置灰)", "row-off" not in row_html(h, "S-T1"))
admin.post(BASE + "/suppliers/%d/toggle" % sid)
h = admin.get(BASE + "/suppliers").text
check("停用后整行置灰", "row-off" in row_html(h, "S-T1"))
check("停用后按钮变为『启用』", u"启用" in row_html(h, "S-T1"))
check("停用后仍可编辑(保留入口)", "openEdit" in row_html(h, "S-T1"))
admin.post(BASE + "/suppliers/%d/toggle" % sid)
h = admin.get(BASE + "/suppliers").text
check("启用后恢复(不再置灰)", "row-off" not in row_html(h, "S-T1"))
admin.post(BASE + "/suppliers/%d/delete" % sid)
h = admin.get(BASE + "/suppliers").text
check("删除后从列表消失", "S-T1" not in h)
print("\n[2] 物料:停用 → 整行置灰 → 启用 → 彻底删除")
admin.post(BASE + "/materials",
data={"code": "M-T1", "spec": u"测试物料T1", "unit": u"件", "price": "9.9"})
mid = dbq("SELECT id FROM materials WHERE code='M-T1'")[0]
h = admin.get(BASE + "/materials").text
check("M-T1 已创建", "M-T1" in h)
check("初始为启用态(未置灰)", "row-off" not in row_html(h, "M-T1"))
admin.post(BASE + "/materials/%d/toggle" % mid)
h = admin.get(BASE + "/materials").text
check("停用后整行置灰", "row-off" in row_html(h, "M-T1"))
admin.post(BASE + "/materials/%d/toggle" % mid)
h = admin.get(BASE + "/materials").text
check("启用后恢复", "row-off" not in row_html(h, "M-T1"))
admin.post(BASE + "/materials/%d/delete" % mid)
h = admin.get(BASE + "/materials").text
check("删除后从列表消失", "M-T1" not in h)
print("\n[3] 主管审核:确认前必须先加章")
r = admin.post(BASE + "/orders/new", data={"doc_type": "purchase"})
m = re.search(r"/orders/(\d+)/edit", r.url)
oid = int(m.group(1))
admin.post(BASE + "/orders/%d" % oid, data={
"supplier_name": u"测试供应商",
"item_code": ["M-1001"], "item_spec": [u"测试物料"],
"item_unit": [u"件"], "item_qty": ["10"], "item_price": ["12.5"],
"item_date": [""], "item_remark": [""],
})
admin.post(BASE + "/orders/%d/submit" % oid, data={"comment": u"模拟提交"})
st = dbq("SELECT status FROM orders WHERE id=?", (oid,))[0]
check("订单已提交,状态待审核", st == "pending_review", st)
rv = new_sess("reviewer", "123456")
r = rv.post(BASE + "/orders/%d/review" % oid,
data={"comment": u"直接点确认试试", "stamped": "0"})
check("未加章 → 被拒绝并提示", u"请先加盖审核章" in flash_of(r.text),
flash_of(r.text))
st = dbq("SELECT status FROM orders WHERE id=?", (oid,))[0]
check("未加章时状态不变", st == "pending_review", st)
r = rv.post(BASE + "/orders/%d/review" % oid,
data={"comment": u"已核对无误", "stamped": "1"})
row = dbq("SELECT status, reviewed_by FROM orders WHERE id=?", (oid,))
check("加章后审核通过", row[0] == "pending_approval", row)
check("记录了审核人", bool(row[1]), row[1])
print("\n[4] 导出带章 PDF 并渲染预览")
pdf_bytes = admin.get(BASE + "/orders/%d/pdf" % oid).content
pdf_path = os.path.join(OUT, "seal_review.pdf")
with open(pdf_path, "wb") as f:
f.write(pdf_bytes)
check("PDF 已生成", len(pdf_bytes) > 10000, "%d bytes" % len(pdf_bytes))
try:
import fitz
doc = fitz.open(pdf_path)
pix = doc[-1].get_pixmap(dpi=130)
png_path = os.path.join(OUT, "seal_review.png")
pix.save(png_path)
print(" 渲染预览:", png_path, "共 %d 页" % doc.page_count)
doc.close()
except Exception as e:
print(" 渲染预览失败:", e)
print("\n[5] 清理测试订单")
con = sqlite3.connect(DB)
cur = con.cursor()
cur.execute("DELETE FROM order_items WHERE order_id=?", (oid,))
cur.execute("DELETE FROM orders WHERE id=?", (oid,))
con.commit()
con.close()
print(" 已删除订单 %d" % oid)
print("\n" + ("模拟全部通过" if not FAILS else "失败: " + "; ".join(FAILS)))
"""Compatibility entry point; tests only use temporary databases."""
from run_tests import main
if __name__ == "__main__":
main()
raise SystemExit(main(('-k', 'full_flow')))