feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
176
server/tests/test_digital_employee_reminder_task.py
Normal file
176
server/tests/test_digital_employee_reminder_task.py
Normal file
@@ -0,0 +1,176 @@
|
||||
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.db.base import Base
|
||||
from app.models.employee import Employee
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.models.role import Role
|
||||
from app.services.digital_employee_dashboard import DigitalEmployeeDashboardService
|
||||
from app.services.digital_employee_reminder_task import DigitalEmployeeReminderTaskService
|
||||
|
||||
|
||||
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_digital_employee_reminder_task_generates_actionable_report() -> None:
|
||||
now = datetime(2026, 6, 2, 2, 0, tzinfo=UTC)
|
||||
|
||||
with build_session() as db:
|
||||
_seed_reminder_data(db, now)
|
||||
|
||||
result = DigitalEmployeeReminderTaskService(db).refresh_reminders(now=now)
|
||||
|
||||
summary = result["summary"]
|
||||
report = result["report"]
|
||||
assert result["task_type"] == "digital_employee_reminder_scan"
|
||||
assert summary["recipient_count"] >= 3
|
||||
assert summary["reminder_count"] >= 4
|
||||
assert summary["approval_pending_count"] == 1
|
||||
assert summary["budget_reminder_count"] == 1
|
||||
assert summary["travel_application_reminder_count"] == 1
|
||||
assert summary["reimbursement_overdue_count"] == 1
|
||||
|
||||
reminder_types = {
|
||||
reminder["type"]
|
||||
for recipient in report["recipients"]
|
||||
for reminder in recipient["reminders"]
|
||||
}
|
||||
assert {
|
||||
"approval_pending",
|
||||
"budget_compilation",
|
||||
"travel_application_expiry",
|
||||
"reimbursement_overdue",
|
||||
}.issubset(reminder_types)
|
||||
|
||||
dashboard = DigitalEmployeeDashboardService(db).build_dashboard(days=7)
|
||||
assert dashboard.totals["reminders"] >= 4
|
||||
assert dashboard.totals["businessOutputs"] >= 4
|
||||
assert dashboard.task_distribution[0]["taskType"] == "digital_employee_reminder_scan"
|
||||
|
||||
|
||||
def _seed_reminder_data(db: Session, now: datetime) -> None:
|
||||
budget_role = Role(
|
||||
id="role-budget",
|
||||
role_code="budget_monitor",
|
||||
name="预算管理员",
|
||||
description="预算编制提醒接收人",
|
||||
)
|
||||
manager = Employee(
|
||||
id="emp-manager",
|
||||
employee_no="M001",
|
||||
name="审批领导",
|
||||
email="manager@example.com",
|
||||
position="部门负责人",
|
||||
grade="M2",
|
||||
)
|
||||
employee = Employee(
|
||||
id="emp-user",
|
||||
employee_no="E001",
|
||||
name="出差员工",
|
||||
email="employee@example.com",
|
||||
position="客户经理",
|
||||
grade="P5",
|
||||
manager=manager,
|
||||
finance_owner_name="财务BP",
|
||||
)
|
||||
budget_admin = Employee(
|
||||
id="emp-budget",
|
||||
employee_no="B001",
|
||||
name="预算管理员甲",
|
||||
email="budget@example.com",
|
||||
position="预算管理员",
|
||||
grade="P6",
|
||||
roles=[budget_role],
|
||||
)
|
||||
db.add_all([budget_role, manager, employee, budget_admin])
|
||||
db.add_all(
|
||||
[
|
||||
_claim(
|
||||
"claim-approval",
|
||||
"EXP-APPROVAL-001",
|
||||
employee,
|
||||
"travel",
|
||||
"12000.00",
|
||||
now - timedelta(days=3),
|
||||
"submitted",
|
||||
"直属领导审批",
|
||||
),
|
||||
_claim(
|
||||
"claim-travel-app",
|
||||
"APP-TRAVEL-001",
|
||||
employee,
|
||||
"travel_application",
|
||||
"8000.00",
|
||||
now - timedelta(days=1),
|
||||
"approved",
|
||||
"已审批",
|
||||
risk_flags=[
|
||||
{
|
||||
"source": "application_detail",
|
||||
"application_detail": {
|
||||
"application_type": "差旅申请",
|
||||
"time": "2026-06-01",
|
||||
},
|
||||
}
|
||||
],
|
||||
),
|
||||
_claim(
|
||||
"claim-supplement",
|
||||
"EXP-SUPPLEMENT-001",
|
||||
employee,
|
||||
"meal",
|
||||
"600.00",
|
||||
now - timedelta(days=2),
|
||||
"returned",
|
||||
"材料待补",
|
||||
),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
|
||||
|
||||
def _claim(
|
||||
claim_id: str,
|
||||
claim_no: str,
|
||||
employee: Employee,
|
||||
expense_type: str,
|
||||
amount: str,
|
||||
happened_at: datetime,
|
||||
status: str,
|
||||
approval_stage: str,
|
||||
*,
|
||||
risk_flags: list[dict] | None = None,
|
||||
) -> ExpenseClaim:
|
||||
return ExpenseClaim(
|
||||
id=claim_id,
|
||||
claim_no=claim_no,
|
||||
employee_id=employee.id,
|
||||
employee_name=employee.name,
|
||||
department_name="市场部",
|
||||
expense_type=expense_type,
|
||||
reason="客户拜访",
|
||||
location="上海",
|
||||
amount=Decimal(amount),
|
||||
invoice_count=1,
|
||||
occurred_at=happened_at,
|
||||
submitted_at=happened_at,
|
||||
status=status,
|
||||
approval_stage=approval_stage,
|
||||
risk_flags_json=risk_flags or [],
|
||||
created_at=happened_at,
|
||||
updated_at=happened_at,
|
||||
)
|
||||
Reference in New Issue
Block a user