refactor(server): 单号规则收紧为 A/R/D+8 位紧凑格式

- DOCUMENT_NUMBER_PREFIXES 改为 A/R/D,新增短格式与旧格式正则并存识别,提取正则加边界锚定避免误匹配
- build_document_number 去掉时间戳段,统一生成 A+token 等紧凑单号,is_application_claim_no 兼容旧 AP-/APP- 前缀
- access_policy/status_registry/reimbursements/expense_claims/budget_support 统一复用 is_application_claim_no 判定申请单
- 同步 document_numbering 单元测试覆盖新旧两种格式
This commit is contained in:
caoxiaozhu
2026-06-20 21:44:06 +08:00
parent 96c2e1099a
commit 47c6a4bb73
6 changed files with 77 additions and 33 deletions

View File

@@ -11,6 +11,7 @@ from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim
from app.models.organization import OrganizationUnit
from app.models.role import Role
from app.services.document_numbering import is_application_claim_no
from app.services.expense_claim_workflow_constants import (
APPLICATION_ARCHIVE_STAGE,
ARCHIVE_ACCOUNTING_STAGE,
@@ -42,6 +43,14 @@ class ExpenseClaimAccessPolicy:
def __init__(self, db: Session) -> None:
self.db = db
@staticmethod
def _build_application_claim_no_condition(claim_no: Any) -> Any:
return or_(
claim_no.like("AP-%"),
claim_no.like("APP-%"),
claim_no.like("A________"),
)
@staticmethod
def has_privileged_claim_access(current_user: CurrentUserContext) -> bool:
if current_user.is_admin:
@@ -61,8 +70,7 @@ class ExpenseClaimAccessPolicy:
normalized_type = func.lower(func.coalesce(ExpenseClaim.expense_type, ""))
claim_no = func.upper(func.coalesce(ExpenseClaim.claim_no, ""))
application_condition = or_(
claim_no.like("AP-%"),
claim_no.like("APP-%"),
ExpenseClaimAccessPolicy._build_application_claim_no_condition(claim_no),
normalized_type == "application",
normalized_type.like("%\\_application", escape="\\"),
)
@@ -101,9 +109,9 @@ class ExpenseClaimAccessPolicy:
normalized_status = str(claim.status or "").strip().lower()
stage = str(claim.approval_stage or "").strip()
normalized_type = str(claim.expense_type or "").strip().lower()
claim_no = str(claim.claim_no or "").strip().upper()
claim_no = str(claim.claim_no or "").strip()
is_application_claim = (
claim_no.startswith(("AP-", "APP-"))
is_application_claim_no(claim_no)
or normalized_type == "application"
or normalized_type.endswith("_application")
)
@@ -715,8 +723,9 @@ class ExpenseClaimAccessPolicy:
"%\\_application",
escape="\\",
),
~func.upper(func.coalesce(ExpenseClaim.claim_no, "")).like("AP-%"),
~func.upper(func.coalesce(ExpenseClaim.claim_no, "")).like("APP-%"),
~self._build_application_claim_no_condition(
func.upper(func.coalesce(ExpenseClaim.claim_no, ""))
),
~self.build_archived_claim_condition(),
)
conditions.append(company_reimbursement_condition)