feat(server): 报销单输出工号/邮箱并扩展申请人邮箱前缀匹配

- ExpenseClaimRead 新增 employee_no/employee_email 字段,ExpenseClaim 模型补对应只读属性
- expense_claim_access_policy 在姓名匹配未果时,按 candidate@% 邮箱前缀匹配 Employee.email,命中唯一记录即返回
- test_backend_pagination/test_expense_claim_service 补充工号/邮箱字段断言与邮箱前缀匹配用例
- 更新公司通信费报销规则表
This commit is contained in:
caoxiaozhu
2026-06-22 15:55:48 +08:00
parent 1b04ee1c4c
commit aa965da69d
6 changed files with 70 additions and 0 deletions

View File

@@ -105,6 +105,8 @@ def test_expense_claims_support_page_envelope_and_keep_legacy_list() -> None:
assert payload["total_pages"] == 2
assert payload["has_next"] is True
assert payload["has_previous"] is False
assert payload["items"][0]["employee_no"] == "E-PAGE"
assert payload["items"][0]["employee_email"] == "page-user@example.com"
def test_employee_directory_supports_backend_pagination() -> None:

View File

@@ -4301,6 +4301,50 @@ def test_list_claims_scopes_to_current_user_id_even_when_names_duplicate() -> No
assert claims[0].claim_no == "EXP-DUP-001"
def test_list_claims_resolves_short_username_to_unique_employee_email_prefix() -> None:
current_user = CurrentUserContext(
username="caoxiaozhu",
name="caoxiaozhu",
role_codes=["employee"],
is_admin=False,
)
with build_session() as db:
employee = Employee(
employee_no="E90919",
name="曹笑竹",
email="caoxiaozhu@xf.com",
)
db.add(employee)
db.flush()
db.add(
ExpenseClaim(
claim_no="AP-SHORT-USERNAME-001",
employee_id=employee.id,
employee_name=employee.name,
department_name="研发部",
project_code="PRJ-APP",
expense_type="travel_application",
reason="客户现场实施",
location="北京",
amount=Decimal("1600.00"),
currency="CNY",
invoice_count=0,
occurred_at=datetime(2026, 6, 23, 9, 0, tzinfo=UTC),
submitted_at=datetime(2026, 6, 23, 10, 0, tzinfo=UTC),
status="approved",
approval_stage=APPROVAL_DONE_STAGE,
risk_flags_json=[],
)
)
db.commit()
claims = ExpenseClaimService(db).list_claims(current_user)
assert len(claims) == 1
assert claims[0].claim_no == "AP-SHORT-USERNAME-001"
def test_list_claims_limits_finance_to_personal_records() -> None:
current_user = CurrentUserContext(
username="finance@example.com",