feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
99
server/tests/test_finance_report_task.py
Normal file
99
server/tests/test_finance_report_task.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.db.base import Base
|
||||
from app.models.agent_run import AgentRun
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.models.risk_observation import RiskObservation
|
||||
from app.services.digital_employee_finance_report_task import (
|
||||
FINANCE_REPORT_TASK_TYPE,
|
||||
DigitalEmployeeFinanceReportTaskService,
|
||||
)
|
||||
|
||||
|
||||
def build_session() -> Session:
|
||||
engine = create_engine(
|
||||
"sqlite+pysqlite:///:memory:",
|
||||
connect_args={"check_same_thread": False},
|
||||
poolclass=StaticPool,
|
||||
)
|
||||
Base.metadata.create_all(bind=engine)
|
||||
session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
return session_factory()
|
||||
|
||||
|
||||
def test_finance_report_task_generates_pdf_and_agent_record(monkeypatch, tmp_path) -> None:
|
||||
monkeypatch.setenv("STORAGE_ROOT_DIR", str(tmp_path))
|
||||
get_settings.cache_clear()
|
||||
now = datetime.now(UTC)
|
||||
|
||||
with build_session() as db:
|
||||
db.add(
|
||||
ExpenseClaim(
|
||||
claim_no="RE-REPORT-001",
|
||||
employee_name="林嘉宁",
|
||||
department_name="市场部",
|
||||
expense_type="travel",
|
||||
reason="客户拜访",
|
||||
location="上海",
|
||||
amount=Decimal("3600.00"),
|
||||
invoice_count=2,
|
||||
occurred_at=now - timedelta(days=2),
|
||||
submitted_at=now - timedelta(days=2),
|
||||
status="paid",
|
||||
approval_stage="已付款",
|
||||
risk_flags_json=[],
|
||||
hermes_risk_flag=False,
|
||||
created_at=now - timedelta(days=2),
|
||||
updated_at=now - timedelta(days=1),
|
||||
)
|
||||
)
|
||||
db.add(
|
||||
RiskObservation(
|
||||
observation_key="risk-report-001",
|
||||
subject_type="expense_claim",
|
||||
subject_key="RE-REPORT-001",
|
||||
subject_label="RE-REPORT-001",
|
||||
claim_no="RE-REPORT-001",
|
||||
risk_type="policy",
|
||||
risk_signal="amount_outlier",
|
||||
title="金额异常",
|
||||
risk_level="high",
|
||||
status="pending_review",
|
||||
created_at=now - timedelta(days=1),
|
||||
updated_at=now - timedelta(days=1),
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
result = DigitalEmployeeFinanceReportTaskService(db).generate_report(
|
||||
report_type="weekly",
|
||||
send_email=True,
|
||||
dry_run_email=True,
|
||||
)
|
||||
|
||||
pdf_path = tmp_path / result["pdf"]["storage_key"]
|
||||
html_path = pdf_path.with_name("report.html")
|
||||
runs = [
|
||||
run
|
||||
for run in db.query(AgentRun).filter(AgentRun.agent == "hermes").all()
|
||||
if (run.route_json or {}).get("task_type") == FINANCE_REPORT_TASK_TYPE
|
||||
]
|
||||
|
||||
assert pdf_path.exists()
|
||||
assert pdf_path.read_bytes().startswith(b"%PDF")
|
||||
assert html_path.exists()
|
||||
assert result["delivery"]["status"] in {"dry_run", "pending_configuration"}
|
||||
assert result["summary"]["reimbursement_count"] >= 1
|
||||
assert runs
|
||||
assert runs[0].status == "succeeded"
|
||||
assert runs[0].route_json["report_delivery"]["pdf"]["storage_key"].endswith("report.pdf")
|
||||
|
||||
get_settings.cache_clear()
|
||||
Reference in New Issue
Block a user