205 lines
7.4 KiB
Python
205 lines
7.4 KiB
Python
"""运输管理 - 侧栏菜单角标统计"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, List, Optional
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.bx_models import BxWorkflowNode
|
|
from app.models.pl_models import (
|
|
PlDocumentReminder,
|
|
PlFreightSettlement,
|
|
PlPlan,
|
|
PlVehicleMaintenanceApply,
|
|
PlVehicleRechargeApply,
|
|
PlVehicleUreaApply,
|
|
)
|
|
from app.services.pl_cert_service import scan_cert_expiry
|
|
from app.services.pl_data_scope import apply_pl_scope
|
|
from app.services.pl_freight_settlement_service import can_user_approve_settlement
|
|
MENU_CODE_DOCUMENT = "pl:document:list"
|
|
MENU_CODE_FREIGHT_APPROVAL = "pl:freight-approval:list"
|
|
MENU_CODE_FREIGHT_DISBURSE = "pl:freight-disburse:list"
|
|
MENU_CODE_RECHARGE_APPROVAL = "pl:vehicle-recharge-approval:list"
|
|
MENU_CODE_RECHARGE_RECORD = "pl:vehicle-recharge-record:list"
|
|
MENU_CODE_MAINT_PROCESS = "pl:vehicle-maintenance-process:list"
|
|
MENU_CODE_UREA_DISBURSE = "pl:vehicle-urea-disburse:list"
|
|
MENU_CODE_PLAN_CERT = "pl:plan-cert:list"
|
|
MENU_CODE_PLAN_ARCHIVE = "pl:plan-archive:list"
|
|
MENU_CODE_PLAN_CONTRACT = "pl:plan-contract:list"
|
|
|
|
SUPPORTED_MENU_CODES = {
|
|
MENU_CODE_DOCUMENT,
|
|
MENU_CODE_FREIGHT_APPROVAL,
|
|
MENU_CODE_FREIGHT_DISBURSE,
|
|
MENU_CODE_RECHARGE_APPROVAL,
|
|
MENU_CODE_RECHARGE_RECORD,
|
|
MENU_CODE_MAINT_PROCESS,
|
|
MENU_CODE_UREA_DISBURSE,
|
|
MENU_CODE_PLAN_CERT,
|
|
MENU_CODE_PLAN_ARCHIVE,
|
|
MENU_CODE_PLAN_CONTRACT,
|
|
}
|
|
|
|
|
|
def _count_pending_applies(db: Session, tenant_no: str, user, model, *extra_filters) -> int:
|
|
query = db.query(model).filter(
|
|
model.tenant_no == tenant_no,
|
|
model.del_flag == 0,
|
|
model.status == "pending",
|
|
*extra_filters,
|
|
)
|
|
query = apply_pl_scope(query, model, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_document_reminder_expired_pending(db: Session, tenant_no: str, user) -> int:
|
|
scan_cert_expiry(db, tenant_no)
|
|
db.flush()
|
|
query = db.query(PlDocumentReminder).filter(
|
|
PlDocumentReminder.tenant_no == tenant_no,
|
|
PlDocumentReminder.del_flag == 0,
|
|
PlDocumentReminder.status == "pending",
|
|
PlDocumentReminder.days_remaining <= 0,
|
|
)
|
|
query = apply_pl_scope(query, PlDocumentReminder, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_freight_approval_pending(db: Session, tenant_no: str, user_id: str) -> int:
|
|
if not user_id:
|
|
return 0
|
|
rows = (
|
|
db.query(PlFreightSettlement.id)
|
|
.filter(
|
|
PlFreightSettlement.tenant_no == tenant_no,
|
|
PlFreightSettlement.del_flag == 0,
|
|
PlFreightSettlement.settlement_status == "approving",
|
|
)
|
|
.all()
|
|
)
|
|
total = 0
|
|
for (settlement_id,) in rows:
|
|
if can_user_approve_settlement(db, settlement_id, user_id):
|
|
total += 1
|
|
return total
|
|
|
|
|
|
def count_plan_cert_pending(db: Session, tenant_no: str, user) -> int:
|
|
"""危化品证待办角标:未齐备且含危化产品的未归档计划(SQL EXISTS,避免全表拉入内存)。"""
|
|
from app.services.pl_plan_service import plan_has_hazard_product_exists
|
|
|
|
query = db.query(PlPlan).filter(
|
|
PlPlan.tenant_no == tenant_no,
|
|
PlPlan.del_flag == 0,
|
|
PlPlan.hazard_cert_ready == 0,
|
|
PlPlan.archive_status != "archived",
|
|
PlPlan.follow_status != "cancelled",
|
|
plan_has_hazard_product_exists(tenant_no),
|
|
)
|
|
query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_plan_archive_pending(db: Session, tenant_no: str, user) -> int:
|
|
"""待归档角标:与归档列表一致,仅统计已安排运输且未归档的计划。"""
|
|
query = db.query(PlPlan).filter(
|
|
PlPlan.tenant_no == tenant_no,
|
|
PlPlan.del_flag == 0,
|
|
PlPlan.archive_status == "unarchived",
|
|
PlPlan.follow_status != "cancelled",
|
|
PlPlan.transport_status == "arranged",
|
|
)
|
|
query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_plan_contract_gaps(db: Session, tenant_no: str, user) -> int:
|
|
from app.services.pl_plan_contract_gap import plan_has_contract_gap_exists
|
|
|
|
query = db.query(PlPlan).filter(
|
|
PlPlan.tenant_no == tenant_no,
|
|
PlPlan.del_flag == 0,
|
|
PlPlan.follow_status != "cancelled",
|
|
plan_has_contract_gap_exists(tenant_no),
|
|
)
|
|
query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_freight_disburse_pending(db: Session, tenant_no: str, user) -> int:
|
|
query = db.query(PlFreightSettlement).filter(
|
|
PlFreightSettlement.tenant_no == tenant_no,
|
|
PlFreightSettlement.del_flag == 0,
|
|
PlFreightSettlement.settlement_status == "approved",
|
|
)
|
|
query = apply_pl_scope(query, PlFreightSettlement, user, db, tenant_no)
|
|
return query.count()
|
|
|
|
|
|
def count_recharge_approval_pending(db: Session, tenant_no: str, user_id: str) -> int:
|
|
"""与充值审批待办列表一致:当前用户为当前节点审批人的审批中单据。"""
|
|
if not user_id:
|
|
return 0
|
|
return (
|
|
db.query(PlVehicleRechargeApply)
|
|
.join(
|
|
BxWorkflowNode,
|
|
BxWorkflowNode.id == PlVehicleRechargeApply.current_node,
|
|
)
|
|
.filter(
|
|
PlVehicleRechargeApply.tenant_no == tenant_no,
|
|
PlVehicleRechargeApply.del_flag == 0,
|
|
PlVehicleRechargeApply.status == "in_approval",
|
|
BxWorkflowNode.tenant_no == tenant_no,
|
|
BxWorkflowNode.del_flag == 0,
|
|
BxWorkflowNode.approver_id == user_id,
|
|
)
|
|
.count()
|
|
)
|
|
|
|
|
|
def build_menu_badge_counts(
|
|
db: Session,
|
|
*,
|
|
tenant_no: str,
|
|
user,
|
|
user_id: str,
|
|
menu_codes: Optional[List[str]] = None,
|
|
) -> Dict[str, int]:
|
|
requested = {c.strip() for c in (menu_codes or []) if c and c.strip()}
|
|
if not requested:
|
|
requested = set(SUPPORTED_MENU_CODES)
|
|
requested &= SUPPORTED_MENU_CODES
|
|
|
|
counts: Dict[str, int] = {}
|
|
if MENU_CODE_DOCUMENT in requested:
|
|
counts[MENU_CODE_DOCUMENT] = count_document_reminder_expired_pending(db, tenant_no, user)
|
|
if MENU_CODE_FREIGHT_APPROVAL in requested:
|
|
counts[MENU_CODE_FREIGHT_APPROVAL] = count_freight_approval_pending(db, tenant_no, user_id)
|
|
if MENU_CODE_FREIGHT_DISBURSE in requested:
|
|
counts[MENU_CODE_FREIGHT_DISBURSE] = count_freight_disburse_pending(db, tenant_no, user)
|
|
if MENU_CODE_RECHARGE_APPROVAL in requested:
|
|
counts[MENU_CODE_RECHARGE_APPROVAL] = count_recharge_approval_pending(
|
|
db, tenant_no, user_id
|
|
)
|
|
if MENU_CODE_RECHARGE_RECORD in requested:
|
|
counts[MENU_CODE_RECHARGE_RECORD] = _count_pending_applies(
|
|
db, tenant_no, user, PlVehicleRechargeApply
|
|
)
|
|
if MENU_CODE_MAINT_PROCESS in requested:
|
|
counts[MENU_CODE_MAINT_PROCESS] = _count_pending_applies(
|
|
db, tenant_no, user, PlVehicleMaintenanceApply
|
|
)
|
|
if MENU_CODE_UREA_DISBURSE in requested:
|
|
counts[MENU_CODE_UREA_DISBURSE] = _count_pending_applies(
|
|
db, tenant_no, user, PlVehicleUreaApply
|
|
)
|
|
if MENU_CODE_PLAN_CERT in requested:
|
|
counts[MENU_CODE_PLAN_CERT] = count_plan_cert_pending(db, tenant_no, user)
|
|
if MENU_CODE_PLAN_ARCHIVE in requested:
|
|
counts[MENU_CODE_PLAN_ARCHIVE] = count_plan_archive_pending(db, tenant_no, user)
|
|
if MENU_CODE_PLAN_CONTRACT in requested:
|
|
counts[MENU_CODE_PLAN_CONTRACT] = count_plan_contract_gaps(db, tenant_no, user)
|
|
return counts
|