|
|
@@ -22,6 +22,7 @@ from app.schemas.pl.vehicle_recharge import (
|
|
|
VehicleRechargeApplyCreate,
|
|
|
VehicleRechargeApplyRead,
|
|
|
VehicleRechargeApplyUpdate,
|
|
|
+ VehicleRechargeApprovalFlowItem,
|
|
|
VehicleRechargeApprovalRead,
|
|
|
VehicleRechargeDisburseBoardItem,
|
|
|
VehicleRechargeRecordCreate,
|
|
|
@@ -29,6 +30,7 @@ from app.schemas.pl.vehicle_recharge import (
|
|
|
VehicleRechargeRecordUpdate,
|
|
|
)
|
|
|
from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
|
|
|
+from app.services.pl_vehicle_mine_vehicles import license_plates_for_user_personnel
|
|
|
|
|
|
_EDITABLE_STATUSES = frozenset({"draft", "rejected"})
|
|
|
_BIZ_TYPE_VEHICLE_RECHARGE = "vehicle_recharge"
|
|
|
@@ -115,7 +117,12 @@ def _resolve_applicant_id(
|
|
|
|
|
|
|
|
|
def _get_apply_row(
|
|
|
- db: Session, tenant_no: str, apply_id: str, user: DbUserContext
|
|
|
+ db: Session,
|
|
|
+ tenant_no: str,
|
|
|
+ apply_id: str,
|
|
|
+ user: DbUserContext,
|
|
|
+ *,
|
|
|
+ mine_vehicles: bool = False,
|
|
|
) -> PlVehicleRechargeApply:
|
|
|
row = (
|
|
|
db.query(PlVehicleRechargeApply)
|
|
|
@@ -128,7 +135,12 @@ def _get_apply_row(
|
|
|
)
|
|
|
if not row:
|
|
|
raise HTTPException(status_code=404, detail="申请单不存在")
|
|
|
- assert_pl_row_in_scope(db, row, PlVehicleRechargeApply, user, tenant_no)
|
|
|
+ if mine_vehicles:
|
|
|
+ plates = license_plates_for_user_personnel(db, tenant_no, user.id)
|
|
|
+ if row.license_plate not in plates:
|
|
|
+ raise HTTPException(status_code=403, detail="无权查看该申请")
|
|
|
+ else:
|
|
|
+ assert_pl_row_in_scope(db, row, PlVehicleRechargeApply, user, tenant_no)
|
|
|
return row
|
|
|
|
|
|
|
|
|
@@ -178,10 +190,10 @@ def approval_to_read(row: PlVehicleRechargeApproval) -> VehicleRechargeApprovalR
|
|
|
)
|
|
|
|
|
|
|
|
|
-def _list_approvals(
|
|
|
+def _list_approval_rows(
|
|
|
db: Session, tenant_no: str, apply_id: str
|
|
|
-) -> list[VehicleRechargeApprovalRead]:
|
|
|
- rows = (
|
|
|
+) -> list[PlVehicleRechargeApproval]:
|
|
|
+ return (
|
|
|
db.query(PlVehicleRechargeApproval)
|
|
|
.filter(
|
|
|
PlVehicleRechargeApproval.apply_id == apply_id,
|
|
|
@@ -195,7 +207,93 @@ def _list_approvals(
|
|
|
)
|
|
|
.all()
|
|
|
)
|
|
|
- return [approval_to_read(row) for row in rows]
|
|
|
+
|
|
|
+
|
|
|
+def _list_approvals(
|
|
|
+ db: Session, tenant_no: str, apply_id: str
|
|
|
+) -> list[VehicleRechargeApprovalRead]:
|
|
|
+ return [approval_to_read(row) for row in _list_approval_rows(db, tenant_no, apply_id)]
|
|
|
+
|
|
|
+
|
|
|
+def _approval_flow_item_from_row(
|
|
|
+ row: PlVehicleRechargeApproval,
|
|
|
+ *,
|
|
|
+ node_order: int,
|
|
|
+) -> VehicleRechargeApprovalFlowItem:
|
|
|
+ return VehicleRechargeApprovalFlowItem(
|
|
|
+ id=row.id,
|
|
|
+ applyId=row.apply_id,
|
|
|
+ nodeName=row.node_name or "节点",
|
|
|
+ nodeOrder=node_order,
|
|
|
+ approvalRound=row.approval_round,
|
|
|
+ approvalStatus=row.approval_status,
|
|
|
+ approvalOpinion=row.approval_opinion,
|
|
|
+ approverName=row.approver_name or "",
|
|
|
+ approvalTime=row.approval_time,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def _build_approval_flow(
|
|
|
+ db: Session,
|
|
|
+ row: PlVehicleRechargeApply,
|
|
|
+ approval_rows: list[PlVehicleRechargeApproval],
|
|
|
+) -> list[VehicleRechargeApprovalFlowItem]:
|
|
|
+ """当前轮:流程全部节点;已审带信息,未审为 pending。"""
|
|
|
+ if not row.wf_id or not (row.approval_round or 0):
|
|
|
+ return []
|
|
|
+ nodes = _workflow_nodes(db, row.wf_id, row.tenant_no)
|
|
|
+ if not nodes:
|
|
|
+ return []
|
|
|
+ current_round = row.approval_round or 0
|
|
|
+ round_approvals = [a for a in approval_rows if (a.approval_round or 0) == current_round]
|
|
|
+ by_node_id: dict[str, PlVehicleRechargeApproval] = {}
|
|
|
+ for a in round_approvals:
|
|
|
+ if not a.node_id:
|
|
|
+ continue
|
|
|
+ existing = by_node_id.get(a.node_id)
|
|
|
+ if existing is None or (a.approval_time or datetime.min) >= (
|
|
|
+ existing.approval_time or datetime.min
|
|
|
+ ):
|
|
|
+ by_node_id[a.node_id] = a
|
|
|
+ result: list[VehicleRechargeApprovalFlowItem] = []
|
|
|
+ for node in nodes:
|
|
|
+ approval = by_node_id.get(node.id)
|
|
|
+ if approval:
|
|
|
+ result.append(
|
|
|
+ _approval_flow_item_from_row(approval, node_order=node.node_order or 0)
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ result.append(
|
|
|
+ VehicleRechargeApprovalFlowItem(
|
|
|
+ id="0",
|
|
|
+ applyId=row.id,
|
|
|
+ nodeName=node.node_name,
|
|
|
+ nodeOrder=node.node_order or 0,
|
|
|
+ approvalRound=current_round,
|
|
|
+ approvalStatus="pending",
|
|
|
+ approvalOpinion=None,
|
|
|
+ approverName=node.approver_name or "",
|
|
|
+ approvalTime=None,
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return result
|
|
|
+
|
|
|
+
|
|
|
+def _build_approval_history(
|
|
|
+ db: Session,
|
|
|
+ row: PlVehicleRechargeApply,
|
|
|
+ approval_rows: list[PlVehicleRechargeApproval],
|
|
|
+) -> list[VehicleRechargeApprovalFlowItem]:
|
|
|
+ """全部历史审批动作(含打回前各轮),按时间正序。"""
|
|
|
+ node_order_map: dict[str, int] = {}
|
|
|
+ if row.wf_id:
|
|
|
+ for node in _workflow_nodes(db, row.wf_id, row.tenant_no):
|
|
|
+ node_order_map[node.id] = node.node_order or 0
|
|
|
+ result: list[VehicleRechargeApprovalFlowItem] = []
|
|
|
+ for idx, a in enumerate(approval_rows):
|
|
|
+ order = node_order_map.get(a.node_id or "", 0) or (idx + 1)
|
|
|
+ result.append(_approval_flow_item_from_row(a, node_order=order))
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
def _get_linked_record(
|
|
|
@@ -219,6 +317,8 @@ def apply_to_read(
|
|
|
include_detail: bool = False,
|
|
|
) -> VehicleRechargeApplyRead:
|
|
|
approvals: list[VehicleRechargeApprovalRead] = []
|
|
|
+ approval_flow: list[VehicleRechargeApprovalFlowItem] = []
|
|
|
+ approval_history: list[VehicleRechargeApprovalFlowItem] = []
|
|
|
record_read: Optional[VehicleRechargeRecordRead] = None
|
|
|
current_node_name: Optional[str] = None
|
|
|
current_node_action_type: Optional[str] = None
|
|
|
@@ -235,7 +335,10 @@ def apply_to_read(
|
|
|
current_node_name = node.node_name
|
|
|
current_node_action_type = (node.action_type or "approve").strip() or "approve"
|
|
|
if include_detail:
|
|
|
- approvals = _list_approvals(db, row.tenant_no, row.id)
|
|
|
+ approval_rows = _list_approval_rows(db, row.tenant_no, row.id)
|
|
|
+ approvals = [approval_to_read(a) for a in approval_rows]
|
|
|
+ approval_flow = _build_approval_flow(db, row, approval_rows)
|
|
|
+ approval_history = _build_approval_history(db, row, approval_rows)
|
|
|
record_row = _get_linked_record(db, row.tenant_no, row.id)
|
|
|
if record_row:
|
|
|
record_read = record_to_read(record_row, db, apply_no=row.apply_no)
|
|
|
@@ -256,6 +359,8 @@ def apply_to_read(
|
|
|
currentNodeActionType=current_node_action_type,
|
|
|
approvalRound=row.approval_round or 0,
|
|
|
approvals=approvals,
|
|
|
+ approvalFlow=approval_flow,
|
|
|
+ approvalHistory=approval_history,
|
|
|
record=record_read,
|
|
|
remark=row.remark,
|
|
|
createTime=row.create_time,
|
|
|
@@ -343,7 +448,7 @@ def _resolve_apply_for_record(
|
|
|
if apply.status == "in_approval":
|
|
|
raise HTTPException(
|
|
|
status_code=400,
|
|
|
- detail="审批中的申请请在「充值审批」中完成发放登记",
|
|
|
+ detail="审批中的申请请在「ETC/加油充值审批」中完成发放登记",
|
|
|
)
|
|
|
if apply.status != "pending":
|
|
|
raise HTTPException(status_code=400, detail="仅待发放申请可关联")
|
|
|
@@ -389,6 +494,7 @@ def list_applies(
|
|
|
max_page_size: Optional[int] = None,
|
|
|
sort_field: Optional[str] = None,
|
|
|
sort_order: Optional[str] = None,
|
|
|
+ mine_vehicles: bool = False,
|
|
|
) -> tuple[list[VehicleRechargeApplyRead], int]:
|
|
|
query = db.query(PlVehicleRechargeApply).filter(
|
|
|
PlVehicleRechargeApply.tenant_no == tenant_no,
|
|
|
@@ -424,7 +530,13 @@ def list_applies(
|
|
|
if ids:
|
|
|
query = query.filter(PlVehicleRechargeApply.id.in_(ids))
|
|
|
|
|
|
- query = apply_pl_scope(query, PlVehicleRechargeApply, user, db, tenant_no)
|
|
|
+ if mine_vehicles:
|
|
|
+ plates = license_plates_for_user_personnel(db, tenant_no, user.id)
|
|
|
+ if not plates:
|
|
|
+ return [], 0
|
|
|
+ query = query.filter(PlVehicleRechargeApply.license_plate.in_(plates))
|
|
|
+ else:
|
|
|
+ query = apply_pl_scope(query, PlVehicleRechargeApply, user, db, tenant_no)
|
|
|
total = query.count()
|
|
|
page = max(1, page)
|
|
|
cap = int(max_page_size) if max_page_size else 500
|
|
|
@@ -439,9 +551,16 @@ def list_applies(
|
|
|
|
|
|
|
|
|
def get_apply(
|
|
|
- db: Session, tenant_no: str, user: DbUserContext, apply_id: str
|
|
|
+ db: Session,
|
|
|
+ tenant_no: str,
|
|
|
+ user: DbUserContext,
|
|
|
+ apply_id: str,
|
|
|
+ *,
|
|
|
+ mine_vehicles: bool = False,
|
|
|
) -> VehicleRechargeApplyRead:
|
|
|
- row = _get_apply_row(db, tenant_no, apply_id, user)
|
|
|
+ row = _get_apply_row(
|
|
|
+ db, tenant_no, apply_id, user, mine_vehicles=mine_vehicles
|
|
|
+ )
|
|
|
return apply_to_read(row, db, include_detail=True)
|
|
|
|
|
|
|
|
|
@@ -658,6 +777,7 @@ def board_item_to_read(
|
|
|
amount=record_row.amount if record_row else None,
|
|
|
recordCreateByName=_resolve_user_name(db, record_row.create_by) if record_row else None,
|
|
|
recordRemark=record_row.remark if record_row else None,
|
|
|
+ recordAttachmentUrls=record_row.attachment_urls if record_row else None,
|
|
|
)
|
|
|
|
|
|
|