Files
fa-agent/tests/test_formula_workbook_docs.py
T
Jayway Liu 4c84b18648
Verify / verify (pull_request) Failing after 1s
AI Review / review (pull_request) Successful in 10s
Verify / verify (push) Failing after 34s
test(workbook): fix margin-rate recalculation fixture and row selection
Clear raw gross values for the current-period lookup rows, edit both matching rows instead of a baseline row, and assert recalculated detail and unchanged baseline totals.
2026-11-01 09:54:20 +08:00

175 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from decimal import Decimal
from io import BytesIO
import pytest
from openpyxl import load_workbook
from finance.exporting import build_formula_workbook
pytestmark = pytest.mark.django_db
EDITABLE_INPUT_HEADERS = ["Revenue", "Cost", "GrossInput", "MarginRate", "ListAmount"]
def _workbook(workspace, **extra):
ds, member, *_ = workspace
output = BytesIO()
build_formula_workbook(
ds,
member,
{"start": "2026-08", "end": "2026-08", "comparison": "yoy", "group": "department", **extra},
output,
)
return load_workbook(BytesIO(output.getvalue()), data_only=False)
def test_parameter_sheet_is_read_only_snapshot(workspace):
workbook = _workbook(workspace)
rows = list(workbook["参数与口径"].iter_rows(values_only=True))
header = list(rows[0])
editable = header.index("可编辑")
assert all(row[editable] == "否" for row in rows[1:] if row[0])
margin = next(row for row in rows if row[0] and "产品毛利率" in str(row[0]))
assert "只读" in margin[2]
assert "MarginRate" in margin[2]
assert "计算明细" in margin[2]
# 维度键与显示名完整对照
texts = [str(row[0]) for row in rows if row[0]]
assert "维度 department" in texts and "维度 product" in texts
department = next(row for row in rows if row[0] == "维度 department")
assert department[1] == "科室"
product = next(row for row in rows if row[0] == "维度 product")
assert product[1] == "产品一级分类"
def test_instructions_document_editable_inputs_with_real_dependencies(workspace):
workbook = _workbook(workspace)
instructions = {
str(row[0]): str(row[1]) for row in workbook["使用说明"].iter_rows(values_only=True) if row[0]
}
data_headers = [cell.value for cell in workbook["计算明细"][1]]
sources = {
"Revenue": "实际收入",
"Cost": "成本",
"GrossInput": "毛利贡献",
"MarginRate": "产品毛利率表",
"ListAmount": "应收金额",
}
for header in EDITABLE_INPUT_HEADERS:
key = next(k for k in instructions if k.startswith(f"输入 {header}("))
text = instructions[key]
assert f"计算明细页 {header} 列" in text
assert sources[header] in text
assert header in data_headers
# 每个可修改输入都被汇总公式真实引用
summary_formula_cells = [
cell.value
for row in workbook["分析汇总"].iter_rows(min_row=6)
for cell in row
if isinstance(cell.value, str) and cell.value.startswith("=IF")
]
joined = "\n".join(summary_formula_cells)
for header in ("Revenue", "Cost", "ListAmount"):
assert f"Data[{header}]" in joined
detail_formula = next(
cell.value
for row in workbook["计算明细"].iter_rows(min_row=2)
for cell in row
if isinstance(cell.value, str) and cell.value.startswith("=IF(")
)
assert "GrossInput" not in detail_formula # 列名按相对引用参与 GrossCalc
assert detail_formula.startswith("=IF(")
boundary = instructions["固定结构"]
assert "人数去重" in boundary and "固定快照" in boundary
assert "重新导出" in boundary
assert "参数与口径" in instructions["维度列"]
def test_margin_rate_lookup_edit_changes_gross_via_detail_recalculation(workspace, tmp_path):
import os
import shutil
if not (os.environ.get("SOFFICE_BIN") or shutil.which("soffice")):
pytest.skip("set SOFFICE_BIN to run the LibreOffice recalculation check")
ds, member, *_ = workspace
# 模拟缺失原始直接毛利的导入行,数据库毛利按原毛利率计算。
gross_index = next(col["index"] for col in ds.mapping["columns"] if col["role"] == "gross")
for fact in ds.facts.filter(
period="2026-08", dimensions__department="儿科", dimensions__product="医疗服务"
):
fact.raw[gross_index] = ""
fact.gross = fact.revenue * Decimal("0.3")
fact.save(update_fields=["raw", "gross"])
mapping = {**ds.mapping, "margin_rates": {"医疗服务": "0.3"}}
ds.mapping = mapping
ds.save(update_fields=["mapping"])
source, output = tmp_path / "source", tmp_path / "out"
source.mkdir()
output.mkdir()
workbook_path = source / "lookup.xlsx"
with workbook_path.open("wb") as file:
build_formula_workbook(
ds,
member,
{"start": "2026-08", "end": "2026-08", "comparison": "yoy", "group": "department"},
file,
)
editable = load_workbook(workbook_path)
details = editable["计算明细"]
columns = {cell.value: cell.column for cell in details[1]}
lookup_rows = [
row
for row in range(2, details.max_row + 1)
if details.cell(row, columns["Period"]).value == "2026-08"
and details.cell(row, columns["department"]).value == "儿科"
and details.cell(row, columns["product"]).value == "医疗服务"
]
assert len(lookup_rows) == 2
for row in lookup_rows:
assert details.cell(row, columns["GrossInput"]).value is None
assert details.cell(row, columns["GrossSource"]).value == "lookup"
assert details.cell(row, columns["MarginRate"]).value == pytest.approx(0.3)
details.cell(row, columns["MarginRate"]).value = 0.5
editable.save(workbook_path)
import subprocess
profile = tmp_path / "profile"
result = subprocess.run(
[
os.environ.get("SOFFICE_BIN") or shutil.which("soffice"),
"--headless",
f"-env:UserInstallation={profile.as_uri()}",
"--convert-to",
"xlsx",
"--outdir",
str(output),
str(workbook_path),
],
capture_output=True,
text=True,
timeout=120,
check=False,
)
assert result.returncode == 0, result.stderr or result.stdout
values = load_workbook(output / workbook_path.name, data_only=True)
details = values["计算明细"]
assert [details.cell(row, columns["GrossCalc"]).value for row in lookup_rows] == pytest.approx([40, 10])
summary = values["分析汇总"]
gross_row = next(
row
for row in range(6, summary.max_row + 1)
if summary.cell(row, 1).value == "毛利贡献" and summary.cell(row, 2).value == "儿科"
)
# 2026-08 儿科收入 100,lookup 毛利率改为 0.5 后毛利应为 50
assert summary.cell(gross_row, 3).value == pytest.approx(50)
assert summary.cell(gross_row, 4).value == pytest.approx(90)