feat: 本体字段治理与风险规则模板执行器重构
- 新增本体字段注册表与字段治理审计脚本 - 重构风险规则模板执行器、DSL 验证与清单分类器 - 完善票据夹服务与差旅请求详情页交互 - 优化趋势图表与总览页数据展示 - 增强报销平台风险分级与模拟公司筛选 - 补充本体字段、风险规则生成与票据夹服务测试覆盖
This commit is contained in:
@@ -49,8 +49,18 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
now=now,
|
||||
)
|
||||
previous_start = start - (end - start)
|
||||
trend_start, trend_end, trend_labels = self._resolve_trend_scope(trend_range, now)
|
||||
ranking_start, ranking_end = self._resolve_ranking_scope(department_range, now)
|
||||
trend_start, trend_end, trend_labels = self._resolve_trend_scope(
|
||||
trend_range,
|
||||
now,
|
||||
fallback_start=start,
|
||||
fallback_end=end,
|
||||
)
|
||||
ranking_start, ranking_end = self._resolve_ranking_scope(
|
||||
department_range,
|
||||
now,
|
||||
fallback_start=start,
|
||||
fallback_end=end,
|
||||
)
|
||||
|
||||
claims = [
|
||||
claim for claim in self._fetch_claims() if is_finance_reimbursement_claim(claim)
|
||||
@@ -127,10 +137,31 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
self,
|
||||
trend_range: str,
|
||||
now: datetime,
|
||||
*,
|
||||
fallback_start: datetime | None = None,
|
||||
fallback_end: datetime | None = None,
|
||||
) -> tuple[datetime, datetime, list[str]]:
|
||||
days = self._days_from_label(trend_range, default=12)
|
||||
end_day = now.date()
|
||||
start_day = end_day - timedelta(days=days - 1)
|
||||
today = now.date()
|
||||
key = str(trend_range or "").strip()
|
||||
if key in {"custom", "自定义"} and fallback_start and fallback_end:
|
||||
start_day = fallback_start.date()
|
||||
end_day = (fallback_end - timedelta(days=1)).date()
|
||||
elif key == "今日":
|
||||
start_day = today
|
||||
end_day = today
|
||||
elif key == "本周":
|
||||
start_day = today - timedelta(days=today.weekday())
|
||||
end_day = today
|
||||
elif key == "本月":
|
||||
start_day = today.replace(day=1)
|
||||
end_day = today
|
||||
else:
|
||||
days = self._days_from_label(trend_range, default=12)
|
||||
end_day = today
|
||||
start_day = end_day - timedelta(days=days - 1)
|
||||
if start_day > end_day:
|
||||
start_day, end_day = end_day, start_day
|
||||
days = max(1, (end_day - start_day).days + 1)
|
||||
labels = [self._date_label(start_day + timedelta(days=index)) for index in range(days)]
|
||||
return self._day_start(start_day), self._day_after(end_day), labels
|
||||
|
||||
@@ -138,9 +169,32 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
self,
|
||||
department_range: str,
|
||||
now: datetime,
|
||||
*,
|
||||
fallback_start: datetime | None = None,
|
||||
fallback_end: datetime | None = None,
|
||||
) -> tuple[datetime, datetime]:
|
||||
today = now.date()
|
||||
key = str(department_range or "").strip()
|
||||
if key in {"custom", "自定义"} and fallback_start and fallback_end:
|
||||
return fallback_start, fallback_end
|
||||
if key == "今日":
|
||||
return self._day_start(today), self._day_after(today)
|
||||
if key == "本周":
|
||||
start_day = today - timedelta(days=today.weekday())
|
||||
return self._day_start(start_day), self._day_after(today)
|
||||
if key == "全部":
|
||||
return datetime(1970, 1, 1, tzinfo=UTC), self._day_after(today)
|
||||
if key == "本季度":
|
||||
quarter_month = ((today.month - 1) // 3) * 3 + 1
|
||||
return self._day_start(today.replace(month=quarter_month, day=1)), self._day_after(today)
|
||||
if key == "本年":
|
||||
return self._day_start(today.replace(month=1, day=1)), self._day_after(today)
|
||||
if key == "本月":
|
||||
return self._day_start(today.replace(day=1)), self._day_after(today)
|
||||
if re.search(r"\d+", key):
|
||||
days = self._days_from_label(key, default=10)
|
||||
start_day = today - timedelta(days=days - 1)
|
||||
return self._day_start(start_day), self._day_after(today)
|
||||
if key == "全部":
|
||||
return datetime(1970, 1, 1, tzinfo=UTC), self._day_after(today)
|
||||
if key == "本季度":
|
||||
@@ -227,6 +281,8 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
claim_count = [0 for _ in labels]
|
||||
claim_amount = [Decimal("0.00") for _ in labels]
|
||||
success_count = [0 for _ in labels]
|
||||
category_amounts: dict[str, list[Decimal]] = {}
|
||||
category_totals: dict[str, Decimal] = defaultdict(Decimal)
|
||||
hours: list[list[Decimal]] = [[] for _ in labels]
|
||||
index = {label: idx for idx, label in enumerate(labels)}
|
||||
|
||||
@@ -237,8 +293,12 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
if label not in index:
|
||||
continue
|
||||
bucket = index[label]
|
||||
amount = self._claim_amount(claim)
|
||||
category = self._expense_type_label(claim.expense_type)
|
||||
claim_count[bucket] += 1
|
||||
claim_amount[bucket] += self._claim_amount(claim)
|
||||
claim_amount[bucket] += amount
|
||||
category_amounts.setdefault(category, [Decimal("0.00") for _ in labels])[bucket] += amount
|
||||
category_totals[category] += amount
|
||||
if self._status(claim) in SUCCESS_STATUSES:
|
||||
success_count[bucket] += 1
|
||||
if claim.submitted_at:
|
||||
@@ -248,6 +308,17 @@ class FinanceDashboardService(BudgetSupportMixin):
|
||||
"labels": labels,
|
||||
"claimCount": claim_count,
|
||||
"claimAmount": [self._decimal_number(value) for value in claim_amount],
|
||||
"categoryAmountSeries": [
|
||||
{
|
||||
"name": name,
|
||||
"color": CHART_COLORS[index % len(CHART_COLORS)],
|
||||
"data": [self._decimal_number(value) for value in category_amounts[name]],
|
||||
"total": self._decimal_number(category_totals[name]),
|
||||
}
|
||||
for index, name in enumerate(
|
||||
sorted(category_amounts, key=lambda item: category_totals[item], reverse=True)[:6]
|
||||
)
|
||||
],
|
||||
"successCount": success_count,
|
||||
# 兼容旧前端字段;新财务看板不再使用审批趋势语义。
|
||||
"applications": claim_count,
|
||||
|
||||
Reference in New Issue
Block a user