184 lines
6.0 KiB
Python
184 lines
6.0 KiB
Python
import os
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from openpyxl import load_workbook
|
|
|
|
from finance.exporting import build_formula_workbook
|
|
from tests.test_formula_export_reliability import custom_column
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_formula_workbook_recalculates_with_libreoffice(workspace, tmp_path):
|
|
executable = os.environ.get("SOFFICE_BIN") or shutil.which("soffice")
|
|
if not executable:
|
|
pytest.skip("set SOFFICE_BIN to run the LibreOffice recalculation check")
|
|
|
|
ds, member, *_ = workspace
|
|
source = tmp_path / "source"
|
|
output = tmp_path / "recalculated"
|
|
source.mkdir()
|
|
output.mkdir()
|
|
workbook_path = source / "formula.xlsx"
|
|
with workbook_path.open("wb") as workbook_file:
|
|
build_formula_workbook(
|
|
ds,
|
|
member,
|
|
{
|
|
"start": "2026-08",
|
|
"end": "2026-08",
|
|
"comparison": "yoy",
|
|
"group": "department",
|
|
"custom_columns": [custom_column()],
|
|
},
|
|
workbook_file,
|
|
)
|
|
|
|
profile = tmp_path / "lo-profile"
|
|
result = subprocess.run(
|
|
[
|
|
executable,
|
|
"--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
|
|
recalculated = output / workbook_path.name
|
|
assert recalculated.is_file(), result.stderr or result.stdout
|
|
|
|
formulas = load_workbook(recalculated, data_only=False)
|
|
values = load_workbook(recalculated, data_only=True)
|
|
summary_formulas = formulas["分析汇总"]
|
|
summary_values = values["分析汇总"]
|
|
row = next(
|
|
row
|
|
for row in range(6, summary_formulas.max_row + 1)
|
|
if summary_formulas.cell(row, 1).value == "收入成本差"
|
|
)
|
|
assert summary_formulas.cell(row, 3).value.startswith("=")
|
|
assert summary_values.cell(row, 3).value == pytest.approx(60)
|
|
assert summary_values.cell(row, 4).value == pytest.approx(90)
|
|
assert summary_values.cell(row, 5).value == pytest.approx(-30)
|
|
assert summary_values.cell(row, 6).value == pytest.approx(-1 / 3)
|
|
errors = {
|
|
cell.value
|
|
for sheet in values
|
|
for row_cells in sheet.iter_rows()
|
|
for cell in row_cells
|
|
if cell.data_type == "e"
|
|
}
|
|
assert errors <= {"#N/A"}
|
|
|
|
|
|
def _recalculate(workbook_path, output, profile, executable):
|
|
result = subprocess.run(
|
|
[
|
|
executable,
|
|
"--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
|
|
recalculated = output / Path(workbook_path).name
|
|
assert recalculated.is_file(), result.stderr or result.stdout
|
|
return load_workbook(recalculated, data_only=True)
|
|
|
|
|
|
def test_custom_column_recalculates_after_detail_amount_edit(workspace, tmp_path):
|
|
executable = os.environ.get("SOFFICE_BIN") or shutil.which("soffice")
|
|
if not executable:
|
|
pytest.skip("set SOFFICE_BIN to run the LibreOffice recalculation check")
|
|
ds, member, *_ = workspace
|
|
source, output = tmp_path / "source", tmp_path / "recalculated"
|
|
source.mkdir()
|
|
output.mkdir()
|
|
workbook_path = source / "edited-input.xlsx"
|
|
with workbook_path.open("wb") as workbook_file:
|
|
build_formula_workbook(
|
|
ds,
|
|
member,
|
|
{
|
|
"start": "2026-08",
|
|
"end": "2026-08",
|
|
"comparison": "yoy",
|
|
"group": "department",
|
|
"custom_columns": [custom_column()],
|
|
},
|
|
workbook_file,
|
|
)
|
|
edited = load_workbook(workbook_path)
|
|
edited["计算明细"]["E4"] = 140
|
|
edited.save(workbook_path)
|
|
values = _recalculate(workbook_path, output, tmp_path / "edit-profile", executable)
|
|
summary = values["分析汇总"]
|
|
row = next(
|
|
row
|
|
for row in range(6, summary.max_row + 1)
|
|
if summary.cell(row, 1).value == "收入成本差" and summary.cell(row, 2).value == "儿科"
|
|
)
|
|
assert summary.cell(row, 3).value == pytest.approx(120)
|
|
assert summary.cell(row, 5).value == pytest.approx(30)
|
|
|
|
|
|
def test_roi_zero_cost_and_missing_baseline_return_excel_na(workspace, tmp_path):
|
|
executable = os.environ.get("SOFFICE_BIN") or shutil.which("soffice")
|
|
if not executable:
|
|
pytest.skip("set SOFFICE_BIN to run the LibreOffice recalculation check")
|
|
ds, member, *_ = workspace
|
|
ds.facts.filter(period="2026-08").update(cost=0)
|
|
source, output = tmp_path / "source", tmp_path / "recalculated"
|
|
source.mkdir()
|
|
output.mkdir()
|
|
workbook_path = source / "roi-edges.xlsx"
|
|
with workbook_path.open("wb") as workbook_file:
|
|
build_formula_workbook(
|
|
ds,
|
|
member,
|
|
{
|
|
"start": "2026-08",
|
|
"end": "2026-08",
|
|
"comparison": "custom",
|
|
"baseline_end": "2024-08",
|
|
"group": "department",
|
|
},
|
|
workbook_file,
|
|
)
|
|
values = _recalculate(workbook_path, output, tmp_path / "roi-profile", executable)
|
|
summary = values["分析汇总"]
|
|
roi_row = next(
|
|
row
|
|
for row in range(6, summary.max_row + 1)
|
|
if summary.cell(row, 1).value == "ROI" and summary.cell(row, 2).value == "儿科"
|
|
)
|
|
assert summary.cell(roi_row, 3).value == "#N/A"
|
|
assert summary.cell(roi_row, 4).value == "#N/A"
|
|
errors = {
|
|
cell.value
|
|
for sheet in values
|
|
for row_cells in sheet.iter_rows()
|
|
for cell in row_cells
|
|
if cell.data_type == "e"
|
|
}
|
|
assert errors <= {"#N/A"}
|