feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
@@ -178,12 +178,17 @@ class UserAgentApplicationMixin:
|
||||
step = self._resolve_expense_application_step(payload, facts)
|
||||
application_claim = None
|
||||
if step == "submitted":
|
||||
application_claim = self._find_duplicate_expense_application_record(payload, facts)
|
||||
if application_claim is not None:
|
||||
step = "duplicate"
|
||||
facts["duplicate_application_stage"] = str(application_claim.approval_stage or "").strip()
|
||||
editable_claim = self._find_editable_expense_application_record(payload)
|
||||
if editable_claim is not None:
|
||||
application_claim = self._update_expense_application_record(payload, facts, editable_claim)
|
||||
facts["application_edit_mode"] = "true"
|
||||
else:
|
||||
application_claim = self._create_expense_application_record(payload, facts)
|
||||
application_claim = self._find_duplicate_expense_application_record(payload, facts)
|
||||
if application_claim is not None:
|
||||
step = "duplicate"
|
||||
facts["duplicate_application_stage"] = str(application_claim.approval_stage or "").strip()
|
||||
else:
|
||||
application_claim = self._create_expense_application_record(payload, facts)
|
||||
facts["application_no"] = application_claim.claim_no
|
||||
facts["application_claim_id"] = application_claim.id
|
||||
facts["manager_name"] = self._resolve_application_manager_name(payload, application_claim)
|
||||
@@ -229,9 +234,14 @@ class UserAgentApplicationMixin:
|
||||
if step == "submitted":
|
||||
application_no = str(facts.get("application_no") or "").strip() or self._build_application_claim_no(payload, facts)
|
||||
manager_name = str(facts.get("manager_name") or "").strip() or "直属领导"
|
||||
submitted_title = (
|
||||
"申请单据已修改并重新提交,已进入审批流程。"
|
||||
if str(facts.get("application_edit_mode") or "").strip().lower() == "true"
|
||||
else "申请单据已生成,并已进入审批流程。"
|
||||
)
|
||||
return "\n\n".join(
|
||||
[
|
||||
"申请单据已生成,并已进入审批流程。",
|
||||
submitted_title,
|
||||
f"系统已推送给 {manager_name} 审核,当前节点:{manager_name}审核中。",
|
||||
f"申请单号:{application_no}",
|
||||
"下方是简要单据信息。需要查看完整详情时,请点击快捷方式进入单据详情。",
|
||||
@@ -930,6 +940,101 @@ class UserAgentApplicationMixin:
|
||||
return "会务费用申请"
|
||||
return "差旅费用申请"
|
||||
|
||||
@staticmethod
|
||||
def _resolve_application_edit_claim_id(context_json: dict[str, object]) -> str:
|
||||
if not isinstance(context_json, dict):
|
||||
return ""
|
||||
is_edit_mode = bool(context_json.get("application_edit_mode") or context_json.get("applicationEditMode"))
|
||||
claim_id = str(
|
||||
context_json.get("application_edit_claim_id")
|
||||
or context_json.get("applicationEditClaimId")
|
||||
or ""
|
||||
).strip()
|
||||
return claim_id if is_edit_mode and claim_id else ""
|
||||
|
||||
@staticmethod
|
||||
def _is_expense_application_claim_like(claim: ExpenseClaim) -> bool:
|
||||
expense_type = str(claim.expense_type or "").strip().lower()
|
||||
claim_no = str(claim.claim_no or "").strip().upper()
|
||||
flags = claim.risk_flags_json
|
||||
if isinstance(flags, dict):
|
||||
flags = [flags]
|
||||
if not isinstance(flags, list):
|
||||
flags = []
|
||||
has_application_detail = any(
|
||||
isinstance(flag, dict)
|
||||
and (
|
||||
str(flag.get("business_stage") or "").strip() == "expense_application"
|
||||
or isinstance(flag.get("application_detail"), dict)
|
||||
)
|
||||
for flag in flags
|
||||
)
|
||||
return (
|
||||
expense_type in {"application", "expense_application"}
|
||||
or expense_type.endswith("_application")
|
||||
or claim_no.startswith("AP-")
|
||||
or claim_no.startswith("APP-")
|
||||
or has_application_detail
|
||||
)
|
||||
|
||||
def _find_editable_expense_application_record(
|
||||
self,
|
||||
payload: UserAgentRequest,
|
||||
) -> ExpenseClaim | None:
|
||||
claim_id = self._resolve_application_edit_claim_id(payload.context_json or {})
|
||||
if not claim_id:
|
||||
return None
|
||||
|
||||
claim = self.db.get(ExpenseClaim, claim_id)
|
||||
if claim is None:
|
||||
raise ValueError("未找到要修改的申请单。")
|
||||
if not self._is_expense_application_claim_like(claim):
|
||||
raise ValueError("只能修改申请单。")
|
||||
|
||||
current_user = self._build_application_current_user(payload)
|
||||
access_policy = ExpenseClaimAccessPolicy(self.db)
|
||||
if not (current_user.is_admin or access_policy.is_claim_owned_by_current_user(claim, current_user)):
|
||||
raise ValueError("只能修改本人被退回的申请单。")
|
||||
|
||||
status = str(claim.status or "").strip().lower()
|
||||
if status not in {"returned", "draft", "supplement"}:
|
||||
raise ValueError("当前申请单状态不支持修改。")
|
||||
return claim
|
||||
|
||||
def _update_expense_application_record(
|
||||
self,
|
||||
payload: UserAgentRequest,
|
||||
facts: dict[str, str],
|
||||
claim: ExpenseClaim,
|
||||
) -> ExpenseClaim:
|
||||
current_user = self._build_application_current_user(payload)
|
||||
flags = claim.risk_flags_json
|
||||
if isinstance(flags, dict):
|
||||
flags = [flags]
|
||||
if not isinstance(flags, list):
|
||||
flags = []
|
||||
preserved_flags = [
|
||||
flag
|
||||
for flag in flags
|
||||
if not (
|
||||
isinstance(flag, dict)
|
||||
and str(flag.get("source") or "").strip() == "application_detail"
|
||||
)
|
||||
]
|
||||
claim.expense_type = self._resolve_application_expense_type_code(facts)
|
||||
claim.reason = str(facts.get("reason") or "费用申请").strip() or "费用申请"
|
||||
claim.location = str(facts.get("location") or "待补充").strip() or "待补充"
|
||||
claim.amount = self._parse_application_amount_to_decimal(facts.get("amount", ""))
|
||||
claim.occurred_at = self._parse_application_occurred_at(facts.get("time", ""))
|
||||
claim.risk_flags_json = [*preserved_flags, self._build_application_detail_flag(facts)]
|
||||
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
|
||||
submitted = ExpenseClaimService(self.db).submit_claim(claim.id, current_user)
|
||||
if submitted is None:
|
||||
raise ValueError("未找到可修改的申请单。")
|
||||
return submitted
|
||||
|
||||
def _create_expense_application_record(
|
||||
self,
|
||||
payload: UserAgentRequest,
|
||||
|
||||
Reference in New Issue
Block a user