2026-05-29 14:11:06 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
|
|
|
|
|
|
from app.api.deps import CurrentUserContext
|
|
|
|
|
from app.models.employee import Employee
|
|
|
|
|
from app.models.financial_record import ExpenseClaim
|
|
|
|
|
from app.services.pagination import PageResult, paginate_select
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExpenseClaimPaginationMixin:
|
|
|
|
|
def _claim_list_stmt(self):
|
|
|
|
|
return select(ExpenseClaim).options(
|
|
|
|
|
selectinload(ExpenseClaim.items),
|
|
|
|
|
selectinload(ExpenseClaim.employee).selectinload(Employee.manager),
|
|
|
|
|
selectinload(ExpenseClaim.employee).selectinload(Employee.roles),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def list_claims_page(
|
|
|
|
|
self,
|
|
|
|
|
current_user: CurrentUserContext,
|
|
|
|
|
*,
|
|
|
|
|
page: int | None,
|
|
|
|
|
page_size: int | None,
|
|
|
|
|
) -> PageResult[ExpenseClaim]:
|
|
|
|
|
stmt = self._claim_list_stmt().order_by(
|
|
|
|
|
ExpenseClaim.created_at.desc(),
|
|
|
|
|
ExpenseClaim.occurred_at.desc(),
|
|
|
|
|
)
|
|
|
|
|
stmt = self._access_policy.apply_claim_scope(stmt, current_user)
|
2026-06-01 17:07:14 +08:00
|
|
|
result = paginate_select(self.db, stmt, page=page, page_size=page_size)
|
2026-06-17 14:38:07 +08:00
|
|
|
self._repair_duplicate_budget_approval_stages(result.items)
|
2026-06-01 17:07:14 +08:00
|
|
|
self._access_policy.attach_budget_approval_snapshots(result.items)
|
|
|
|
|
return result
|
2026-05-29 14:11:06 +08:00
|
|
|
|
|
|
|
|
def list_approval_claims_page(
|
|
|
|
|
self,
|
|
|
|
|
current_user: CurrentUserContext,
|
|
|
|
|
*,
|
|
|
|
|
page: int | None,
|
|
|
|
|
page_size: int | None,
|
|
|
|
|
) -> PageResult[ExpenseClaim]:
|
|
|
|
|
stmt = self._claim_list_stmt().order_by(
|
|
|
|
|
ExpenseClaim.submitted_at.desc(),
|
|
|
|
|
ExpenseClaim.created_at.desc(),
|
|
|
|
|
)
|
|
|
|
|
stmt = self._access_policy.apply_approval_claim_scope(stmt, current_user)
|
2026-06-01 17:07:14 +08:00
|
|
|
result = paginate_select(self.db, stmt, page=page, page_size=page_size)
|
2026-06-17 14:38:07 +08:00
|
|
|
self._repair_duplicate_budget_approval_stages(result.items)
|
2026-06-01 17:07:14 +08:00
|
|
|
self._access_policy.attach_budget_approval_snapshots(result.items)
|
|
|
|
|
return result
|
2026-05-29 14:11:06 +08:00
|
|
|
|
|
|
|
|
def list_archived_claims_page(
|
|
|
|
|
self,
|
|
|
|
|
current_user: CurrentUserContext,
|
|
|
|
|
*,
|
|
|
|
|
page: int | None,
|
|
|
|
|
page_size: int | None,
|
|
|
|
|
) -> PageResult[ExpenseClaim]:
|
|
|
|
|
stmt = self._claim_list_stmt().order_by(
|
|
|
|
|
ExpenseClaim.updated_at.desc(),
|
|
|
|
|
ExpenseClaim.submitted_at.desc(),
|
|
|
|
|
ExpenseClaim.created_at.desc(),
|
|
|
|
|
)
|
|
|
|
|
stmt = self._access_policy.apply_archived_claim_scope(stmt, current_user)
|
|
|
|
|
return paginate_select(self.db, stmt, page=page, page_size=page_size)
|