227 lines
8.9 KiB
Python
227 lines
8.9 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_LEGAL_USE_DOCUMENT = "pl:legal-use-document: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_LEGAL_USE_DOCUMENT,
|
|
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:
|
|
"""待处理用证配置或结转异常;不再读取人工齐备标记。"""
|
|
from sqlalchemy import or_, cast, String
|
|
from app.models.pl_models import PlPlanProduct
|
|
from app.models.pl_quota_models import PlPlanCertUsage
|
|
toxic = db.query(PlPlanProduct.id).filter(
|
|
PlPlanProduct.plan_id == PlPlan.id, PlPlanProduct.tenant_no == tenant_no,
|
|
PlPlanProduct.del_flag == 0, PlPlanProduct.is_toxic == 1).exists()
|
|
exception = db.query(PlPlanCertUsage.id).filter(
|
|
PlPlanCertUsage.plan_id == PlPlan.id, PlPlanCertUsage.tenant_no == tenant_no,
|
|
PlPlanCertUsage.del_flag == 0, or_(PlPlanCertUsage.usage_status == 'exception',
|
|
PlPlanCertUsage.confirmation_snapshot['settlementError'].as_string().isnot(None))).exists()
|
|
query = db.query(PlPlan).filter(
|
|
PlPlan.tenant_no == tenant_no, PlPlan.del_flag == 0,
|
|
PlPlan.follow_status != 'cancelled', toxic,
|
|
or_(PlPlan.cert_configuration.is_(None), cast(PlPlan.cert_configuration, String).in_(['null', '{}']), exception),
|
|
or_(PlPlan.archive_status != 'archived', exception),
|
|
)
|
|
return apply_pl_scope(query, PlPlan, user, db, tenant_no).count()
|
|
|
|
|
|
def count_legal_use_document_pending(db: Session, tenant_no: str, user) -> int:
|
|
"""独立资料待补数;归档不免除资料后补,不参与运输额度判断。"""
|
|
from sqlalchemy import or_
|
|
from app.models.pl_models import PlPlanProduct
|
|
pending = db.query(PlPlanProduct.id).filter(
|
|
PlPlanProduct.plan_id == PlPlan.id, PlPlanProduct.tenant_no == tenant_no,
|
|
PlPlanProduct.del_flag == 0, PlPlanProduct.is_explosive == 1,
|
|
or_(PlPlanProduct.legal_use_cert_url.is_(None), PlPlanProduct.legal_use_cert_url == ''),
|
|
).exists()
|
|
query = db.query(PlPlan).filter(PlPlan.tenant_no == tenant_no, PlPlan.del_flag == 0, pending)
|
|
return apply_pl_scope(query, PlPlan, user, db, tenant_no).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_LEGAL_USE_DOCUMENT in requested:
|
|
counts[MENU_CODE_LEGAL_USE_DOCUMENT] = count_legal_use_document_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
|