fix(requisition): 模板三级解析,空白模板入库,产物只写会话 artifacts
历史清洗删掉了根目录两份 低值易耗品申购*.xls——它们其实是**已填写的真实申购单** (半导体型号、单价、项目名都在),不是模板;同时旧实现「项目根 glob 取名字最大者」会把 上一次生成的产物当模板,所以一并修掉: - 新增 resolve_requisition_template():REQUISITION_TEMPLATE 环境变量 → templates/ → 项目根(兼容旧位置); 环境变量指向不存在的文件时告警并继续回退,不静默失败 - 入库 templates/低值易耗品申购.xls 空白件(由旧文件删掉第 3 行起的全部内容得到,UsedRange 只剩 2 行: 标题 + 表头;真实采购明细不进仓库) - generate_requisition 默认输出改为 <会话>/artifacts/低值易耗品申购<ts>.xls,不再写模板旁边 - mcp/tools/documents.py 的 _find_template 改为复用同一解析函数,避免两处规则漂移 - 模块 docstring 与 CLI help 里 src/web/uploads 的过时示例改为 cases/ - tests:5 个纯单测覆盖解析优先级(env 优先 / templates 胜过根目录 / 根目录取最新 / env 指向不存在的文件时回退 / 全都没有时返回 None),不依赖 COM 验证:真实 Excel COM 端到端 2 项 → 产物 UsedRange 5 行(标题/表头/2 明细/合计),第 6 行起为空, 无旧明细残留;fill_requisition_excel 原本就会先删第 3 行起内容,这条也实测确认过。 门禁:ruff / format --check / mypy src / deptry 全绿,pytest 112 passed,mcp_smoke 17 工具。
This commit is contained in:
@@ -12,6 +12,7 @@ from src.infra.documents.requisition import (
|
||||
guess_spec,
|
||||
load_project_desc,
|
||||
load_requisition_items,
|
||||
resolve_requisition_template,
|
||||
)
|
||||
|
||||
|
||||
@@ -175,3 +176,38 @@ class TestLoadProjectDesc:
|
||||
def test_falls_back_on_corrupt_file(self, tmp_path: Path) -> None:
|
||||
(tmp_path / "agent_state.json").write_text("not json", encoding="utf-8")
|
||||
assert load_project_desc(tmp_path) == "电子元器件及工具耗材采购"
|
||||
|
||||
|
||||
class TestResolveRequisitionTemplate:
|
||||
"""模板解析不涉及 COM,可以纯单测覆盖优先级。"""
|
||||
|
||||
def test_env_var_wins(self, tmp_path: Path, monkeypatch) -> None:
|
||||
custom = tmp_path / "自定义模板.xls"
|
||||
custom.write_bytes(b"x")
|
||||
monkeypatch.setenv("REQUISITION_TEMPLATE", str(custom))
|
||||
assert resolve_requisition_template(tmp_path / "proj") == custom
|
||||
|
||||
def test_templates_dir_beats_root(self, tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.delenv("REQUISITION_TEMPLATE", raising=False)
|
||||
(tmp_path / "templates").mkdir()
|
||||
blank = tmp_path / "templates" / "低值易耗品申购.xls"
|
||||
blank.write_bytes(b"a")
|
||||
(tmp_path / "低值易耗品申购999.xls").write_bytes(b"b")
|
||||
assert resolve_requisition_template(tmp_path) == blank
|
||||
|
||||
def test_root_fallback_takes_latest_name(self, tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.delenv("REQUISITION_TEMPLATE", raising=False)
|
||||
(tmp_path / "低值易耗品申购111.xls").write_bytes(b"a")
|
||||
newest = tmp_path / "低值易耗品申购222.xls"
|
||||
newest.write_bytes(b"b")
|
||||
assert resolve_requisition_template(tmp_path) == newest
|
||||
|
||||
def test_env_pointing_nowhere_falls_back(self, tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.setenv("REQUISITION_TEMPLATE", str(tmp_path / "不存在.xls"))
|
||||
legacy = tmp_path / "低值易耗品申购.xls"
|
||||
legacy.write_bytes(b"b")
|
||||
assert resolve_requisition_template(tmp_path) == legacy
|
||||
|
||||
def test_missing_everywhere_returns_none(self, tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.delenv("REQUISITION_TEMPLATE", raising=False)
|
||||
assert resolve_requisition_template(tmp_path) is None
|
||||
|
||||
Reference in New Issue
Block a user