- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
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()
|