feat(finance): 添加公司通信费报销规则
- 新增通信费报销规则代码和文件名常量 - 在初始化数据中创建公司通信费报销规则资产 - 添加对应的版本和审核记录 - 标记为 v1.0.0 版本并审核通过
This commit is contained in:
@@ -36,6 +36,8 @@ from app.models.financial_record import (
|
||||
)
|
||||
from app.services.agent_asset_spreadsheet import (
|
||||
AgentAssetSpreadsheetManager,
|
||||
COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
|
||||
COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
|
||||
COMPANY_TRAVEL_EXPENSE_RULE_CODE,
|
||||
COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||||
FINANCE_RULES_LIBRARY,
|
||||
@@ -88,6 +90,7 @@ LEGACY_RULE_CODES = (
|
||||
|
||||
ATTACHMENT_RULE_ASSET_CODE = "rule.expense.attachment_submission_requirements"
|
||||
COMPANY_TRAVEL_RULE_VERSION = "v1.0.0"
|
||||
COMPANY_COMMUNICATION_RULE_VERSION = "v1.0.0"
|
||||
|
||||
ATTACHMENT_RULE_RUNTIME_CONFIG = {
|
||||
"kind": "policy_rule_draft",
|
||||
@@ -279,6 +282,28 @@ class AgentFoundationService:
|
||||
"rule_template_label": "差旅报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
company_communication_rule = AgentAsset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code=COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
|
||||
name="公司通信费报销规则",
|
||||
description="通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "communication_expense", "expense_standard"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
published_version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
working_version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
config_json={
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_template_label": "通信费报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
skill_expense_asset = AgentAsset(
|
||||
asset_type=AgentAssetType.SKILL.value,
|
||||
code="skill.expense.summary_lookup",
|
||||
@@ -406,6 +431,7 @@ class AgentFoundationService:
|
||||
scene_submission_rule,
|
||||
travel_policy_rule,
|
||||
company_travel_rule,
|
||||
company_communication_rule,
|
||||
skill_expense_asset,
|
||||
skill_ar_asset,
|
||||
invoice_mcp_asset,
|
||||
@@ -423,6 +449,11 @@ class AgentFoundationService:
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
actor_name="系统初始化",
|
||||
)
|
||||
company_communication_rule_meta = self._ensure_company_communication_rule_spreadsheet_seed(
|
||||
company_communication_rule,
|
||||
version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
actor_name="系统初始化",
|
||||
)
|
||||
|
||||
self.db.add_all(
|
||||
[
|
||||
@@ -484,6 +515,18 @@ class AgentFoundationService:
|
||||
change_note="初始化差旅费报销 Excel 规则表。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=company_communication_rule,
|
||||
version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
content=AgentAssetSpreadsheetManager.build_version_markdown(
|
||||
rule_name=company_communication_rule.name,
|
||||
version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
metadata=company_communication_rule_meta,
|
||||
),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="初始化通信费报销 Excel 规则表。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=skill_expense_asset,
|
||||
version="v1.0.0",
|
||||
@@ -635,6 +678,14 @@ class AgentFoundationService:
|
||||
review_note="首版 Excel 规则表已确认,可作为财务规则使用。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
AgentAssetReview(
|
||||
asset=company_communication_rule,
|
||||
version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="首版 Excel 规则表已确认,可作为财务规则使用。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1001,6 +1052,9 @@ class AgentFoundationService:
|
||||
company_travel_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == COMPANY_TRAVEL_EXPENSE_RULE_CODE)
|
||||
)
|
||||
company_communication_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == COMPANY_COMMUNICATION_EXPENSE_RULE_CODE)
|
||||
)
|
||||
|
||||
if ATTACHMENT_RULE_ASSET_CODE not in existing_codes:
|
||||
attachment_rule = self._create_seed_asset(
|
||||
@@ -1209,6 +1263,26 @@ class AgentFoundationService:
|
||||
"rule_template_label": "差旅报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
if COMPANY_COMMUNICATION_EXPENSE_RULE_CODE not in existing_codes:
|
||||
company_communication_rule = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code=COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
|
||||
name="公司通信费报销规则",
|
||||
description="通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "communication_expense", "expense_standard"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
config_json={
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_template_label": "通信费报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
|
||||
if company_travel_rule is not None:
|
||||
if not str(company_travel_rule.current_version or "").strip():
|
||||
@@ -1256,6 +1330,52 @@ class AgentFoundationService:
|
||||
reviewed_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
if company_communication_rule is not None:
|
||||
if not str(company_communication_rule.current_version or "").strip():
|
||||
company_communication_rule.current_version = COMPANY_COMMUNICATION_RULE_VERSION
|
||||
if not str(company_communication_rule.working_version or "").strip():
|
||||
company_communication_rule.working_version = company_communication_rule.current_version
|
||||
if not str(company_communication_rule.published_version or "").strip():
|
||||
company_communication_rule.published_version = company_communication_rule.current_version
|
||||
if not str(company_communication_rule.status or "").strip():
|
||||
company_communication_rule.status = AgentAssetStatus.ACTIVE.value
|
||||
company_communication_rule.description = "通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。"
|
||||
company_communication_rule.config_json = {
|
||||
**(company_communication_rule.config_json or {}),
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_template_label": "通信费报销 Excel 模板",
|
||||
}
|
||||
company_communication_rule_meta = self._ensure_company_communication_rule_spreadsheet_seed(
|
||||
company_communication_rule,
|
||||
version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION),
|
||||
actor_name="系统初始化",
|
||||
)
|
||||
self._ensure_asset_version(
|
||||
company_communication_rule,
|
||||
version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION),
|
||||
content=AgentAssetSpreadsheetManager.build_version_markdown(
|
||||
rule_name=company_communication_rule.name,
|
||||
version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION),
|
||||
metadata=company_communication_rule_meta,
|
||||
),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="初始化通信费报销 Excel 规则表。",
|
||||
created_by="系统初始化",
|
||||
)
|
||||
if str(company_communication_rule.current_version or "").strip() == COMPANY_COMMUNICATION_RULE_VERSION:
|
||||
self._ensure_asset_review(
|
||||
company_communication_rule,
|
||||
version=COMPANY_COMMUNICATION_RULE_VERSION,
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="首版 Excel 规则表已确认,可作为财务规则使用。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
if "skill.ar.aging_summary" not in existing_codes:
|
||||
asset = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.SKILL.value,
|
||||
@@ -1487,6 +1607,21 @@ class AgentFoundationService:
|
||||
}
|
||||
return metadata
|
||||
|
||||
def _ensure_company_communication_rule_spreadsheet_seed(
|
||||
self,
|
||||
asset: AgentAsset,
|
||||
*,
|
||||
version: str,
|
||||
actor_name: str,
|
||||
):
|
||||
return self._ensure_finance_rule_spreadsheet_seed(
|
||||
asset,
|
||||
version=version,
|
||||
actor_name=actor_name,
|
||||
file_name=COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
|
||||
fallback_sheet_name="通信费报销规则",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _read_or_build_company_travel_rule_file(
|
||||
manager: AgentAssetSpreadsheetManager,
|
||||
@@ -1501,6 +1636,113 @@ class AgentFoundationService:
|
||||
return live_path.read_bytes()
|
||||
return AgentAssetSpreadsheetManager.build_blank_rule_workbook("差旅费报销规则")
|
||||
|
||||
def _ensure_finance_rule_spreadsheet_seed(
|
||||
self,
|
||||
asset: AgentAsset,
|
||||
*,
|
||||
version: str,
|
||||
actor_name: str,
|
||||
file_name: str,
|
||||
fallback_sheet_name: str,
|
||||
):
|
||||
manager = AgentAssetSpreadsheetManager()
|
||||
manager.ensure_rule_library_dirs()
|
||||
live_document = manager.store_rule_library_spreadsheet(
|
||||
library=FINANCE_RULES_LIBRARY,
|
||||
file_name=file_name,
|
||||
content=self._read_or_build_finance_rule_file(
|
||||
manager,
|
||||
file_name=file_name,
|
||||
fallback_sheet_name=fallback_sheet_name,
|
||||
),
|
||||
actor_name=actor_name,
|
||||
source="rule-library",
|
||||
)
|
||||
existing_document = (
|
||||
asset.config_json.get("rule_document")
|
||||
if isinstance(asset.config_json, dict)
|
||||
else None
|
||||
)
|
||||
storage_key = (
|
||||
str(existing_document.get("storage_key") or "").strip()
|
||||
if isinstance(existing_document, dict)
|
||||
else ""
|
||||
)
|
||||
if storage_key:
|
||||
try:
|
||||
existing_path = manager.resolve_storage_path(storage_key)
|
||||
except FileNotFoundError:
|
||||
existing_path = None
|
||||
if existing_path is not None and existing_path.exists():
|
||||
metadata = RuleSpreadsheetMeta(
|
||||
file_name=str(existing_document.get("file_name") or file_name),
|
||||
storage_key=storage_key,
|
||||
mime_type=str(existing_document.get("mime_type") or "").strip()
|
||||
or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
size_bytes=int(existing_document.get("size_bytes") or existing_path.stat().st_size),
|
||||
checksum=hashlib.sha256(existing_path.read_bytes()).hexdigest(),
|
||||
updated_at=str(existing_document.get("updated_at") or "").strip()
|
||||
or datetime.now(UTC).isoformat(),
|
||||
updated_by=str(existing_document.get("updated_by") or actor_name).strip()
|
||||
or actor_name,
|
||||
source=str(existing_document.get("source") or "seed").strip() or "seed",
|
||||
)
|
||||
asset.config_json = {
|
||||
**(asset.config_json or {}),
|
||||
"detail_mode": "spreadsheet",
|
||||
"tag": "财务规则",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_document": {
|
||||
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||||
live_document,
|
||||
asset_version=version,
|
||||
),
|
||||
"storage_key": live_document.storage_key,
|
||||
},
|
||||
}
|
||||
return metadata
|
||||
|
||||
live_content = manager.resolve_storage_path(live_document.storage_key).read_bytes()
|
||||
metadata = manager.store_spreadsheet(
|
||||
asset_id=asset.id,
|
||||
version=version,
|
||||
file_name=file_name,
|
||||
content=live_content,
|
||||
actor_name=actor_name,
|
||||
source="seed",
|
||||
)
|
||||
asset.config_json = {
|
||||
**(asset.config_json or {}),
|
||||
"detail_mode": "spreadsheet",
|
||||
"tag": "财务规则",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_document": {
|
||||
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||||
live_document,
|
||||
asset_version=version,
|
||||
),
|
||||
"storage_key": live_document.storage_key,
|
||||
},
|
||||
}
|
||||
return metadata
|
||||
|
||||
@staticmethod
|
||||
def _read_or_build_finance_rule_file(
|
||||
manager: AgentAssetSpreadsheetManager,
|
||||
*,
|
||||
file_name: str,
|
||||
fallback_sheet_name: str,
|
||||
) -> bytes:
|
||||
live_key = (
|
||||
Path("rules")
|
||||
/ FINANCE_RULES_LIBRARY
|
||||
/ file_name
|
||||
).as_posix()
|
||||
live_path = manager.resolve_storage_path(live_key)
|
||||
if live_path.exists():
|
||||
return live_path.read_bytes()
|
||||
return AgentAssetSpreadsheetManager.build_blank_rule_workbook(fallback_sheet_name)
|
||||
|
||||
def _create_seed_asset(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user