project_quota.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. from sqlalchemy import and_, or_, func, Integer
  2. from datetime import datetime
  3. from typing import Optional
  4. import tools.db_helper as db_helper
  5. from core.dtos import ProjectQuotaDto
  6. from core.enum import SendStatusEnum
  7. from core.models import ProjectQuotaModel
  8. from core.user_session import UserSession
  9. class ProjectQuotaStore:
  10. def __init__(self):
  11. # self._database= None
  12. self._database = "iwb_railway_costing_v1"
  13. self._current_user = None
  14. @property
  15. def current_user(self):
  16. if self._current_user is None:
  17. self._current_user = UserSession.get_current_user()
  18. return self._current_user
  19. def get_quotas_paginated(
  20. self,
  21. budget_id: int,
  22. project_id: str,
  23. item_code: str,
  24. page: int = 1,
  25. page_size: int = 10,
  26. keyword: Optional[str] = None,
  27. send_status: Optional[int] = None,
  28. ):
  29. """分页查询定额列表
  30. Args:
  31. page: 页码,从1开始
  32. page_size: 每页数量
  33. project_id: 项目编号
  34. budget_id: 概算序号
  35. item_code: 条目序号
  36. keyword: 关键字
  37. send_status: 发送状态
  38. Returns:
  39. Tuple[total_count, quotas]
  40. """
  41. with db_helper.mysql_query_session(self._database) as db_session:
  42. query = db_session.query(ProjectQuotaModel)
  43. # 构建查询条件
  44. conditions = [
  45. ProjectQuotaModel.is_del == 0,
  46. ProjectQuotaModel.project_id == project_id,
  47. ProjectQuotaModel.budget_id == budget_id,
  48. ProjectQuotaModel.item_code.like(f"{item_code}%"),
  49. ]
  50. if send_status is not None:
  51. conditions.append(ProjectQuotaModel.send_status == send_status)
  52. if keyword:
  53. conditions.append(
  54. or_(
  55. ProjectQuotaModel.quota_code.like(f"%{keyword}%"),
  56. ProjectQuotaModel.entry_name.like(f"%{keyword}%"),
  57. )
  58. )
  59. query = query.filter(and_(*conditions))
  60. # 计算总数
  61. total_count = query.count()
  62. # 分页
  63. query = (
  64. query.order_by(ProjectQuotaModel.created_at.desc())
  65. .offset((page - 1) * page_size)
  66. .limit(page_size)
  67. )
  68. quotas = query.all()
  69. return {"total": total_count, "data": quotas}
  70. def get_quotas_by_task_paginated(
  71. self,
  72. task_id: int,
  73. budget_id,
  74. project_id,
  75. item_code,
  76. page: int = 1,
  77. page_size: int = 10,
  78. keyword: Optional[str] = None,
  79. send_status: Optional[int] = None,
  80. ):
  81. with db_helper.mysql_query_session(self._database) as db_session:
  82. query = db_session.query(ProjectQuotaModel).filter(
  83. and_(
  84. ProjectQuotaModel.task_id == task_id,
  85. ProjectQuotaModel.budget_id == budget_id,
  86. ProjectQuotaModel.project_id == project_id,
  87. ProjectQuotaModel.item_code.like(f"{item_code}%"),
  88. ProjectQuotaModel.is_del == 0,
  89. )
  90. )
  91. if keyword:
  92. query = query.filter(
  93. or_(
  94. ProjectQuotaModel.quota_code.like(f"%{keyword}%"),
  95. ProjectQuotaModel.entry_name.like(f"%{keyword}%"),
  96. ProjectQuotaModel.item_code.like(f"%{keyword}%"),
  97. )
  98. )
  99. if send_status is not None:
  100. query = query.filter(ProjectQuotaModel.send_status == send_status)
  101. # 计算总数
  102. total_count = query.count()
  103. # 分页
  104. query = (
  105. query.order_by(
  106. func.cast(func.substr(ProjectQuotaModel.ex_cell, 2), Integer).asc(),
  107. ProjectQuotaModel.sort.asc(),
  108. )
  109. .offset((page - 1) * page_size)
  110. .limit(page_size)
  111. )
  112. quotas = query.all()
  113. return {"total": total_count, "data": quotas}
  114. def get_quotas_by_task_id(self, task_id: int, with_quota_code: bool = False):
  115. with db_helper.mysql_query_session(self._database) as db_session:
  116. query = db_session.query(ProjectQuotaModel).filter(
  117. and_(
  118. ProjectQuotaModel.task_id == task_id, ProjectQuotaModel.is_del == 0
  119. )
  120. )
  121. if with_quota_code:
  122. query = query.filter(
  123. and_(
  124. ProjectQuotaModel.quota_code != None,
  125. ProjectQuotaModel.quota_code != "",
  126. )
  127. )
  128. quotas = query.all()
  129. return quotas
  130. def get_quota(self, quota_id: int) -> Optional[ProjectQuotaModel]:
  131. """根据ID查询定额
  132. Args:
  133. quota_id: 定额ID
  134. Returns:
  135. Optional[ProjectQuotaDto]
  136. """
  137. with db_helper.mysql_query_session(self._database) as db_session:
  138. quota = (
  139. db_session.query(ProjectQuotaModel)
  140. .filter(
  141. and_(
  142. ProjectQuotaModel.id == quota_id, ProjectQuotaModel.is_del == 0
  143. )
  144. )
  145. .first()
  146. )
  147. return quota
  148. def get_quota_dto(self, quota_id: int) -> Optional[ProjectQuotaDto]:
  149. """根据ID查询定额
  150. Args:
  151. quota_id: 定额ID
  152. Returns:
  153. Optional[ProjectQuotaDto]
  154. """
  155. quota = self.get_quota(quota_id)
  156. return ProjectQuotaDto.from_model(quota) if quota else None
  157. def get_quota_by_quota_input(
  158. self, project_id: str, budget_id: int, quota_input_id: int
  159. ):
  160. with db_helper.mysql_query_session(self._database) as db_session:
  161. quota = (
  162. db_session.query(ProjectQuotaModel)
  163. .filter(
  164. and_(
  165. ProjectQuotaModel.project_id == project_id,
  166. ProjectQuotaModel.budget_id == budget_id,
  167. ProjectQuotaModel.quota_id == quota_input_id,
  168. ProjectQuotaModel.is_del == 0,
  169. )
  170. )
  171. .first()
  172. )
  173. return ProjectQuotaDto.from_model(quota) if quota else None
  174. def create_quota(self, quota_dto: ProjectQuotaDto) -> ProjectQuotaDto:
  175. """创建定额
  176. Args:
  177. quota_dto: 定额DTO
  178. Returns:
  179. ProjectQuotaDto
  180. """
  181. with db_helper.mysql_session(self._database) as db_session:
  182. quota = ProjectQuotaModel(
  183. project_id=quota_dto.project_id,
  184. budget_id=quota_dto.budget_id,
  185. budget_code=quota_dto.budget_code,
  186. task_id=quota_dto.task_id,
  187. item_id=quota_dto.item_id,
  188. item_code=quota_dto.item_code,
  189. sort=quota_dto.sort,
  190. quota_id=quota_dto.quota_id,
  191. quota_code=quota_dto.quota_code,
  192. copy_quota_id=quota_dto.copy_quota_id,
  193. quota_adjustment=quota_dto.quota_adjustment,
  194. entry_name=quota_dto.entry_name,
  195. units=quota_dto.units,
  196. amount=quota_dto.amount,
  197. ex_file=quota_dto.ex_file,
  198. ex_cell=quota_dto.ex_cell,
  199. ex_row=quota_dto.ex_row,
  200. ex_unit=quota_dto.ex_unit,
  201. ex_amount=quota_dto.ex_amount,
  202. note=quota_dto.note,
  203. is_change=quota_dto.is_change,
  204. send_status=SendStatusEnum.NEW.value,
  205. send_time=quota_dto.send_time,
  206. send_error=quota_dto.send_error,
  207. created_by=quota_dto.created_by or self.current_user.username,
  208. created_at=datetime.now(),
  209. )
  210. db_session.add(quota)
  211. db_session.flush()
  212. return ProjectQuotaDto.from_model(quota)
  213. def update_quota(self, quota_dto: ProjectQuotaDto) -> Optional[ProjectQuotaDto]:
  214. """更新定额
  215. Args:
  216. quota_dto: 定额DTO
  217. Returns:
  218. Optional[ProjectQuotaDto]
  219. """
  220. quota = self.get_quota(quota_dto.id)
  221. if not quota:
  222. return None
  223. with db_helper.mysql_session(self._database) as db_session:
  224. quota = self._map_quota_dto(quota, quota_dto)
  225. quota = db_session.merge(quota)
  226. return ProjectQuotaDto.from_model(quota)
  227. def update_quota_all(self, quota_dto: ProjectQuotaDto) -> Optional[ProjectQuotaDto]:
  228. """更新定额
  229. Args:
  230. quota_dto: 定额DTO
  231. Returns:
  232. Optional[ProjectQuotaDto]
  233. """
  234. quota = self.get_quota(quota_dto.id)
  235. if not quota:
  236. return None
  237. with db_helper.mysql_session(self._database) as db_session:
  238. quota = self._map_quota_dto(quota, quota_dto)
  239. quota.item_code = quota_dto.item_code
  240. quota.item_id = quota_dto.item_id
  241. quota = db_session.merge(quota)
  242. return ProjectQuotaDto.from_model(quota)
  243. def _map_quota_dto(self, quota, quota_dto):
  244. quota.quota_id = quota_dto.quota_id
  245. quota.quota_code = quota_dto.quota_code
  246. quota.quota_adjustment = quota_dto.quota_adjustment
  247. quota.entry_name = quota_dto.entry_name
  248. quota.units = quota_dto.units
  249. quota.sort = quota_dto.sort
  250. quota.amount = quota_dto.amount
  251. quota.ex_file = quota_dto.ex_file
  252. quota.ex_cell = quota_dto.ex_cell
  253. quota.ex_row = quota_dto.ex_row
  254. quota.ex_unit = quota_dto.ex_unit
  255. quota.ex_amount = quota_dto.ex_amount
  256. quota.note = quota_dto.note
  257. quota.is_change = quota_dto.is_change
  258. quota.send_status = quota_dto.send_status
  259. quota.send_error = None
  260. quota.updated_by = self.current_user.username
  261. quota.updated_at = datetime.now()
  262. return quota
  263. def update_quota_id(self, id: int, quota_id: int):
  264. quota = self.get_quota(id)
  265. if not quota:
  266. raise Exception(f"定额条目[{id}]不存在")
  267. with db_helper.mysql_session(self._database) as db_session:
  268. quota.quota_id = quota_id
  269. quota.is_change = 0
  270. quota.copy_quota_id = -1 if quota.copy_quota_id > 0 else quota.copy_quota_id
  271. quota = db_session.merge(quota)
  272. return ProjectQuotaDto.from_model(quota)
  273. def update_quota_change(self, quota_id: int, is_change: bool):
  274. quota = self.get_quota(quota_id)
  275. if not quota:
  276. raise Exception(f"定额条目[{quota_id}]不存在")
  277. with db_helper.mysql_session(self._database) as db_session:
  278. if is_change:
  279. quota.is_change = 1
  280. quota.send_status = (
  281. SendStatusEnum.CHANGE.value
  282. if quota.send_status == SendStatusEnum.SUCCESS.value
  283. else quota.send_status
  284. )
  285. else:
  286. quota.is_change = 0
  287. quota = db_session.merge(quota)
  288. return ProjectQuotaDto.from_model(quota)
  289. def update_quota_chapter(
  290. self, quota_id: int, item_id: int, item_code: str
  291. ) -> Optional[ProjectQuotaDto]:
  292. quota = self.get_quota(quota_id)
  293. if not quota:
  294. raise Exception(f"定额条目[{quota_id}]不存在")
  295. with db_helper.mysql_session(self._database) as db_session:
  296. sort = self.get_next_sort(quota.project_id, quota.budget_id, item_id)
  297. quota.item_id = item_id
  298. quota.item_code = item_code
  299. quota.is_change = 2
  300. quota.sort = sort
  301. quota.send_status = (
  302. SendStatusEnum.CHANGE.value
  303. if quota.send_status == SendStatusEnum.SUCCESS.value
  304. else quota.send_status
  305. )
  306. quota.updated_by = self.current_user.username
  307. quota.updated_at = datetime.now()
  308. quota = db_session.merge(quota)
  309. return ProjectQuotaDto.from_model(quota)
  310. def delete_quota(self, quota_id: int) -> bool:
  311. """删除定额
  312. Args:
  313. quota_id: 定额ID
  314. Returns:
  315. bool
  316. """
  317. quota = self.get_quota(quota_id)
  318. if not quota:
  319. return False
  320. with db_helper.mysql_session(self._database) as db_session:
  321. quota.is_del = 1
  322. quota.deleted_by = self.current_user.username
  323. quota.deleted_at = datetime.now()
  324. db_session.merge(quota)
  325. return True
  326. def update_send_status(self, quota_id: int, status: int, err: str = None) -> bool:
  327. """
  328. 更新发送状态
  329. Args:
  330. quota_id: int
  331. status: int
  332. err: str
  333. Returns:
  334. bool
  335. """
  336. quota = self.get_quota(quota_id)
  337. if not quota:
  338. return False
  339. with db_helper.mysql_session(self._database) as db_session:
  340. quota.send_status = status
  341. quota.send_error = err
  342. quota.send_time = datetime.now()
  343. db_session.merge(quota)
  344. return True
  345. def update_quota_code(self, quota_dto: ProjectQuotaDto):
  346. """更新定额编号
  347. Args:
  348. quota_dto: 定额DTO
  349. Returns:
  350. bool
  351. """
  352. quota = self.get_quota(quota_dto.id)
  353. if not quota:
  354. return False
  355. with db_helper.mysql_session(self._database) as db_session:
  356. quota.quota_code = quota_dto.quota_code
  357. quota.unit = quota_dto.unit
  358. quota.unit_price = quota_dto.unit_price
  359. quota.total_price = quota_dto.total_price
  360. quota.updated_by = self.current_user.username
  361. quota.updated_at = datetime.now()
  362. db_session.merge(quota)
  363. return True
  364. def get_next_sort(self, project_id: str, budget_id: int, item_id: int):
  365. with db_helper.mysql_query_session(self._database) as db_session:
  366. num = (
  367. db_session.query(ProjectQuotaModel.sort)
  368. .filter(
  369. ProjectQuotaModel.project_id == project_id,
  370. ProjectQuotaModel.budget_id == budget_id,
  371. ProjectQuotaModel.item_id == item_id,
  372. )
  373. .order_by(ProjectQuotaModel.sort.desc())
  374. .first()
  375. )
  376. return num[0] + 1 if num and num[0] else 1
  377. def update_ge_sort(self, project_id: str, budget_id: int, item_id: int, sort: int):
  378. with db_helper.mysql_session(self._database) as db_session:
  379. db_session.query(ProjectQuotaModel).filter(
  380. ProjectQuotaModel.project_id == project_id,
  381. ProjectQuotaModel.budget_id == budget_id,
  382. ProjectQuotaModel.item_id == item_id,
  383. ProjectQuotaModel.sort >= sort,
  384. ).update({ProjectQuotaModel.sort: ProjectQuotaModel.sort + 1})