feat: Project通过关联查询出来所有子表审批单记录的金额总和来计算已花费金额以及剩余预算余额

This commit is contained in:
martsforever
2025-09-10 18:53:21 +08:00
parent 78dadd2b8d
commit 226efc1aa0
5 changed files with 58 additions and 69 deletions
+1 -2
View File
@@ -9,8 +9,7 @@ from pydantic import BaseModel
from app.model.ApproveModel import ApproveModel, ApproveService
from app.model.HotelModel import HotelService, HotelModel
from app.model.OrderModel import OrderService
from app.model.ProjectModel import ProjectModel
from app.model.ProjectService import ProjectService
from app.model.ProjectModel import ProjectModel, ProjectService
from app.model.ReimburseModel import ReimburseService, ReimburseModel
from app.model.ReimburseOtherModel import ReimburseOtherService
from app.model.UserModel import UserServiceModel, UserService
+55 -7
View File
@@ -1,11 +1,13 @@
from decimal import Decimal
from typing import Optional
from typing import Optional, List
from pydantic import computed_field, PrivateAttr
from sqlmodel import Field, Relationship
from pydantic import computed_field
from sqlalchemy.orm import selectinload
from sqlmodel import Field, Relationship, select
from app.model.BasicModel import BasicModel
from app.model.UserModel import UserServiceModel
from app.utils.create_module_service import create_model_service
class ProjectModel(BasicModel, table=True):
@@ -13,12 +15,9 @@ class ProjectModel(BasicModel, table=True):
name: str = Field(default=None, description="项目名称")
description: str = Field(default=None, description="项目描述")
budget: float = Field(default=None, description="项目预算金额")
budget: Decimal = Field(default=None, description="项目预算金额")
version: int = Field(default=None, description="项目版本号")
spent: Decimal = Field(default=None, description="项目花费金额")
balance: Decimal = Field(default=None, description="项目预算余额")
# /*---------------------------------------leader_id-------------------------------------------*/
leader_id: str = Field(
@@ -47,3 +46,52 @@ class ProjectModel(BasicModel, table=True):
@leader_name.setter
def leader_name(self, value: Optional[str]) -> None:
pass
# /*---------------------------------------approve_list-------------------------------------------*/
approve_list_relationship: Optional[List["ApproveModel"]] = Relationship(
sa_relationship_kwargs={
"foreign_keys": "ApproveModel.proj_id",
"primaryjoin": "ProjectModel.id == ApproveModel.proj_id",
}
)
# /*---------------------------------------spent: 已花费金额-------------------------------------------*/
@computed_field
@property
def spent(self) -> Optional[Decimal]:
try:
total_amount = Decimal(0)
for approve_item in self.approve_list_relationship:
if approve_item.status == 'approved':
total_amount = total_amount + approve_item.amount
return total_amount
except:
return None
@spent.setter
def spent(self, value: Optional[Decimal]) -> None:
pass
# /*---------------------------------------balance:剩余预算金额-------------------------------------------*/
@computed_field
@property
def balance(self) -> Optional[Decimal]:
if self.spent is not None:
return self.budget - self.spent
else:
return None
@balance.setter
def balance(self, value: Optional[Decimal]) -> None:
pass
ProjectService = create_model_service(
Cls=ProjectModel,
custom_query=lambda: select(ProjectModel)
.options(
selectinload(ProjectModel.leader),
selectinload(ProjectModel.approve_list_relationship)
),
)
-58
View File
@@ -1,58 +0,0 @@
from decimal import Decimal
from typing import List
from sqlalchemy.orm import selectinload
from sqlmodel import select
from app.model.ApproveModel import ApproveService, ApproveModel
from app.model.ProjectModel import ProjectModel
from app.utils.create_module_service import create_model_service
from app.utils.db_utils import AsyncSessionDep
ProjectService = create_model_service(
Cls=ProjectModel,
custom_query=lambda: select(ProjectModel).options(
selectinload(ProjectModel.leader),
),
after_query_list=lambda query_cls_list, has_next, query_param, session: set_list_spent(query_cls_list, session),
after_query_item=lambda item_cls, row_dict, session: set_item_spent(item_cls, session),
after_insert=lambda insert_cls, row_dict, session: set_item_spent(insert_cls, session),
after_update=lambda update_cls, row_dict, session: set_item_spent(update_cls, session),
after_batch_insert=lambda refresh_cls_list, row_dict_list, session: set_list_spent(refresh_cls_list, session),
after_batch_update=lambda refresh_cls_list, row_dict_list, session: set_list_spent(refresh_cls_list, session),
)
async def set_list_spent(cls_list: List[ProjectModel], session: AsyncSessionDep):
project_dict_list, project_dict_map = await query_proj_spent([item.id for item in cls_list], session)
for item in cls_list:
target = project_dict_map.get(item.id, None)
if target:
item.spent = target['spent']
item.balance = target['balance']
async def set_item_spent(cls: ProjectModel, session: AsyncSessionDep):
project_dict_list, project_dict_map = await query_proj_spent([cls.id], session)
target = project_dict_map.get(cls.id, None)
if target:
cls.spent = target['spent']
cls.balance = target['balance']
async def query_proj_spent(proj_id_list: List[str], session: AsyncSessionDep):
result = {item: Decimal(0) for item in proj_id_list}
approve_cls_list = (await session.execute(ApproveService.select_cls().where(ApproveModel.proj_id.in_(proj_id_list)))).scalars().all()
for approve_item in approve_cls_list:
if approve_item.status == 'approved':
result[approve_item.proj_id] += Decimal(approve_item.amount)
project_cls_list = (await session.execute(ProjectService.select_cls().where(ProjectModel.id.in_(proj_id_list)))).scalars().all()
project_dict_list = [item.model_dump() for item in project_cls_list]
for dict_item in project_dict_list:
# 已花费金额
dict_item['spent'] = result[dict_item['id']]
# 剩余金额
dict_item['balance'] = Decimal(dict_item['budget']) - Decimal(dict_item['spent'])
project_dict_map = {item['id']: item for item in project_dict_list}
return project_dict_list, project_dict_map
+1 -1
View File
@@ -37,7 +37,7 @@ from app.model.LlmProduct import LlmProductService
from app.model.OrderModel import OrderService
from app.model.OrgModel import OrgService
from app.model.PosModel import PosService
from app.model.ProjectService import ProjectService
from app.model.ProjectModel import ProjectService
from app.model.ReimburseModel import ReimburseService
from app.model.ReimburseOtherModel import ReimburseOtherService
from app.model.ReimburseTravelModel import ReimburseTravelService
+1 -1
View File
@@ -1,7 +1,7 @@
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from app.model.ProjectService import ProjectService
from app.model.ProjectModel import ProjectService
from app.model.RelProjUserModel import RelProjUserService
from app.utils.PageQueryParams import PageQueryParams
from app.utils.db_utils import async_session