feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
@@ -29,7 +29,6 @@ from app.services.demo_company_simulation_catalog import ( # noqa: E402
|
||||
BUDGETED_STATUSES,
|
||||
PENDING_STATUSES,
|
||||
SIM_BUDGET_PREFIX,
|
||||
SIM_CLAIM_PREFIX,
|
||||
SIM_EMPLOYEE_PREFIX,
|
||||
SIM_PROJECT_CODE,
|
||||
SIM_RESERVATION_PREFIX,
|
||||
@@ -60,6 +59,8 @@ RECENT_DATES = (
|
||||
datetime(2026, 6, 1, 15, 0, tzinfo=UTC),
|
||||
datetime(2026, 6, 2, 6, 0, tzinfo=UTC),
|
||||
)
|
||||
PERIOD_START = date(2026, 1, 1)
|
||||
PERIOD_END = date(2026, 6, 2)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -139,6 +140,7 @@ def repair_distribution(db, *, apply: bool) -> RepairSummary:
|
||||
|
||||
if apply:
|
||||
_normalize_sim_claim_workflow(sim_claims)
|
||||
_clamp_sim_claim_dates(sim_claims)
|
||||
_redistribute_employees(sim_employees, departments, employee_plan)
|
||||
db.flush()
|
||||
employees_by_dept = _employees_by_department(db)
|
||||
@@ -235,8 +237,8 @@ def _sim_claims(db) -> list[ExpenseClaim]:
|
||||
db.scalars(
|
||||
select(ExpenseClaim)
|
||||
.options(selectinload(ExpenseClaim.items))
|
||||
.where(ExpenseClaim.claim_no.like(f"{SIM_CLAIM_PREFIX}%"))
|
||||
.order_by(ExpenseClaim.claim_no.asc())
|
||||
.where(ExpenseClaim.project_code == SIM_PROJECT_CODE)
|
||||
.order_by(ExpenseClaim.created_at.asc(), ExpenseClaim.claim_no.asc())
|
||||
).all()
|
||||
)
|
||||
|
||||
@@ -254,6 +256,23 @@ def _normalize_sim_claim_workflow(claims: list[ExpenseClaim]) -> None:
|
||||
claim.approval_stage = normalized.approval_stage
|
||||
|
||||
|
||||
def _clamp_sim_claim_dates(claims: list[ExpenseClaim]) -> None:
|
||||
for index, claim in enumerate(claims):
|
||||
occurred_at = claim.occurred_at or claim.submitted_at
|
||||
if occurred_at is None:
|
||||
continue
|
||||
if PERIOD_START <= occurred_at.date() <= PERIOD_END:
|
||||
continue
|
||||
anchor = RECENT_DATES[index % len(RECENT_DATES)]
|
||||
claim.occurred_at = anchor - _hours(2)
|
||||
if claim.submitted_at is not None or claim.status != "draft":
|
||||
claim.submitted_at = anchor
|
||||
claim.created_at = claim.occurred_at
|
||||
claim.updated_at = anchor + _hours(1)
|
||||
for item in claim.items or []:
|
||||
item.item_date = claim.occurred_at.date()
|
||||
|
||||
|
||||
def _counts_by_weight(total: int) -> dict[str, int]:
|
||||
raw = [(code, total * weight) for code, weight in DEPARTMENT_PLAN]
|
||||
counts = {code: int(value) for code, value in raw}
|
||||
|
||||
Reference in New Issue
Block a user