修复归档路径错误,添加公式单元格缓存值支持

- fill 命令生成 Excel 后正确移动到归档目录而非保留在报销输出
- 新增 build_formula_cached_values 支持填充公式单元格的缓存值
- 完成后清理报销输出目录避免残留临时文件
- .gitignore 移除 *.json 使 analyzed_result.json 可被追踪
This commit is contained in:
copper-xw 2026-05-29 14:21:03 +08:00
parent 240d8534fd
commit 04e1592fe3
2 changed files with 43 additions and 7 deletions

1
.gitignore vendored
View File

@ -3,7 +3,6 @@
*.jpeg *.jpeg
*.png *.png
*.txt *.txt
*.json
*.xlsx *.xlsx
报销输出/ 报销输出/
__pycache__/ __pycache__/

View File

@ -221,10 +221,11 @@ def command_fill(args):
output_dir = json_path.parent output_dir = json_path.parent
archive_dir = output_dir.parent if output_dir.name == OUTPUT_DIR_NAME else Path(".") archive_dir = output_dir.parent if output_dir.name == OUTPUT_DIR_NAME else Path(".")
output_file = fill_template(result, output_dir) output_file = fill_template(result, archive_dir)
verify_output(output_file, result) verify_output(output_file, result)
final_archive_dir = finalize_archive_dir(archive_dir, result) final_archive_dir = finalize_archive_dir(archive_dir, result)
final_output_file = final_archive_dir / OUTPUT_DIR_NAME / output_file.name final_output_file = final_archive_dir / output_file.name
cleanup_output_dir(final_archive_dir / OUTPUT_DIR_NAME)
cleanup_pycache() cleanup_pycache()
print(f"完成:{final_output_file}") print(f"完成:{final_output_file}")
@ -314,7 +315,7 @@ def fill_template(result, output_dir):
output_file = output_dir / build_output_filename(meta) output_file = output_dir / build_output_filename(meta)
shutil.copy(TEMPLATE_FILE, output_file) shutil.copy(TEMPLATE_FILE, output_file)
update_xlsx_cells(output_file, build_cell_values(result)) update_xlsx_cells(output_file, build_cell_values(result), build_formula_cached_values(result))
return output_file return output_file
@ -327,6 +328,7 @@ def build_cell_values(result):
"B6": f'{meta["出发地"]}-{meta["目的地"]}', "B6": f'{meta["出发地"]}-{meta["目的地"]}',
"C6": f'{meta["出差天数"]}天', "C6": f'{meta["出差天数"]}天',
"D6": meta["出行方式"], "D6": meta["出行方式"],
"F6": meta["出差天数"],
"H6": expenses["住宿"]["单据张数"], "H6": expenses["住宿"]["单据张数"],
"I6": expenses["住宿"]["金额"], "I6": expenses["住宿"]["金额"],
"H7": expenses["饮食"]["单据张数"], "H7": expenses["饮食"]["单据张数"],
@ -338,7 +340,20 @@ def build_cell_values(result):
} }
def update_xlsx_cells(xlsx_path, cell_values): def build_formula_cached_values(result):
summary = result["summary"]
advance = summary["预借差旅费"]
total = summary["报销总额"]
return {
"H2": summary["附件总张数"],
"F9": result["meta"]["出差天数"],
"C10": total,
"H10": max(total - advance, 0),
"H11": max(advance - total, 0),
}
def update_xlsx_cells(xlsx_path, cell_values, formula_cached_values):
temp_file = NamedTemporaryFile(delete=False, suffix=".xlsx", dir=xlsx_path.parent) temp_file = NamedTemporaryFile(delete=False, suffix=".xlsx", dir=xlsx_path.parent)
temp_path = Path(temp_file.name) temp_path = Path(temp_file.name)
temp_file.close() temp_file.close()
@ -349,7 +364,7 @@ def update_xlsx_cells(xlsx_path, cell_values):
for item in source.infolist(): for item in source.infolist():
data = source.read(item.filename) data = source.read(item.filename)
if item.filename == "xl/worksheets/sheet1.xml": if item.filename == "xl/worksheets/sheet1.xml":
data = update_sheet_xml(data, cell_values) data = update_sheet_xml(data, cell_values, formula_cached_values)
target.writestr(item, data) target.writestr(item, data)
temp_path.replace(xlsx_path) temp_path.replace(xlsx_path)
except Exception: except Exception:
@ -357,7 +372,7 @@ def update_xlsx_cells(xlsx_path, cell_values):
raise raise
def update_sheet_xml(sheet_xml, cell_values): def update_sheet_xml(sheet_xml, cell_values, formula_cached_values):
namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main" namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
ET.register_namespace("", namespace) ET.register_namespace("", namespace)
root = ET.fromstring(sheet_xml) root = ET.fromstring(sheet_xml)
@ -366,6 +381,11 @@ def update_sheet_xml(sheet_xml, cell_values):
if cell is None: if cell is None:
raise SystemExit(f"模板缺少单元格:{cell_ref}") raise SystemExit(f"模板缺少单元格:{cell_ref}")
set_cell_xml_value(cell, value, namespace) set_cell_xml_value(cell, value, namespace)
for cell_ref, value in formula_cached_values.items():
cell = root.find(f".//{{{namespace}}}c[@r='{cell_ref}']")
if cell is None:
raise SystemExit(f"模板缺少单元格:{cell_ref}")
set_formula_cached_value(cell, value, namespace)
return ET.tostring(root, encoding="utf-8", xml_declaration=True) return ET.tostring(root, encoding="utf-8", xml_declaration=True)
@ -385,6 +405,18 @@ def set_cell_xml_value(cell, value, namespace):
text_node.text = str(value) text_node.text = str(value)
def set_formula_cached_value(cell, value, namespace):
cell.attrib.pop("t", None)
value_node = None
for child in cell:
if child.tag == f"{{{namespace}}}v":
value_node = child
break
if value_node is None:
value_node = ET.SubElement(cell, f"{{{namespace}}}v")
value_node.text = format_number_for_excel(value)
def format_number_for_excel(value): def format_number_for_excel(value):
if isinstance(value, int): if isinstance(value, int):
return str(value) return str(value)
@ -415,6 +447,11 @@ def unique_archive_dir(path):
index += 1 index += 1
def cleanup_output_dir(output_dir):
if output_dir.exists():
shutil.rmtree(output_dir)
def cleanup_pycache(): def cleanup_pycache():
pycache_dir = Path("__pycache__") pycache_dir = Path("__pycache__")
if pycache_dir.exists(): if pycache_dir.exists():