feat: add vehicle recharge approval pending approve reject disburse
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
ea8c2c0362
commit
f80a29fb8d
8 files changed
+15288
-12531
No files matched your search
@@ -6,6 +6,7 @@ from app.api.pl import (
|
||||
base_data,
|
||||
vehicle_fuel,
|
||||
vehicle_recharge,
|
||||
vehicle_recharge_approval,
|
||||
vehicle_maintenance,
|
||||
vehicle_urea,
|
||||
transport_address,
|
||||
@@ -55,6 +56,7 @@ pl_router.include_router(menu_badges.router)
|
||||
# ETC/加油充值
|
||||
pl_router.include_router(vehicle_recharge.apply_router)
|
||||
pl_router.include_router(vehicle_recharge.record_router)
|
||||
pl_router.include_router(vehicle_recharge_approval.approval_router)
|
||||
|
||||
pl_router.include_router(vehicle_fuel.router)
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"""车辆充值审批 API"""
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.db_auth import DbUserContext, get_db_current_user
|
||||
from app.core.tenant_context import get_tenant_no
|
||||
from app.schemas.pl.vehicle_recharge import (
|
||||
VehicleRechargeApplyListResponse,
|
||||
VehicleRechargeApplyRead,
|
||||
VehicleRechargeOpinionBody,
|
||||
VehicleRechargeRecordCreate,
|
||||
)
|
||||
from app.services import pl_vehicle_recharge_approval_service as approval_svc
|
||||
|
||||
approval_router = APIRouter(
|
||||
prefix="/vehicle-recharge-approval", tags=["运输计划-ETC加油充值审批"]
|
||||
)
|
||||
|
||||
|
||||
@approval_router.get("/pending", response_model=VehicleRechargeApplyListResponse)
|
||||
def list_pending(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=500, alias="pageSize"),
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
) -> VehicleRechargeApplyListResponse:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
items, total = approval_svc.list_pending(
|
||||
db, tenant_no, user, page=page, page_size=page_size
|
||||
)
|
||||
return VehicleRechargeApplyListResponse(
|
||||
items=items, total=total, page=page, pageSize=page_size
|
||||
)
|
||||
|
||||
|
||||
@approval_router.get("/done", response_model=VehicleRechargeApplyListResponse)
|
||||
def list_done(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=500, alias="pageSize"),
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
) -> VehicleRechargeApplyListResponse:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
items, total = approval_svc.list_done(
|
||||
db, tenant_no, user, page=page, page_size=page_size
|
||||
)
|
||||
return VehicleRechargeApplyListResponse(
|
||||
items=items, total=total, page=page, pageSize=page_size
|
||||
)
|
||||
|
||||
|
||||
@approval_router.post("/{apply_id}/approve", response_model=VehicleRechargeApplyRead)
|
||||
def approve_apply(
|
||||
apply_id: str,
|
||||
body: Optional[VehicleRechargeOpinionBody] = None,
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
) -> VehicleRechargeApplyRead:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
opinion = body.opinion if body else None
|
||||
return approval_svc.approve_apply(
|
||||
db, tenant_no, user, apply_id, opinion=opinion
|
||||
)
|
||||
|
||||
|
||||
@approval_router.post("/{apply_id}/reject", response_model=VehicleRechargeApplyRead)
|
||||
def reject_apply(
|
||||
apply_id: str,
|
||||
body: VehicleRechargeOpinionBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
) -> VehicleRechargeApplyRead:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
return approval_svc.reject_apply(
|
||||
db, tenant_no, user, apply_id, opinion=body.opinion
|
||||
)
|
||||
|
||||
|
||||
@approval_router.post("/{apply_id}/disburse", response_model=VehicleRechargeApplyRead)
|
||||
def disburse_apply(
|
||||
apply_id: str,
|
||||
payload: VehicleRechargeRecordCreate,
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
) -> VehicleRechargeApplyRead:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
return approval_svc.disburse_apply(db, tenant_no, user, apply_id, payload)
|
||||
@@ -0,0 +1,315 @@
|
||||
"""车辆充值审批:待办、通过、驳回、发放登记。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.db_auth import DbUserContext
|
||||
from app.core.utils import generate_uuid
|
||||
from app.models.bx_models import BxWorkflowNode
|
||||
from app.models.pl_models import (
|
||||
PlVehicleRechargeApply,
|
||||
PlVehicleRechargeApproval,
|
||||
PlVehicleRechargeRecord,
|
||||
)
|
||||
from app.schemas.pl.vehicle_recharge import (
|
||||
VehicleRechargeApplyRead,
|
||||
VehicleRechargeRecordCreate,
|
||||
)
|
||||
from app.services.pl_vehicle_recharge_service import (
|
||||
apply_to_read,
|
||||
generate_recharge_order_no,
|
||||
_workflow_nodes,
|
||||
)
|
||||
|
||||
|
||||
def _get_apply_for_approval(
|
||||
db: Session, tenant_no: str, apply_id: str
|
||||
) -> PlVehicleRechargeApply:
|
||||
row = (
|
||||
db.query(PlVehicleRechargeApply)
|
||||
.filter(
|
||||
PlVehicleRechargeApply.id == apply_id,
|
||||
PlVehicleRechargeApply.tenant_no == tenant_no,
|
||||
PlVehicleRechargeApply.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="申请单不存在")
|
||||
return row
|
||||
|
||||
|
||||
def _current_node(
|
||||
db: Session,
|
||||
apply: PlVehicleRechargeApply,
|
||||
user: DbUserContext,
|
||||
tenant_no: str,
|
||||
) -> BxWorkflowNode:
|
||||
if apply.status != "in_approval" or not apply.current_node:
|
||||
raise HTTPException(status_code=400, detail="申请单不在审批中")
|
||||
node = (
|
||||
db.query(BxWorkflowNode)
|
||||
.filter(
|
||||
BxWorkflowNode.id == apply.current_node,
|
||||
BxWorkflowNode.tenant_no == tenant_no,
|
||||
BxWorkflowNode.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not node:
|
||||
raise HTTPException(status_code=400, detail="当前审批节点不存在或已删除")
|
||||
if node.approver_id != user.id:
|
||||
raise HTTPException(status_code=403, detail="无权审批当前节点")
|
||||
return node
|
||||
|
||||
|
||||
def _append_approval(
|
||||
db: Session,
|
||||
*,
|
||||
apply: PlVehicleRechargeApply,
|
||||
node: BxWorkflowNode,
|
||||
user: DbUserContext,
|
||||
tenant_no: str,
|
||||
status: str,
|
||||
opinion: Optional[str],
|
||||
now: datetime,
|
||||
) -> PlVehicleRechargeApproval:
|
||||
row = PlVehicleRechargeApproval(
|
||||
id=generate_uuid(),
|
||||
apply_id=apply.id,
|
||||
approval_round=apply.approval_round or 1,
|
||||
node_id=node.id,
|
||||
node_name=node.node_name,
|
||||
approver_id=user.id,
|
||||
approver_name=user.name or "",
|
||||
approval_status=status,
|
||||
approval_opinion=(opinion or "").strip() or None,
|
||||
approval_time=now,
|
||||
tenant_no=tenant_no,
|
||||
create_by=user.id,
|
||||
create_dept_id=user.dept_id or "",
|
||||
create_time=now,
|
||||
update_by=user.id,
|
||||
update_time=now,
|
||||
del_flag=0,
|
||||
)
|
||||
db.add(row)
|
||||
return row
|
||||
|
||||
|
||||
def list_pending(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user: DbUserContext,
|
||||
*,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> tuple[list[VehicleRechargeApplyRead], int]:
|
||||
query = (
|
||||
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,
|
||||
)
|
||||
)
|
||||
# 待办按当前节点审批人授权,不做 create_by 数据范围过滤
|
||||
total = query.count()
|
||||
page = max(1, page)
|
||||
page_size = min(max(1, page_size), 500)
|
||||
rows = (
|
||||
query.order_by(PlVehicleRechargeApply.create_time.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
.all()
|
||||
)
|
||||
return [apply_to_read(row, db, include_detail=True) for row in rows], total
|
||||
|
||||
|
||||
def list_done(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user: DbUserContext,
|
||||
*,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> tuple[list[VehicleRechargeApplyRead], int]:
|
||||
apply_ids = [
|
||||
r[0]
|
||||
for r in (
|
||||
db.query(PlVehicleRechargeApproval.apply_id)
|
||||
.filter(
|
||||
PlVehicleRechargeApproval.tenant_no == tenant_no,
|
||||
PlVehicleRechargeApproval.del_flag == 0,
|
||||
PlVehicleRechargeApproval.approver_id == user.id,
|
||||
)
|
||||
.distinct()
|
||||
.all()
|
||||
)
|
||||
]
|
||||
if not apply_ids:
|
||||
return [], 0
|
||||
query = db.query(PlVehicleRechargeApply).filter(
|
||||
PlVehicleRechargeApply.tenant_no == tenant_no,
|
||||
PlVehicleRechargeApply.del_flag == 0,
|
||||
PlVehicleRechargeApply.id.in_(apply_ids),
|
||||
)
|
||||
total = query.count()
|
||||
page = max(1, page)
|
||||
page_size = min(max(1, page_size), 500)
|
||||
rows = (
|
||||
query.order_by(PlVehicleRechargeApply.update_time.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
.all()
|
||||
)
|
||||
return [apply_to_read(row, db, include_detail=True) for row in rows], total
|
||||
|
||||
|
||||
def approve_apply(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user: DbUserContext,
|
||||
apply_id: str,
|
||||
*,
|
||||
opinion: Optional[str] = None,
|
||||
) -> VehicleRechargeApplyRead:
|
||||
apply = _get_apply_for_approval(db, tenant_no, apply_id)
|
||||
node = _current_node(db, apply, user, tenant_no)
|
||||
action = (node.action_type or "approve").strip() or "approve"
|
||||
if action != "approve":
|
||||
raise HTTPException(status_code=400, detail="当前节点请使用发放登记")
|
||||
nodes = _workflow_nodes(db, apply.wf_id, tenant_no)
|
||||
idx = next((i for i, n in enumerate(nodes) if n.id == node.id), None)
|
||||
if idx is None:
|
||||
raise HTTPException(status_code=400, detail="当前审批节点不在流程中")
|
||||
if idx >= len(nodes) - 1:
|
||||
raise HTTPException(status_code=400, detail="末节点须为发放登记,不能仅通过")
|
||||
now = datetime.now()
|
||||
_append_approval(
|
||||
db,
|
||||
apply=apply,
|
||||
node=node,
|
||||
user=user,
|
||||
tenant_no=tenant_no,
|
||||
status="approved",
|
||||
opinion=opinion,
|
||||
now=now,
|
||||
)
|
||||
apply.current_node = nodes[idx + 1].id
|
||||
apply.update_by = user.id
|
||||
apply.update_time = now
|
||||
db.commit()
|
||||
db.refresh(apply)
|
||||
return apply_to_read(apply, db, include_detail=True)
|
||||
|
||||
|
||||
def reject_apply(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user: DbUserContext,
|
||||
apply_id: str,
|
||||
*,
|
||||
opinion: Optional[str] = None,
|
||||
) -> VehicleRechargeApplyRead:
|
||||
apply = _get_apply_for_approval(db, tenant_no, apply_id)
|
||||
node = _current_node(db, apply, user, tenant_no)
|
||||
opinion_text = (opinion or "").strip()
|
||||
if not opinion_text:
|
||||
raise HTTPException(status_code=400, detail="驳回须填写意见")
|
||||
now = datetime.now()
|
||||
_append_approval(
|
||||
db,
|
||||
apply=apply,
|
||||
node=node,
|
||||
user=user,
|
||||
tenant_no=tenant_no,
|
||||
status="rejected",
|
||||
opinion=opinion_text,
|
||||
now=now,
|
||||
)
|
||||
apply.status = "rejected"
|
||||
apply.current_node = None
|
||||
apply.update_by = user.id
|
||||
apply.update_time = now
|
||||
db.commit()
|
||||
db.refresh(apply)
|
||||
return apply_to_read(apply, db, include_detail=True)
|
||||
|
||||
|
||||
def disburse_apply(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
user: DbUserContext,
|
||||
apply_id: str,
|
||||
payload: VehicleRechargeRecordCreate,
|
||||
) -> VehicleRechargeApplyRead:
|
||||
apply = _get_apply_for_approval(db, tenant_no, apply_id)
|
||||
node = _current_node(db, apply, user, tenant_no)
|
||||
action = (node.action_type or "approve").strip() or "approve"
|
||||
if action != "disburse":
|
||||
raise HTTPException(status_code=400, detail="当前节点不是发放节点")
|
||||
if payload.recharge_type != apply.recharge_type:
|
||||
raise HTTPException(status_code=400, detail="充值类型与申请单不一致")
|
||||
existing = (
|
||||
db.query(PlVehicleRechargeRecord.id)
|
||||
.filter(
|
||||
PlVehicleRechargeRecord.apply_id == apply.id,
|
||||
PlVehicleRechargeRecord.tenant_no == tenant_no,
|
||||
PlVehicleRechargeRecord.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="该申请单已有发放记录")
|
||||
|
||||
now = datetime.now()
|
||||
record = PlVehicleRechargeRecord(
|
||||
id=generate_uuid(),
|
||||
recharge_order_no=generate_recharge_order_no(db),
|
||||
recharge_date=payload.recharge_date,
|
||||
recharge_type=apply.recharge_type,
|
||||
card_no=(payload.card_no or "").strip() or None,
|
||||
gas_station=(payload.gas_station or "").strip() or None,
|
||||
amount=payload.amount,
|
||||
attachment_urls=payload.attachment_urls,
|
||||
apply_id=apply.id,
|
||||
license_plate=apply.license_plate,
|
||||
tenant_no=tenant_no,
|
||||
remark=payload.remark,
|
||||
create_by=user.id,
|
||||
create_dept_id=user.dept_id or "",
|
||||
create_time=now,
|
||||
update_by=user.id,
|
||||
update_time=now,
|
||||
del_flag=0,
|
||||
)
|
||||
db.add(record)
|
||||
_append_approval(
|
||||
db,
|
||||
apply=apply,
|
||||
node=node,
|
||||
user=user,
|
||||
tenant_no=tenant_no,
|
||||
status="disbursed",
|
||||
opinion=payload.remark,
|
||||
now=now,
|
||||
)
|
||||
apply.status = "disbursed"
|
||||
apply.current_node = None
|
||||
apply.update_by = user.id
|
||||
apply.update_time = now
|
||||
db.commit()
|
||||
db.refresh(apply)
|
||||
return apply_to_read(apply, db, include_detail=True)
|
||||
@@ -137,10 +137,7 @@
|
||||
"135": "context.py",
|
||||
"136": "get_db_current_user_optional",
|
||||
"137": "iter_user_menu_perm_codes",
|
||||
"138": "Community 138",
|
||||
"139": "customer_options",
|
||||
"140": "crm_export_columns.py",
|
||||
"141": "web_login_rsa_public_key",
|
||||
"142": "clear_pl_plan_instance_data.py",
|
||||
"143": "create_from_quote",
|
||||
"144": "response.py",
|
||||
@@ -150,7 +147,6 @@
|
||||
"148": "plan_has_incomplete_hazard_cert_exists",
|
||||
"149": ".to_tenant_user_dict",
|
||||
"150": "list_product_options",
|
||||
"151": "Community 151",
|
||||
"152": "register_local_callback",
|
||||
"153": "web_login_rsa_public_key",
|
||||
"154": "redis_client.py",
|
||||
@@ -176,20 +172,15 @@
|
||||
"174": "test_pl_vehicle_ops_list_filters.py",
|
||||
"175": "pl_ocr_service.py",
|
||||
"176": "invoice_list_filters.py",
|
||||
"177": "build_unsettled_plan_column_filters",
|
||||
"178": "build_contract_template_column_filters",
|
||||
"179": "test_pl_vehicle_personnel_photo_schema.py",
|
||||
"180": "软删装货产品与装/送货段合同;送货段本体由 upsert 保留主键,不在此全量软删。",
|
||||
"181": "PlCarrier",
|
||||
"182": "vehicle_recharge.py",
|
||||
"183": "resolve_name_pinyin_abbr",
|
||||
"184": "verify_plan_archive_zip_export.py",
|
||||
"185": "public_transport_site.py",
|
||||
"186": "BaseStorage",
|
||||
"187": "price_letter.py",
|
||||
"188": "api_crypto.py",
|
||||
"189": "build_customer_column_filters",
|
||||
"190": "Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} 流水与撞号检测须含软删:uk_contr",
|
||||
"191": "pl_plan_cert_perm.py",
|
||||
"192": "assert_row_deletable_by_creator",
|
||||
"193": "CRUDBase",
|
||||
@@ -200,17 +191,13 @@
|
||||
"198": "login_crypto.py",
|
||||
"199": "WechatMessageService",
|
||||
"200": "quote_share_log.py",
|
||||
"201": "clue.py",
|
||||
"202": "build_clue_column_filters",
|
||||
"203": "product_list_filters.py",
|
||||
"204": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"205": "build_contract_change_column_filters",
|
||||
"206": "crm_quote_pdf.py",
|
||||
"207": "shanxia2.0 后端骨架(FastAPI)",
|
||||
"208": "customer_follow.py",
|
||||
"209": "storage.py",
|
||||
"210": "customer_pool.py",
|
||||
"211": "车辆ETC/加油充值申请 pl_vehicle_recharge_apply",
|
||||
"212": "contract_list_filters.py",
|
||||
"213": "web_auth_roles.py",
|
||||
"214": "timedelta",
|
||||
@@ -219,24 +206,15 @@
|
||||
"217": "pl_plan_list_attachments.py",
|
||||
"218": "若存在入库/出库单,用明细 actual_qty 覆盖归档提交中的实际数量(权威来源)。",
|
||||
"219": "expense_item.py",
|
||||
"220": "apply_site_feedback",
|
||||
"221": "admin_request_json.py",
|
||||
"222": "start.sh",
|
||||
"223": "vehicle_recharge.py",
|
||||
"224": "_history_matches_date",
|
||||
"225": "FakeRedis",
|
||||
"226": "invoice.py",
|
||||
"227": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"228": "resolve_product_id_for_quote_item",
|
||||
"229": "Java: viewByShareCode",
|
||||
"230": "草稿/已驳回结算单按当前运输单运费刷新明细快照与合计。",
|
||||
"231": "build_advance_column_filters",
|
||||
"232": "crm_export_columns.py",
|
||||
"233": "code:block1 (backend/)",
|
||||
"234": "calc_age_from_chinese_id_card",
|
||||
"235": "wx_qr_session.py",
|
||||
"236": "一次上传一条发票:多文件挂同一档案,金额为总额,只写一条额度流水。 使用 request.form().getlist,兼容只传 1 个文件时 Fas",
|
||||
"237": "order_status_compat.py",
|
||||
"238": "vehicle_urea.py",
|
||||
"239": "shanxia2.0 Backend 生产部署说明",
|
||||
"240": "reimburse.py",
|
||||
@@ -257,19 +235,15 @@
|
||||
"255": "vehicle_fuel.py",
|
||||
"256": "Java: createFromOpportunity — 返回合同 id",
|
||||
"257": "mini_auth.py",
|
||||
"258": "dict_data.py",
|
||||
"259": "dict_type.py",
|
||||
"260": "tenant.py",
|
||||
"261": "手动触发计划按月自动生成(定时任务同款逻辑)。",
|
||||
"262": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"263": "__init__.py",
|
||||
"264": "__init__.py",
|
||||
"265": "供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll",
|
||||
"266": "menu.py",
|
||||
"267": "post.py",
|
||||
"268": "leave.py",
|
||||
"269": "site.py",
|
||||
"270": "validate_stop_products_package_against_contract",
|
||||
"271": "statistics.py",
|
||||
"272": "default_settlement_clause",
|
||||
"273": "storage.py",
|
||||
@@ -277,34 +251,24 @@
|
||||
"275": "fix_pl_transport_db_link_stop_ids.py",
|
||||
"276": "供方详情 — Java: CrmSupplierInfoController.getInfo",
|
||||
"277": "新增供方 — Java: CrmSupplierInfoController.add",
|
||||
"278": "修改供方 — Java: CrmSupplierInfoController.edit",
|
||||
"279": "修改供方 — Java: CrmSupplierInfoController.edit",
|
||||
"280": "resolve_wechat_login_user",
|
||||
"281": "apply_crm_owner_scope",
|
||||
"282": "crm_quote_pdf.py",
|
||||
"283": "删除供方 — Java: CrmSupplierInfoController.remove",
|
||||
"284": "导出供方列表 — Java: CrmSupplierInfoController.export",
|
||||
"285": "导出供方列表 — Java: CrmSupplierInfoController.export",
|
||||
"286": "shanxia2.0 后端骨架(FastAPI)",
|
||||
"287": "test_wx_qr_session.py",
|
||||
"288": "按客户ID查询开票信息(与 by-customer 等价)",
|
||||
"289": "Java: queryList for export",
|
||||
"290": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"291": "Java: createFromQuote — 返回合同 id",
|
||||
"292": "列表展示用:优先 CRM 标准名(详情仍返回库内 product_name,以便编辑反推别名)。",
|
||||
"293": "导出出入库列表(当前筛选全量,或 ids 已选)。",
|
||||
"294": "证书 URL 为空(NULL / 空串 / 纯空白)。",
|
||||
"295": "导出出入库列表(当前筛选全量,或 ids 已选)。",
|
||||
"296": "单条查询(三方 H5 填写页无需门户登录)。",
|
||||
"297": "返回 'archived' | 'arranged_unarchived' | None(不计入)。",
|
||||
"298": "collect_unique_delivery_date_strings",
|
||||
"299": "单条查询(三方 H5 填写页无需门户登录)。",
|
||||
"300": "三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。",
|
||||
"301": "price_letter.py",
|
||||
"302": "clear_pl_plan_instance_data.py",
|
||||
"303": "导出地磅单列表(当前筛选全量,或 ids 已选)。",
|
||||
"304": "__init__.py",
|
||||
"305": "__init__.py",
|
||||
"306": "__init__.py",
|
||||
"307": "__init__.py",
|
||||
"308": "__init__.py",
|
||||
@@ -317,28 +281,18 @@
|
||||
"315": "base_data.py",
|
||||
"316": "get_active",
|
||||
"317": "shanxia2.0 Backend 生产部署说明",
|
||||
"318": "导出司机列表(当前筛选全量,或 ids 已选)。",
|
||||
"319": "导出承运商列表(当前筛选全量,或 ids 已选)。",
|
||||
"320": "导出车辆列表(当前筛选全量,或 ids 已选)。",
|
||||
"321": "前端预览误用单位全称时,名称片段会等于面板/客户/开票全称。",
|
||||
"322": "导出人员列表(当前筛选全量,或 ids 已选)。",
|
||||
"323": "customer_stop_pref.py",
|
||||
"324": "db_collation.py",
|
||||
"325": "__init__.py",
|
||||
"326": "customer_pool.py",
|
||||
"327": "io_order.py",
|
||||
"328": "_plan_plate_match",
|
||||
"329": "save_driver_plan_feedback",
|
||||
"330": "将关联且状态为 approved 的合同置为 executing(不 commit)。返回已推进的合同 id。",
|
||||
"331": "common.py",
|
||||
"332": "_plan_product_name_exists",
|
||||
"333": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"334": "车辆尿素申请 pl_vehicle_urea_apply",
|
||||
"335": "公开分享查看报价单 — Java: GET /share/{shareCode}(无 JWT)",
|
||||
"336": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"337": "按客户ID查询开票信息(与 by-customer 等价)",
|
||||
"338": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"339": "新增供方 — Java: CrmSupplierInfoController.add",
|
||||
"340": "返回全部已完成的审批动作(含撤回/重提前的历史轮次),按时间正序。",
|
||||
"341": "已有汇款凭证但结算单仍停留在 approved 时自动纠正(兼容历史脏数据)",
|
||||
"342": "软删除结算单历史审批记录(重新提交审批前清理)",
|
||||
@@ -356,7 +310,6 @@
|
||||
"354": "__init__.py",
|
||||
"355": "__init__.py",
|
||||
"356": "__init__.py",
|
||||
"357": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"358": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"359": "创建运输计划(完整表单:供方/需方 + 产品库产品 + 可选预选车辆,不自动安排运输)",
|
||||
"360": "列表展示用:优先 CRM 标准名(详情仍返回库内 product_name,以便编辑反推别名)。",
|
||||
@@ -380,25 +333,18 @@
|
||||
"378": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"379": "生成计划编号 POYYYYMMDD###(当日 3 位序号,每日重置)",
|
||||
"380": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"381": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"382": "__init__.py",
|
||||
"383": "__init__.py",
|
||||
"384": "__init__.py",
|
||||
"385": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"386": "同一节点多次审批时,取 approval_time 最新的一条作为当前节点状态。",
|
||||
"387": "返回全部审批动作,按时间正序排列,用于展示完整审批历史。",
|
||||
"388": "同一节点多次审批时,取 approval_time 最新的一条作为当前节点状态。",
|
||||
"389": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"390": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"391": "生成计划编号 POYYYYMMDD###(当日 3 位序号,每日重置)",
|
||||
"392": "返回全部审批动作,按时间正序排列,用于展示完整审批历史。",
|
||||
"393": "生成计划编号 POYYYYMMDD###(当日 3 位序号,每日重置)",
|
||||
"394": "返回全部审批动作,按时间正序排列,用于展示完整审批历史。",
|
||||
"395": "生成计划编号 POYYYYMMDD###(当日 3 位序号,每日重置)",
|
||||
"396": "生成计划编号 POYYYYMMDD###(当日 3 位序号,每日重置)",
|
||||
"397": "归一化勾选链接;返回 None 表示未传(不改关联),[] 表示清空。",
|
||||
"398": "同一节点多次审批时,取 approval_time 最新的一条作为当前节点状态。",
|
||||
"399": "返回全部审批动作,按时间正序排列,用于展示完整审批历史。",
|
||||
"400": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"401": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"402": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
@@ -417,7 +363,6 @@
|
||||
"415": "__init__.py",
|
||||
"416": "__init__.py",
|
||||
"417": "更新计划危化品产品证书(购买证/运输证/合法使用证明,不触发安排运输)",
|
||||
"418": "获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。",
|
||||
"419": "获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。",
|
||||
"420": "获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。",
|
||||
"421": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
@@ -426,7 +371,6 @@
|
||||
"424": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"425": "作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。",
|
||||
"426": "获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。",
|
||||
"427": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"428": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"429": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"430": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
@@ -452,20 +396,16 @@
|
||||
"450": "作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。",
|
||||
"451": "将历史分装单号统一为 NO:SXFZYYYYMMDD-###",
|
||||
"452": "强制修改跟进状态(仅改字段,不作废联动、不补运输安排)。需权限 pl:plan:force-follow-status。",
|
||||
"453": "将历史分装单号统一为 NO:SXFZYYYYMMDD-###",
|
||||
"454": "将历史分装单号统一为 NO:SXFZYYYYMMDD-###",
|
||||
"455": "作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。",
|
||||
"456": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"457": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
"458": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"459": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
"460": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"461": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
"462": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
"463": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"464": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"465": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"466": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"467": "按送货客户聚合送(提)货单打印数据;不传 customerId 时仅返回客户选项。",
|
||||
"468": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
"469": "分配分装单号 NO:SXFZYYYYMMDD-###(按租户每日递增)",
|
||||
@@ -484,14 +424,12 @@
|
||||
"482": "导出计划列表(当前筛选全量,或 ids 已选)。",
|
||||
"483": "导出计划列表(当前筛选全量,或 ids 已选)。",
|
||||
"484": "导出计划列表(当前筛选全量,或 ids 已选)。",
|
||||
"485": "导出计划列表(当前筛选全量,或 ids 已选)。",
|
||||
"486": "导出计划列表(当前筛选全量,或 ids 已选)。",
|
||||
"487": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"488": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"489": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"490": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"491": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"492": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"493": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"494": "表单可安排数量 + 问号弹窗:合同用量与已安排计划列表。",
|
||||
"495": "code:bash (cd /path/to/shanxia2.0)",
|
||||
@@ -507,23 +445,16 @@
|
||||
"505": "_format_plan_date",
|
||||
"506": "_resolve_plan_flags",
|
||||
"507": "transport.py",
|
||||
"508": "导出运输列表(当前筛选全量,或 ids 已选)。",
|
||||
"509": "_transport_has_db_links",
|
||||
"510": "导出维保申请列表(当前筛选全量,或 ids 已选)。",
|
||||
"511": "vehicle_urea.py",
|
||||
"512": "_avatar_url_field",
|
||||
"513": "_build_user_info_payload",
|
||||
"514": "_jwt_user_payload",
|
||||
"515": "登出:吊销 Redis 会话(若已配置 REDIS_HOST)。无 Token 或已失效时仍返回成功(幂等)。",
|
||||
"516": "获取当前用户信息(与 sys_user 字段对应)。",
|
||||
"517": "获取当前用户信息(与 sys_user 字段对应)。",
|
||||
"518": "小程序内嵌 WebView:用 ticket 兑换 Web JWT(不吊销原 refresh)。",
|
||||
"519": "公开分享查看报价单 — Java: GET /share/{shareCode}(无 JWT)",
|
||||
"520": "与 web-admin 静态路由 meta.roles(admin/super_admin)对齐的单元素数组。",
|
||||
"521": "与 web-admin `userInfo` 字段约定一致。",
|
||||
"522": "Web 端登录:账号为 sys_user.user_no,密码为 sys_user.password(bcrypt)。",
|
||||
"523": "Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致)",
|
||||
"524": "Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致)",
|
||||
"525": "Web 端登录:账号为 sys_user.user_no,密码为 sys_user.password(bcrypt)。",
|
||||
"526": "_web_role_list",
|
||||
"527": "auth.py",
|
||||
@@ -547,11 +478,8 @@
|
||||
"545": "框架合同提交审批校验:仅要求有产品明细;调价区间为可选,不强制。",
|
||||
"546": "框架合同提交审批校验:仅要求有产品明细;调价区间为可选,不强制。",
|
||||
"547": "Java: deleteWithValidByIds — 软删除",
|
||||
"548": "Java: deleteWithValidByIds — 软删除",
|
||||
"549": "框架合同提交审批校验:仅要求有产品明细;调价区间为可选,不强制。",
|
||||
"550": "框架合同提交审批校验:仅要求有产品明细;调价区间为可选,不强制。",
|
||||
"551": "Java: batchSubmitApproval",
|
||||
"552": "Java: withdrawApproval",
|
||||
"553": "Java: batchSubmitApproval",
|
||||
"554": "Java: batchSubmitApproval",
|
||||
"555": "Java: batchSubmitApproval",
|
||||
@@ -559,16 +487,13 @@
|
||||
"557": "Java: withdrawApproval",
|
||||
"558": "Java: withdrawApproval",
|
||||
"559": "Java: batchSubmitApproval",
|
||||
"560": "Java: batchSubmitApproval",
|
||||
"561": "Java: withdrawApproval",
|
||||
"562": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"563": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"564": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"565": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"566": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"567": "按入参整表覆盖合同扫描件;attachments=[] 表示清空。",
|
||||
"568": "Java: createFromQuote — 返回合同 id",
|
||||
"569": "Java: createFromQuote — 返回合同 id",
|
||||
"570": "Java: createFromQuote — 返回合同 id",
|
||||
"571": "Java: createFromQuote — 返回合同 id",
|
||||
"572": "Java: createFromQuote — 返回合同 id",
|
||||
@@ -579,7 +504,6 @@
|
||||
"577": "Java: renewContract — 基础续签",
|
||||
"578": "Java: renewContract — 基础续签",
|
||||
"579": "Java: queryList for export",
|
||||
"580": "Java: renewContract — 基础续签",
|
||||
"581": "Java: renewContract — 基础续签",
|
||||
"582": "Java: queryList for export",
|
||||
"583": "Java: queryList for export",
|
||||
@@ -594,15 +518,12 @@
|
||||
"592": "Java: createFromOpportunity — 返回合同 id",
|
||||
"593": "Java: batchAssignOwner",
|
||||
"594": "Java: createFromOpportunity — 返回合同 id",
|
||||
"595": "Java: createFromOpportunity — 返回合同 id",
|
||||
"596": "Java: downloadContractPdf — 返回合同文件 URL/路径",
|
||||
"597": "Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}",
|
||||
"598": "Java: batchAssignOwner",
|
||||
"599": "Java: batchAssignOwner",
|
||||
"600": "Java: batchAssignOwner",
|
||||
"601": "Java: batchAssignOwner",
|
||||
"602": "Java: batchAssignOwner",
|
||||
"603": "Java: downloadContractPdf — 返回合同文件 URL/路径",
|
||||
"604": "Java: downloadContractPdf — 返回合同文件 URL/路径",
|
||||
"605": "Java: downloadContractPdf — 返回合同文件 URL/路径",
|
||||
"606": "Java: batchAssignOwner",
|
||||
@@ -613,47 +534,38 @@
|
||||
"611": "Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}",
|
||||
"612": "Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}",
|
||||
"613": "Java: downloadContractPdf — 返回合同文件 URL/路径",
|
||||
"614": "Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}",
|
||||
"615": "Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}",
|
||||
"616": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"617": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"618": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"619": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"620": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"621": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"622": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"623": "Java Controller TODO — 保持 pending,记录备注",
|
||||
"624": "根据供需方面板与主数据生成/校正合同名称。",
|
||||
"625": "根据供需方面板与主数据生成/校正合同名称。",
|
||||
"626": "根据供需方面板与主数据生成/校正合同名称。",
|
||||
"627": "Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} 流水与撞号检测须含软删:uk_contr",
|
||||
"628": "Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} 流水与撞号检测须含软删:uk_contr",
|
||||
"629": "Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} 流水与撞号检测须含软删:uk_contr",
|
||||
"630": "租户内合同编号是否已占用(含软删,保证全局唯一)。",
|
||||
"631": "租户内合同编号是否已占用(含软删,保证全局唯一)。",
|
||||
"632": "租户内合同编号是否已占用(含软删,保证全局唯一)。",
|
||||
"633": "标准编号替换日期段,保留序号;非标准返回 None。",
|
||||
"634": "租户内合同编号是否已占用(含软删,保证全局唯一)。",
|
||||
"635": "标准编号替换日期段,保留序号;非标准返回 None。",
|
||||
"636": "标准编号替换日期段,保留序号;非标准返回 None。",
|
||||
"637": "将标准合同编号日期段对齐签订日;撞号则序号顺延(含软删查重)。 非标准编号原样返回。无签订日则原样返回。",
|
||||
"638": "将标准合同编号日期段对齐签订日;撞号则序号顺延(含软删查重)。 非标准编号原样返回。无签订日则原样返回。",
|
||||
"639": "将标准合同编号日期段对齐签订日;撞号则序号顺延(含软删查重)。 非标准编号原样返回。无签订日则原样返回。",
|
||||
"640": "按面板单位全称精确匹配交易主体/客户主数据,取 shortName;对不上则用全称。",
|
||||
"641": "按面板单位全称精确匹配交易主体/客户主数据,取 shortName;对不上则用全称。",
|
||||
"642": "按面板单位全称精确匹配交易主体/客户主数据,取 shortName;对不上则用全称。",
|
||||
"643": "按面板单位全称精确匹配交易主体/客户主数据,取 shortName;对不上则用全称。",
|
||||
"644": "前端预览误用单位全称时,名称片段会等于面板/客户/开票全称。",
|
||||
"645": "按面板供方(partyA)-需方(partyB)生成:{供方简称}-{需方简称}({合同编号})。 - 名称为空:按主数据简称自动生成 - 名",
|
||||
"646": "前端预览误用单位全称时,名称片段会等于面板/客户/开票全称。",
|
||||
"647": "前端预览误用单位全称时,名称片段会等于面板/客户/开票全称。",
|
||||
"648": "按面板供方(partyA)-需方(partyB)生成:{供方简称}-{需方简称}({合同编号})。 - 名称为空:按主数据简称自动生成 - 名",
|
||||
"649": "按面板供方(partyA)-需方(partyB)生成:{供方简称}-{需方简称}({合同编号})。 - 名称为空:按主数据简称自动生成 - 名",
|
||||
"650": "按面板供方(partyA)-需方(partyB)生成:{供方简称}-{需方简称}({合同编号})。 - 名称为空:按主数据简称自动生成 - 名",
|
||||
"651": "customer_follow.py",
|
||||
"652": "customer_pool.py",
|
||||
"653": "customer.py",
|
||||
"654": "客户下拉选项:租户内只读搜索,不做 owner 数据权限(供地磅/合同等主数据选用)。",
|
||||
"655": "invoice_info.py",
|
||||
"656": "pool_rule.py",
|
||||
"657": "price_letter.py",
|
||||
@@ -662,25 +574,13 @@
|
||||
"660": "报价自动建档时生成产品编号(与产品管理新增规则一致)",
|
||||
"661": "Java: resolveProductIdForQuoteItem(CrmProduct 无 tax_rate,跳过税率匹配)",
|
||||
"662": "quote_approver_setting.py",
|
||||
"663": "Java: viewByShareCode",
|
||||
"664": "Java: toContract — 返回 contractId",
|
||||
"665": "Java: toContract — 返回 contractId",
|
||||
"666": "Java: createNewVersion — 返回新报价单 id",
|
||||
"667": "Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人)",
|
||||
"668": "Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人)",
|
||||
"669": "Java: statisticsOverview",
|
||||
"670": "Java: statisticsOverview",
|
||||
"671": "Java: statisticsStatus",
|
||||
"672": "Java: statisticsStatus",
|
||||
"673": "Java: statisticsTrend",
|
||||
"674": "Java: statisticsTrend",
|
||||
"675": "Java: fillContractLinkedCount",
|
||||
"676": "Java: calculateAmount",
|
||||
"677": "Java: resolveQuoteProductIds",
|
||||
"678": "Java: generateQuoteNo — {供方编码}DD-yyyyMMdd + 3位序号",
|
||||
"679": "Java: generateQuoteNo — {供方编码}DD-yyyyMMdd + 3位序号",
|
||||
"680": "Java: deleteWithValidByIds",
|
||||
"681": "Java: deleteWithValidByIds",
|
||||
"682": "statistics.py",
|
||||
"683": "supplier_info.py",
|
||||
"684": "__init__.py",
|
||||
@@ -690,16 +590,11 @@
|
||||
"688": "__tablename__",
|
||||
"689": "__init__.py",
|
||||
"690": "车辆维保申请 pl_vehicle_maintenance_apply",
|
||||
"691": "车辆维保处理登记 pl_vehicle_maintenance_process",
|
||||
"692": "车辆维保申请 pl_vehicle_maintenance_apply",
|
||||
"693": "车辆尿素申请 pl_vehicle_urea_apply",
|
||||
"694": "车辆维保处理登记 pl_vehicle_maintenance_process",
|
||||
"695": "阿里云 OCR 月度调用计数 pl_ocr_usage_month(按自然月全局统计)",
|
||||
"696": "车辆尿素发放登记 pl_vehicle_urea_disburse",
|
||||
"697": "车辆尿素发放登记 pl_vehicle_urea_disburse",
|
||||
"698": "阿里云 OCR 月度调用计数 pl_ocr_usage_month(按自然月全局统计)",
|
||||
"699": "车辆ETC/加油充值发放登记 pl_vehicle_recharge_record",
|
||||
"700": "车辆ETC/加油充值发放登记 pl_vehicle_recharge_record",
|
||||
"701": "attachment.py",
|
||||
"702": "auth.py",
|
||||
"703": "advance.py",
|
||||
@@ -722,19 +617,12 @@
|
||||
"720": "contract_approver_setting.py",
|
||||
"721": "contract.py",
|
||||
"722": "POST /contract/approval — 按合同 ID 审批",
|
||||
"723": "POST /contract/approval — 按合同 ID 审批",
|
||||
"724": "POST /contract/export 筛选条件(无分页或沿用 list 参数)",
|
||||
"725": "POST /contract/export 筛选条件(无分页或沿用 list 参数)",
|
||||
"726": "续签 — 字段同创建,parent 由路径提供",
|
||||
"727": "续签 — 字段同创建,parent 由路径提供",
|
||||
"728": "GET /contract/approval/list;extra=allow 透传列头漏斗动态参数。",
|
||||
"729": "GET /contract/change/list;extra=allow 透传列头漏斗动态参数。",
|
||||
"730": "GET /contract/change/list;extra=allow 透传列头漏斗动态参数。",
|
||||
"731": "GET /contract/template/list;extra=allow 透传列头漏斗动态参数。",
|
||||
"732": "customer_follow.py",
|
||||
"733": "_forbid_legacy_invoice_fields",
|
||||
"734": "customer.py",
|
||||
"735": "解析列表查询参数,供 API 与 CRUD 共用。",
|
||||
"736": "invoice_info.py",
|
||||
"737": "opportunity.py",
|
||||
"738": "pool_rule.py",
|
||||
@@ -760,7 +648,6 @@
|
||||
"758": "io_order.py",
|
||||
"759": "plan.py",
|
||||
"760": "运维纠错:强制改跟进状态(仅改字段,不联动业务)。",
|
||||
"761": "运维纠错:强制改跟进状态(仅改字段,不联动业务)。",
|
||||
"762": "_coerce_resource_snapshot",
|
||||
"763": "transport_address.py",
|
||||
"764": "validate_coordinates",
|
||||
@@ -837,8 +724,6 @@
|
||||
"835": "_lookup_user_name",
|
||||
"836": "out_rows_with_attachments",
|
||||
"837": "invoice.py",
|
||||
"838": "返回流水字典列表;流水备注为空时回填关联发票备注(含已软删发票)。",
|
||||
"839": "一次上传创建一条发票档案:金额为总额,files 全部挂到同一 biz_id,只写一条 credit_upload。",
|
||||
"840": "_require_wallet_admin",
|
||||
"841": "_resolve_owner",
|
||||
"842": "_resolve_owner_for_query",
|
||||
@@ -895,81 +780,41 @@
|
||||
"893": "list_sort.py",
|
||||
"894": "list_sort.py",
|
||||
"895": "plan_list_filters.py",
|
||||
"896": "(product_id, package_spec) -> {archivedQty, arrangedUnarchivedQty, arrangedTotal",
|
||||
"897": "plan_id -> { load_ids: set[str] | None, # None = 该侧全部装货段 delivery_",
|
||||
"898": "返回 productId -> 三数量字段。",
|
||||
"899": "返回 (productId, packageSpec) -> 三数量字段。",
|
||||
"900": "按复合键匹配合同产品行;同键多行时 quantity 相加(防御)。",
|
||||
"901": "已安排合计 − 本计划旧量 + 本计划新量。",
|
||||
"902": "将关联且状态为 approved 的合同置为 executing(不 commit)。返回已推进的合同 id。",
|
||||
"903": "已安排合计 − 本计划旧量 + 本计划新量。",
|
||||
"904": "返回: - None: 跳过(本次无量) - kind=violation | remaining | framework_cumula",
|
||||
"905": "返回 'archived' | 'arranged_unarchived' | None(不计入)。",
|
||||
"906": "返回: - None: 跳过(本次无量) - kind=violation | remaining | framework_cumula",
|
||||
"907": "product_id -> {archivedQty, arrangedUnarchivedQty, arrangedTotalQty}",
|
||||
"908": "返回 (violations, reminders)。 在写运输前调用;reminders 可缓存到成功响应,勿在状态变更后再算。",
|
||||
"909": "返回 (violations, reminders)。 在写运输前调用;reminders 可缓存到成功响应,勿在状态变更后再算。",
|
||||
"910": "超限则 422;通过则返回 reminders(供成功响应复用)。",
|
||||
"911": "超限则 422;通过则返回 reminders(供成功响应复用)。",
|
||||
"912": "表单「可安排数量」与问号弹窗数据。 arrangedQty / availableQty 均不含 exclude_plan_id(若该计划已在已安排桶中",
|
||||
"913": "表单「可安排数量」与问号弹窗数据。 arrangedQty / availableQty 均不含 exclude_plan_id(若该计划已在已安排桶中",
|
||||
"914": "勾选「生成地磅单」:按产品生成/复用地磅计划,并创建关联出入库单。",
|
||||
"915": "按段 upsert 地磅出入库与 link:新建 / 就地更新 / 取消勾选软删。 返回 (link_results, warnings)。已过磅取消",
|
||||
"916": "按装货/送货段勾选 upsert 地磅计划与出入库单(兼容旧调用方,忽略 warnings)。",
|
||||
"917": "按装货/送货段勾选 upsert 地磅计划与出入库单(兼容旧调用方,忽略 warnings)。",
|
||||
"918": "勾选「生成地磅单」:按产品生成/复用地磅计划,并创建关联出入库单。",
|
||||
"919": "勾选「生成地磅单」:按产品生成/复用地磅计划,并创建关联出入库单。",
|
||||
"920": "未过磅且状态可作废时,可就地更新日期/往来/车牌/数量。",
|
||||
"921": "计划保存后按当前段次/计划日期回写可编辑出入库单日期;跨月则改挂月计划。 不可回写时不抛错,返回 warnings。",
|
||||
"922": "取消段 generate_dibang:已过磅阻断;否则软删 link+出入库并回减月计划。",
|
||||
"923": "就地更新已有出入库可变字段;不可编辑时写 warnings,不抛错。",
|
||||
"924": "计划保存后按当前段次/计划日期回写可编辑出入库单日期;跨月则改挂月计划。 不可回写时不抛错,返回 warnings。",
|
||||
"925": "按装货/送货段勾选生成地磅计划与出入库单。",
|
||||
"926": "按段产品 upsert 出入库+link:已有则就地更新,无则新建。",
|
||||
"927": "运输单运费变更后,同步关联草稿结算单的明细快照与合计。",
|
||||
"928": "草稿/已驳回结算单按当前运输单运费刷新明细快照与合计。",
|
||||
"929": "软删除结算单历史审批记录(重新提交审批前清理)",
|
||||
"930": "审批记录已全部完成但结算单仍停留在 approving 时自动纠正(兼容历史脏数据)",
|
||||
"931": "审批记录已全部完成但结算单仍停留在 approving 时自动纠正(兼容历史脏数据)",
|
||||
"932": "已有汇款凭证但结算单仍停留在 approved 时自动纠正(兼容历史脏数据)",
|
||||
"933": "批量纠正已有汇款凭证但仍为 approved 的结算单",
|
||||
"934": "返回全部已完成的审批动作(含撤回/重提前的历史轮次),按时间正序。",
|
||||
"935": "__init__.py",
|
||||
"936": "list_sort.py",
|
||||
"937": "无段表数据时回退计划产品级导出(兼容旧数据)。",
|
||||
"938": "构建归档导出数据行:每计划 × 每段次 × 每段产品一行。",
|
||||
"939": "构建归档导出数据行:每计划 × 每段次 × 每段产品一行。 stop_kind 为 load / delivery 时只导出该类段次,与归档列表段次漏",
|
||||
"940": "装货/送货段入库出库列方向(与前端 resolveArchiveListIoColumn 一致)。",
|
||||
"941": "单侧保存计划合同关联(旧计划级接口,兼容保留)。",
|
||||
"942": "单侧保存计划合同关联(旧计划级接口,兼容保留)。",
|
||||
"943": "单侧保存计划合同关联(旧计划级接口,兼容保留)。",
|
||||
"944": "按段次汇总计划是否仍有合同缺口(supplier=装货侧,demander=送货侧)。",
|
||||
"945": "若存在入库/出库单,用明细 actual_qty 覆盖归档提交中的实际数量(权威来源)。",
|
||||
"946": "一车多卸:段级数量已写入,此处仅处理计划产品品牌分量。",
|
||||
"947": "解析司机:显式选择优先,否则取车辆关联司机;均无则允许为空。",
|
||||
"948": "解析司机:显式选择优先,否则取车辆关联司机;均无则允许为空。",
|
||||
"949": "批量从 CRM 产品主数据读取易制毒/易制爆标识。",
|
||||
"950": "批量从 CRM 产品主数据读取易制毒/易制爆标识。",
|
||||
"951": "列表筛选:计划下存在 CRM 标识为易制毒/易制爆的产品。",
|
||||
"952": "从 CRM 产品主数据读取易制毒/易制爆标识(不读计划产品快照)。",
|
||||
"953": "列表筛选:存在危化产品且至少一张必需证书未上传。",
|
||||
"954": "列表筛选:计划下存在 CRM 标识为易制毒/易制爆的产品。",
|
||||
"955": "证书 URL 为空(NULL / 空串 / 纯空白)。",
|
||||
"956": "列表筛选:存在危化产品且至少一张必需证书未上传。",
|
||||
"957": "计划内所有危化品产品的所需证书槽位均已上传。",
|
||||
"958": "计划内所有危化品产品的所需证书槽位均已上传。",
|
||||
"959": "全部危化品证书已上传时,自动标记证书齐备(与人工确认等效)。",
|
||||
"960": "全部危化品证书已上传时,自动标记证书齐备(与人工确认等效)。",
|
||||
"961": "写入/更新草稿运输单(pl_transport + pl_plan_transport),计划保持待安排。",
|
||||
"962": "写入/更新草稿运输单(pl_transport + pl_plan_transport),计划保持待安排。",
|
||||
"963": "同步运输预选到草稿运输子表(pl_transport)。",
|
||||
"964": "同步运输预选到草稿运输子表(pl_transport)。",
|
||||
"965": "装货段与 plan 1:1;uk_pl_plan_load_plan 仅按 plan_id 唯一,软删后不可再 INSERT,须原地更新。",
|
||||
"966": "装货段与 plan 1:1;uk_pl_plan_load_plan 仅按 plan_id 唯一,软删后不可再 INSERT,须原地更新。",
|
||||
"967": "_soft_delete_plan_stops",
|
||||
"968": "保存段回传。成功返回 (True, None);失败返回 (False, msg)。不 commit。",
|
||||
"969": "上传附件。成功返回 (data, None) 并 commit;失败返回 (None, (code, msg))。",
|
||||
"970": "本租户计划 + 关联运输(须有有效 site_access_token)。找不到返回 (None, None)。",
|
||||
"971": "_resolve_plan_flags",
|
||||
"972": "_compare",
|
||||
"973": "_get_variable",
|
||||
@@ -977,8 +822,6 @@
|
||||
"975": "_parse_variables",
|
||||
"976": "__init__.py",
|
||||
"977": "分配同租户、同报销日期下的标题序号(MAX+1)。 配合 uk_tenant_reimburse_date_title_seq 唯一约束,并发冲突时由上",
|
||||
"978": "预览用标题生成(提交时仍会在服务端重新分配序号以保证并发安全)。",
|
||||
"979": "确定最终标题与 title_seq。 - 未填标题或仍为自动标题格式时:按报销日期重新分配序号并生成标题。 - 自定义标题:title_seq",
|
||||
"980": "code:bash (cd /path/to/shanxia2.0)",
|
||||
"981": "code:bash (./start-prod.sh status # 查看状态)",
|
||||
"982": "code:bash (curl -s http://127.0.0.1:9111/health)",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -1 +1 @@
|
||||
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6958,"mtime_ns":1784032424448146538,"hash":"c2fb44aedb880733de28afa7f92d2c1c681eb600f821bbeba2bcda3933e5bc37"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
|
||||
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6958,"mtime_ns":1784032424448146538,"hash":"c2fb44aedb880733de28afa7f92d2c1c681eb600f821bbeba2bcda3933e5bc37"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
|
||||
+14478
-11929
File diff suppressed because it is too large.
Load diff
@@ -2,8 +2,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
@@ -187,3 +189,152 @@ def test_resubmit_increments_round_keeps_history(monkeypatch):
|
||||
assert result.current_node == node_gm
|
||||
assert len(result.approvals) == 1
|
||||
assert result.approvals[0].approval_status == "rejected"
|
||||
|
||||
|
||||
def test_approve_advances_to_next_node(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, node_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="in_approval",
|
||||
wf_id=wf_id,
|
||||
current_node=node_gm,
|
||||
approval_round=1,
|
||||
tenant_no="t1",
|
||||
create_dept_id="d1",
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
_patch_scope(monkeypatch, svc)
|
||||
|
||||
result = approval_svc.approve_apply(
|
||||
db, "t1", _user("u-gm", "总经理"), "apply-1", opinion="同意"
|
||||
)
|
||||
assert result.status == "in_approval"
|
||||
assert result.current_node == node_fin
|
||||
assert any(a.approval_status == "approved" for a in result.approvals)
|
||||
|
||||
|
||||
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
|
||||
|
||||
db = _make_session()
|
||||
_seed_vehicle(db)
|
||||
wf_id, node_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="in_approval",
|
||||
wf_id=wf_id,
|
||||
current_node=node_gm,
|
||||
approval_round=1,
|
||||
tenant_no="t1",
|
||||
create_dept_id="d1",
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
_patch_scope(monkeypatch, svc)
|
||||
|
||||
result = approval_svc.reject_apply(
|
||||
db, "t1", _user("u-gm", "总经理"), "apply-1", opinion="材料不全"
|
||||
)
|
||||
assert result.status == "rejected"
|
||||
assert result.current_node is None
|
||||
assert result.approvals[-1].approval_status == "rejected"
|
||||
|
||||
|
||||
def test_disburse_writes_record_and_disbursed(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, _node_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="in_approval",
|
||||
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("100.00"),
|
||||
applyId="apply-1",
|
||||
)
|
||||
result = approval_svc.disburse_apply(
|
||||
db, "t1", _user("u-fin", "财务"), "apply-1", payload
|
||||
)
|
||||
assert result.status == "disbursed"
|
||||
assert result.record is not None
|
||||
assert result.record.amount == Decimal("100.00")
|
||||
assert any(a.approval_status == "disbursed" for a in result.approvals)
|
||||
|
||||
|
||||
def test_approve_forbidden_on_disburse_node(monkeypatch):
|
||||
from fastapi import HTTPException
|
||||
|
||||
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, _node_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="in_approval",
|
||||
wf_id=wf_id,
|
||||
current_node=node_fin,
|
||||
approval_round=1,
|
||||
tenant_no="t1",
|
||||
create_dept_id="d1",
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
_patch_scope(monkeypatch, svc)
|
||||
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
approval_svc.approve_apply(
|
||||
db, "t1", _user("u-fin", "财务"), "apply-1", opinion="ok"
|
||||
)
|
||||
assert exc.value.status_code == 400
|
||||
Reference in new issue
Block a user