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

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
+37 -75
View File
@@ -1,82 +1,44 @@
# -*- coding: utf-8 -*-
"""打包后 EXE 冒烟测试。
确认这些确实进了包:批量导入入口与模板、列表编辑入口、弹窗样式,
以及「新建订单明细为空时仍能添加行」所依赖的 rowTpl 模板。
用法:python verify_exe.py 8080 [数据库路径]
不传数据库路径时默认 po_app/dist/instance/po.db
"""
import os
"""Read-only page/asset checks for a running packaged app; never writes order data."""
import argparse
import getpass
import http.cookiejar
import re
import sqlite3
import sys
import requests
BASE = "http://127.0.0.1:%s" % (sys.argv[1] if len(sys.argv) > 1 else "8080")
HERE = os.path.dirname(os.path.abspath(__file__))
DB = sys.argv[2] if len(sys.argv) > 2 else os.path.join(
os.path.dirname(HERE), "po_app", "dist", "instance", "po.db")
s = requests.Session()
FAILS = []
def check(label, cond, extra=""):
print((" OK " if cond else " FAIL ") + label + (" " + str(extra) if extra else ""))
if not cond:
FAILS.append(label)
import urllib.parse
import urllib.request
def main():
r = s.post(BASE + "/login", data={"username": "admin", "password": "admin123"})
check("登录", r.status_code == 200, r.status_code)
# 注意:url_for() 会被 Jinja 渲染成实际路径,页面里不会出现函数名
for path, marks in (
("/materials", [u"批量导入 Excel", "matImpModal", "/materials/import_excel",
"/materials/import_template.xlsx", "openEdit", "editModal",
"/toggle", "/delete", u"停用", u"删除"]),
("/suppliers", [u"批量导入 Excel", "supImpModal", "/suppliers/import_excel",
"/suppliers/import_template.xlsx", "openEdit", "editModal",
"/toggle", "/delete", u"停用", u"删除"]),
):
h = s.get(BASE + path).text
missing = [m for m in marks if m not in h]
check("%s 含导入/编辑/停用删除/置灰" % path, not missing, missing)
css = s.get(BASE + "/static/style.css").text
check("style.css 含弹窗样式", "modal-mask" in css and "modal-box" in css)
for u in ("/materials/import_template.xlsx", "/suppliers/import_template.xlsx"):
r = s.get(BASE + u)
check("模板下载 %s" % u, r.status_code == 200 and len(r.content) > 3000,
"%s bytes" % len(r.content))
# 新建订单:明细为空时必须仍能添加行(依赖 rowTpl 模板存在)
r = s.post(BASE + "/orders/new", data={"doc_type": "purchase"})
h = r.text
no = ""
m = re.search(r"PO\d{12}", h)
if m:
no = m.group(0)
check("新建订单页含 rowTpl 明细行模板", 'id="rowTpl"' in h)
tpl = re.search(r'<template id="rowTpl">(.*?)</template>', h, re.S)
check("rowTpl 内含完整空行", bool(tpl) and 'name="item_code"' in tpl.group(1))
if no and os.path.exists(DB):
con = sqlite3.connect(DB)
cur = con.cursor()
cur.execute("DELETE FROM order_items WHERE order_id IN "
"(SELECT id FROM orders WHERE order_no=?)", (no,))
cur.execute("DELETE FROM orders WHERE order_no=?", (no,))
con.commit()
con.close()
print(" 已清理测试订单 %s" % no)
print("\n" + ("打包版冒烟全部通过" if not FAILS else "失败: " + "; ".join(FAILS)))
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--url',default='http://127.0.0.1:8080')
parser.add_argument('--username',required=True,help='Use an initialized admin account')
args=parser.parse_args()
base=args.url.rstrip('/')
opener=urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
def get(path):
with opener.open(base+path,timeout=30) as response:
return response.geturl(),response.read()
_,body=get('/login')
match=re.search(r'name="csrf_token" value="([^"]+)"',body.decode('utf-8'))
if not match:
raise SystemExit('Complete first-time setup before running this check.')
fields={'username':args.username,'password':getpass.getpass('Password: '),'csrf_token':match[1]}
with opener.open(urllib.request.Request(base+'/login',data=urllib.parse.urlencode(fields).encode()),timeout=30) as response:
if response.geturl().rstrip('/') != base:
raise SystemExit('Login failed or password change is required.')
response.read()
for path,marker in [('/orders/new',b'request_key'),('/users',b'username'),('/materials',b'matImpModal'),
('/suppliers',b'supImpModal'),('/static/money.js',b'lineCents'),('/static/style.css',b'modal-mask')]:
url,body=get(path)
if url != base+path or marker not in body:
raise SystemExit('Failed page/asset check: '+path)
print('PASS',path)
for path in ['/materials/import_template.xlsx','/suppliers/import_template.xlsx','/import_template.xlsx']:
url,body=get(path)
if url != base+path or not body.startswith(b'PK'):
raise SystemExit('Failed workbook check: '+path)
print('PASS',path)
print('Packaged page/asset checks passed; order data was not modified.')
if __name__ == "__main__":
if __name__=='__main__':
main()