diff --git a/backend/app/schemas/pl/vehicle_recharge.py b/backend/app/schemas/pl/vehicle_recharge.py index c96f2d400..2b8789206 100644 --- a/backend/app/schemas/pl/vehicle_recharge.py +++ b/backend/app/schemas/pl/vehicle_recharge.py @@ -6,7 +6,7 @@ from typing import List, Optional from pydantic import BaseModel, Field, field_validator RECHARGE_TYPES = {"etc", "fuel", "shanxia", "bailian"} -APPLY_STATUSES = {"draft", "in_approval", "rejected", "disbursed", "cancelled"} +APPLY_STATUSES = {"draft", "in_approval", "pending", "rejected", "disbursed", "cancelled"} NODE_ACTION_TYPES = {"approve", "disburse"} APPROVAL_STATUSES = {"approved", "rejected", "disbursed"} diff --git a/backend/app/services/pl_vehicle_recharge_approval_service.py b/backend/app/services/pl_vehicle_recharge_approval_service.py index 3b0d11259..074ec606c 100644 --- a/backend/app/services/pl_vehicle_recharge_approval_service.py +++ b/backend/app/services/pl_vehicle_recharge_approval_service.py @@ -58,8 +58,8 @@ def _current_node( user: DbUserContext, tenant_no: str, ) -> BxWorkflowNode: - if apply.status != "in_approval" or not apply.current_node: - raise HTTPException(status_code=400, detail="申请单不在审批中") + if apply.status not in ("in_approval", "pending") or not apply.current_node: + raise HTTPException(status_code=400, detail="申请单不在可审批/发放状态") node = ( db.query(BxWorkflowNode) .filter( @@ -145,7 +145,7 @@ def list_pending( .filter( PlVehicleRechargeApply.tenant_no == tenant_no, PlVehicleRechargeApply.del_flag == 0, - PlVehicleRechargeApply.status == "in_approval", + PlVehicleRechargeApply.status.in_(("in_approval", "pending")), BxWorkflowNode.tenant_no == tenant_no, BxWorkflowNode.del_flag == 0, BxWorkflowNode.approver_id == user.id, @@ -294,7 +294,11 @@ def approve_apply( opinion=opinion, now=now, ) - apply.current_node = nodes[idx + 1].id + next_node = nodes[idx + 1] + apply.current_node = next_node.id + next_action = (next_node.action_type or "approve").strip() or "approve" + if next_action == "disburse": + apply.status = "pending" apply.update_by = user.id apply.update_time = now sync_approval_notifications( diff --git a/backend/tests/test_pl_vehicle_recharge_workflow.py b/backend/tests/test_pl_vehicle_recharge_workflow.py index 54879fee7..5faea9867 100644 --- a/backend/tests/test_pl_vehicle_recharge_workflow.py +++ b/backend/tests/test_pl_vehicle_recharge_workflow.py @@ -20,7 +20,7 @@ from app.schemas.pl.vehicle_recharge import APPLY_STATUSES, NODE_ACTION_TYPES, R def test_apply_statuses_include_workflow_states(): - assert {"draft", "in_approval", "rejected", "disbursed", "cancelled"} <= APPLY_STATUSES + assert {"draft", "in_approval", "pending", "rejected", "disbursed", "cancelled"} <= APPLY_STATUSES assert {"approve", "disburse"} <= NODE_ACTION_TYPES assert {"etc", "fuel"} <= RECHARGE_TYPES @@ -98,6 +98,40 @@ def _seed_workflow(db) -> tuple[str, str, str]: return wf_id, node_gm, node_fin +def _seed_three_node_workflow(db) -> tuple[str, str, str, str]: + wf_id = "wf-3" + n1, n2, n3 = "node-a1", "node-a2", "node-dis" + db.add( + BxWorkflowDef( + id=wf_id, + biz_type="vehicle_recharge", + wf_name="三级充值流程", + status=1, + tenant_no="t1", + create_dept_id="d1", + ) + ) + for nid, name, order, uid, uname, action in ( + (n1, "一审", 1, "u-a1", "一审", "approve"), + (n2, "二审", 2, "u-a2", "二审", "approve"), + (n3, "发放", 3, "u-fin", "财务", "disburse"), + ): + db.add( + BxWorkflowNode( + id=nid, + wf_id=wf_id, + node_name=name, + node_order=order, + approver_id=uid, + approver_name=uname, + action_type=action, + tenant_no="t1", + create_dept_id="d1", + ) + ) + return wf_id, n1, n2, n3 + + def _patch_scope(monkeypatch, *modules) -> None: for mod in modules: monkeypatch.setattr(mod, "apply_pl_scope", lambda query, *_a, **_k: query) @@ -272,7 +306,7 @@ def test_approve_advances_to_next_node(monkeypatch): result = approval_svc.approve_apply( db, "t1", _user("u-gm", "总经理"), "apply-1", opinion="同意" ) - assert result.status == "in_approval" + assert result.status == "pending" assert result.current_node == node_fin assert any(a.approval_status == "approved" for a in result.approvals) assert len(result.approval_flow) == 2 @@ -285,6 +319,79 @@ def test_approve_advances_to_next_node(monkeypatch): assert result.approval_history[0].approval_status == "approved" +def test_mid_approve_stays_in_approval_final_approve_sets_pending(monkeypatch): + from app.services import pl_vehicle_recharge_approval_service as approval_svc + from app.services import pl_vehicle_recharge_service as svc + + db = _make_session() + _seed_vehicle(db) + wf_id, n1, n2, n3 = _seed_three_node_workflow(db) + db.add( + PlVehicleRechargeApply( + id="apply-1", + apply_no="YK2026092601", + license_plate="鄂A00001", + recharge_type="fuel", + apply_date=date(2026, 9, 26), + applicant_id="u-applicant", + status="in_approval", + wf_id=wf_id, + current_node=n1, + approval_round=1, + tenant_no="t1", + create_dept_id="d1", + ) + ) + db.commit() + _patch_scope(monkeypatch, svc) + + mid = approval_svc.approve_apply(db, "t1", _user("u-a1", "一审"), "apply-1", opinion="ok") + assert mid.status == "in_approval" + assert mid.current_node == n2 + + final = approval_svc.approve_apply(db, "t1", _user("u-a2", "二审"), "apply-1", opinion="ok") + assert final.status == "pending" + assert final.current_node == n3 + + +def test_disburse_accepts_pending_status(monkeypatch): + from app.services import pl_vehicle_recharge_approval_service as approval_svc + from app.services import pl_vehicle_recharge_service as svc + from app.schemas.pl.vehicle_recharge import VehicleRechargeRecordCreate + + db = _make_session() + _seed_vehicle(db) + wf_id, _gm, node_fin = _seed_workflow(db) + db.add( + PlVehicleRechargeApply( + id="apply-1", + apply_no="ETC2026092601", + license_plate="鄂A00001", + recharge_type="etc", + apply_date=date(2026, 9, 26), + applicant_id="u-applicant", + status="pending", + wf_id=wf_id, + current_node=node_fin, + approval_round=1, + tenant_no="t1", + create_dept_id="d1", + ) + ) + db.commit() + _patch_scope(monkeypatch, svc) + payload = VehicleRechargeRecordCreate( + rechargeDate=date(2026, 9, 26), + rechargeType="etc", + amount=Decimal("50.00"), + applyId="apply-1", + ) + result = approval_svc.disburse_apply( + db, "t1", _user("u-fin", "财务"), "apply-1", payload + ) + assert result.status == "disbursed" + + def test_reject_sets_rejected_and_appends_history(monkeypatch): from app.services import pl_vehicle_recharge_approval_service as approval_svc from app.services import pl_vehicle_recharge_service as svc