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