pl_menu_badge_service.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. """运输管理 - 侧栏菜单角标统计"""
  2. from __future__ import annotations
  3. from collections import defaultdict
  4. from typing import Dict, List, Optional
  5. from sqlalchemy.orm import Session
  6. from app.models.pl_models import (
  7. PlDocumentReminder,
  8. PlFreightSettlement,
  9. PlPlan,
  10. PlPlanProduct,
  11. PlVehicleMaintenanceApply,
  12. PlVehicleRechargeApply,
  13. PlVehicleUreaApply,
  14. )
  15. from app.services.pl_cert_service import scan_cert_expiry
  16. from app.services.pl_data_scope import apply_pl_scope
  17. from app.services.pl_freight_settlement_service import can_user_approve_settlement
  18. from app.services.pl_plan_service import plan_has_hazard_products
  19. MENU_CODE_DOCUMENT = "pl:document:list"
  20. MENU_CODE_FREIGHT_APPROVAL = "pl:freight-approval:list"
  21. MENU_CODE_FREIGHT_DISBURSE = "pl:freight-disburse:list"
  22. MENU_CODE_RECHARGE_RECORD = "pl:vehicle-recharge-record:list"
  23. MENU_CODE_MAINT_PROCESS = "pl:vehicle-maintenance-process:list"
  24. MENU_CODE_UREA_DISBURSE = "pl:vehicle-urea-disburse:list"
  25. MENU_CODE_PLAN_CERT = "pl:plan-cert:list"
  26. MENU_CODE_PLAN_ARCHIVE = "pl:plan-archive:list"
  27. SUPPORTED_MENU_CODES = {
  28. MENU_CODE_DOCUMENT,
  29. MENU_CODE_FREIGHT_APPROVAL,
  30. MENU_CODE_FREIGHT_DISBURSE,
  31. MENU_CODE_RECHARGE_RECORD,
  32. MENU_CODE_MAINT_PROCESS,
  33. MENU_CODE_UREA_DISBURSE,
  34. MENU_CODE_PLAN_CERT,
  35. MENU_CODE_PLAN_ARCHIVE,
  36. }
  37. def _count_pending_applies(db: Session, tenant_no: str, user, model) -> int:
  38. query = db.query(model).filter(
  39. model.tenant_no == tenant_no,
  40. model.del_flag == 0,
  41. model.status == "pending",
  42. )
  43. query = apply_pl_scope(query, model, user, db, tenant_no)
  44. return query.count()
  45. def count_document_reminder_expired_pending(db: Session, tenant_no: str, user) -> int:
  46. scan_cert_expiry(db, tenant_no)
  47. db.flush()
  48. query = db.query(PlDocumentReminder).filter(
  49. PlDocumentReminder.tenant_no == tenant_no,
  50. PlDocumentReminder.del_flag == 0,
  51. PlDocumentReminder.status == "pending",
  52. PlDocumentReminder.days_remaining <= 0,
  53. )
  54. query = apply_pl_scope(query, PlDocumentReminder, user, db, tenant_no)
  55. return query.count()
  56. def count_freight_approval_pending(db: Session, tenant_no: str, user_id: str) -> int:
  57. if not user_id:
  58. return 0
  59. rows = (
  60. db.query(PlFreightSettlement.id)
  61. .filter(
  62. PlFreightSettlement.tenant_no == tenant_no,
  63. PlFreightSettlement.del_flag == 0,
  64. PlFreightSettlement.settlement_status == "approving",
  65. )
  66. .all()
  67. )
  68. total = 0
  69. for (settlement_id,) in rows:
  70. if can_user_approve_settlement(db, settlement_id, user_id):
  71. total += 1
  72. return total
  73. def count_plan_cert_pending(db: Session, tenant_no: str, user) -> int:
  74. query = db.query(PlPlan).filter(
  75. PlPlan.tenant_no == tenant_no,
  76. PlPlan.del_flag == 0,
  77. PlPlan.hazard_cert_ready == 0,
  78. PlPlan.archive_status != "archived",
  79. PlPlan.follow_status != "cancelled",
  80. )
  81. query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
  82. plans = query.all()
  83. if not plans:
  84. return 0
  85. plan_ids = [plan.id for plan in plans]
  86. products_by_plan: dict[str, list[PlPlanProduct]] = defaultdict(list)
  87. product_rows = (
  88. db.query(PlPlanProduct)
  89. .filter(
  90. PlPlanProduct.plan_id.in_(plan_ids),
  91. PlPlanProduct.del_flag == 0,
  92. )
  93. .all()
  94. )
  95. for product in product_rows:
  96. products_by_plan[str(product.plan_id)].append(product)
  97. total = 0
  98. for plan in plans:
  99. products = products_by_plan.get(str(plan.id), [])
  100. if plan_has_hazard_products(db, tenant_no, products):
  101. total += 1
  102. return total
  103. def count_plan_archive_pending(db: Session, tenant_no: str, user) -> int:
  104. query = db.query(PlPlan).filter(
  105. PlPlan.tenant_no == tenant_no,
  106. PlPlan.del_flag == 0,
  107. PlPlan.archive_status == "unarchived",
  108. PlPlan.follow_status != "cancelled",
  109. )
  110. query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
  111. return query.count()
  112. def count_freight_disburse_pending(db: Session, tenant_no: str, user) -> int:
  113. query = db.query(PlFreightSettlement).filter(
  114. PlFreightSettlement.tenant_no == tenant_no,
  115. PlFreightSettlement.del_flag == 0,
  116. PlFreightSettlement.settlement_status == "approved",
  117. )
  118. query = apply_pl_scope(query, PlFreightSettlement, user, db, tenant_no)
  119. return query.count()
  120. def build_menu_badge_counts(
  121. db: Session,
  122. *,
  123. tenant_no: str,
  124. user,
  125. user_id: str,
  126. menu_codes: Optional[List[str]] = None,
  127. ) -> Dict[str, int]:
  128. requested = {c.strip() for c in (menu_codes or []) if c and c.strip()}
  129. if not requested:
  130. requested = set(SUPPORTED_MENU_CODES)
  131. requested &= SUPPORTED_MENU_CODES
  132. counts: Dict[str, int] = {}
  133. if MENU_CODE_DOCUMENT in requested:
  134. counts[MENU_CODE_DOCUMENT] = count_document_reminder_expired_pending(db, tenant_no, user)
  135. if MENU_CODE_FREIGHT_APPROVAL in requested:
  136. counts[MENU_CODE_FREIGHT_APPROVAL] = count_freight_approval_pending(db, tenant_no, user_id)
  137. if MENU_CODE_FREIGHT_DISBURSE in requested:
  138. counts[MENU_CODE_FREIGHT_DISBURSE] = count_freight_disburse_pending(db, tenant_no, user)
  139. if MENU_CODE_RECHARGE_RECORD in requested:
  140. counts[MENU_CODE_RECHARGE_RECORD] = _count_pending_applies(
  141. db, tenant_no, user, PlVehicleRechargeApply
  142. )
  143. if MENU_CODE_MAINT_PROCESS in requested:
  144. counts[MENU_CODE_MAINT_PROCESS] = _count_pending_applies(
  145. db, tenant_no, user, PlVehicleMaintenanceApply
  146. )
  147. if MENU_CODE_UREA_DISBURSE in requested:
  148. counts[MENU_CODE_UREA_DISBURSE] = _count_pending_applies(
  149. db, tenant_no, user, PlVehicleUreaApply
  150. )
  151. if MENU_CODE_PLAN_CERT in requested:
  152. counts[MENU_CODE_PLAN_CERT] = count_plan_cert_pending(db, tenant_no, user)
  153. if MENU_CODE_PLAN_ARCHIVE in requested:
  154. counts[MENU_CODE_PLAN_ARCHIVE] = count_plan_archive_pending(db, tenant_no, user)
  155. return counts