openapi: 3.0.3 info: title: Firstleg Container Fee API version: 1.0.0 description: | 本契约只生产国内段费用来源(订舱、国内拖车、出口报关、海运等)。 loadingPlanId 由服务端解析到同一真实 containerBookingId;海外段费用由 Lastleg 的 OVERSEAS_CONTAINER_FEE 独立维护。 审核通过后发布不可变来源版本到 Finance,不复制海外费用或财务状态。 应付必须选择有效供应商;DIRECT_PAID 是已付快照,不再进入待结算。 当前币种仅支持 CNY,不进行跨币种加总。公共费用按柜内真实客户与订单分摊, 各行按 HALF_UP 保留两位后必须大于零,落库金额合计必须等于两位小数的总额。 成功和业务错误均使用 HTTP 200 包装,业务冲突 code 为 A0409。 security: [{bearerAuth: []}] paths: /api/platform/v1/loading-plans/{loadingPlanId}/fee-details: get: operationId: listContainerFees summary: 查询当前客户的国内段柜级费用 x-permission: firstleg:inbound:fee-detail:view parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeListResponse'} post: operationId: createContainerFee summary: 创建单一业务归属的国内段柜级费用 x-permission: firstleg:inbound:fee-detail:edit parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/SaveRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/allocation-groups: post: operationId: createContainerFeeAllocationGroup summary: 原子创建国内段柜级公共费用分摊组 x-permission: firstleg:inbound:fee-detail:edit parameters: - {$ref: '#/components/parameters/LoadingPlanId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/AllocationGroupRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeListResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/allocation-groups/{groupId}/approve: post: operationId: approveContainerFeeAllocationGroup summary: 整组审核国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/GroupId'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeListResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/allocation-groups/{groupId}/reject: post: operationId: rejectContainerFeeAllocationGroup summary: 整组驳回国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/GroupId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/RejectRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeListResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/allocation-groups/{groupId}/adjust: post: operationId: adjustContainerFeeAllocationGroup summary: 整组调整国内段柜级费用 x-permission: firstleg:inbound:fee-detail:edit parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/GroupId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/AllocationGroupAdjustRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeListResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/allocation-groups/{groupId}/void: post: operationId: voidContainerFeeAllocationGroup summary: 整组作废国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/GroupId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/VersionRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/EmptyResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/approve: post: operationId: approveContainerFee summary: 审核单行国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} - {$ref: '#/components/parameters/FeeId'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/EmptyResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/reject: post: operationId: rejectContainerFee summary: 驳回单行国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} - {$ref: '#/components/parameters/FeeId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/RejectRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/EmptyResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/adjust: post: operationId: adjustContainerFee summary: 调整单行国内段柜级费用 x-permission: firstleg:inbound:fee-detail:edit parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} - {$ref: '#/components/parameters/FeeId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/AdjustRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/FeeResponse'} /api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/void: post: operationId: voidContainerFee summary: 作废单行国内段柜级费用 x-permission: firstleg:inbound:fee-detail:review parameters: - {$ref: '#/components/parameters/LoadingPlanId'} - {$ref: '#/components/parameters/AccountId'} - {$ref: '#/components/parameters/FeeId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/VersionRequest'} responses: '200': description: 通过 code 区分成功与业务失败;分摊组不可经单行入口修改。 content: application/json: schema: {$ref: '#/components/schemas/EmptyResponse'} components: securitySchemes: bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT} parameters: LoadingPlanId: {in: path, name: loadingPlanId, required: true, schema: {$ref: '#/components/schemas/Id'}} GroupId: {in: path, name: groupId, required: true, schema: {$ref: '#/components/schemas/Id'}} FeeId: {in: path, name: feeId, required: true, schema: {$ref: '#/components/schemas/Id'}} AccountId: {in: query, name: accountId, required: true, schema: {type: integer, format: int64, minimum: 1}, description: 平台选择的客户,服务端校验柜内归属} schemas: Id: {type: string, pattern: '^[1-9][0-9]{0,19}$'} Amount: {type: number, format: decimal, minimum: 0, description: HALF_UP 保留两位小数} SaveRequest: type: object required: [feeItemId, direction, businessDate, amount, currency] properties: feeItemId: {$ref: '#/components/schemas/Id'} direction: {type: string, enum: [RECEIVABLE, PAYABLE]} businessDate: {type: string, format: date} amount: {$ref: '#/components/schemas/Amount'} currency: {type: string, enum: [CNY]} supplierId: {type: string, nullable: true, description: PAYABLE 必填,必须为启用供应商} settlementMode: {type: string, enum: [ON_ACCOUNT, DIRECT_PAID], nullable: true, description: 仅 PAYABLE 必填} paidAt: {type: string, format: date-time, nullable: true} paymentMethod: {type: string, enum: [ONLINE, BANK_TRANSFER, CASH, OTHER], nullable: true} paymentReferenceNo: {type: string, maxLength: 128, nullable: true} paymentVoucherFileIds: type: array maxItems: 20 nullable: true items: {$ref: '#/components/schemas/Id'} remark: {type: string, maxLength: 500, nullable: true} description: DIRECT_PAID 必须填写付款时间、方式及流水号或当前客户凭证;RECEIVABLE 和 ON_ACCOUNT 不允许残留付款字段。 Allocation: type: object required: [customerId, orderId, orderNo, amount] properties: customerId: {$ref: '#/components/schemas/Id'} orderId: {$ref: '#/components/schemas/Id'} orderNo: {type: string, minLength: 1, maxLength: 64} inboundId: {type: string, nullable: true} inboundCode: {type: string, maxLength: 64, nullable: true} amount: {type: number, format: decimal, minimum: 0, exclusiveMinimum: true} description: 客户和订单必须属于当前柜;入仓ID与入仓码同时填写或同时留空。 AllocationGroupRequest: type: object required: [fee, allocations] properties: fee: {$ref: '#/components/schemas/SaveRequest'} allocations: type: array minItems: 1 maxItems: 200 items: {$ref: '#/components/schemas/Allocation'} AdjustRequest: type: object required: [expectedSourceVersion, businessDate, amount] properties: expectedSourceVersion: {type: integer, minimum: 1} businessDate: {type: string, format: date} amount: {$ref: '#/components/schemas/Amount'} description: 只允许调整当前已审核有效版本,成功递增来源版本并重新待审核。 AllocationAmount: type: object required: [feeId, amount] properties: feeId: {$ref: '#/components/schemas/Id'} amount: {type: number, format: decimal, minimum: 0, exclusiveMinimum: true} AllocationGroupAdjustRequest: type: object required: [expectedSourceVersion, businessDate, totalAmount, allocations] properties: expectedSourceVersion: {type: integer, minimum: 1} businessDate: {type: string, format: date} totalAmount: {type: number, format: decimal, minimum: 0, exclusiveMinimum: true} allocations: type: array minItems: 1 maxItems: 200 items: {$ref: '#/components/schemas/AllocationAmount'} description: 必须包含整组全部费用行,逐行取两位后的合计与总额一致;成功后整组重新待审核。 VersionRequest: type: object required: [expectedSourceVersion] properties: expectedSourceVersion: {type: integer, minimum: 1} RejectRequest: type: object required: [reason] properties: reason: {type: string, minLength: 1, maxLength: 500} FeeDetail: type: object properties: id: {type: string, nullable: true} tenantId: {type: string, nullable: true} allocationGroupId: {type: string, nullable: true} allocationOrderId: {type: string, nullable: true} allocationInboundId: {type: string, nullable: true} loadingPlanId: {type: string, nullable: true} containerBookingId: {type: string, nullable: true} feeItemId: {type: string, nullable: true} supplierId: {type: string, nullable: true} reviewerId: {type: string, nullable: true} createdBy: {type: string, nullable: true} updatedBy: {type: string, nullable: true} allocationOrderNo: {type: string, nullable: true} allocationInboundCode: {type: string, nullable: true} containerNo: {type: string, nullable: true} feeItemCodeSnapshot: {type: string, nullable: true} feeItemNameSnapshot: {type: string, nullable: true} supplierName: {type: string, nullable: true} supplierCategory: {type: string, nullable: true} remark: {type: string, nullable: true} reviewerName: {type: string, nullable: true} reviewReason: {type: string, nullable: true} paymentReferenceNo: {type: string, nullable: true} paidAt: {type: string, format: date-time, nullable: true} reviewedAt: {type: string, format: date-time, nullable: true} createdAt: {type: string, format: date-time, nullable: true} updatedAt: {type: string, format: date-time, nullable: true} amount: {$ref: '#/components/schemas/Amount'} allocationGroupAmount: {$ref: '#/components/schemas/Amount'} businessDate: {type: string, format: date} currency: {type: string, enum: [CNY]} direction: {type: string, enum: [RECEIVABLE, PAYABLE]} reviewStatus: {type: string, enum: [PENDING, APPROVED, REJECTED]} sourceStatus: {type: string, enum: [ACTIVE, VOIDED]} sourceVersion: {type: integer, minimum: 1} settlementMode: {type: string, enum: [ON_ACCOUNT, DIRECT_PAID], nullable: true} paymentMethod: {type: string, enum: [ONLINE, BANK_TRANSFER, CASH, OTHER], nullable: true} paymentVoucherFileIds: {type: array, nullable: true, items: {$ref: '#/components/schemas/Id'}} FeeResponse: type: object required: [code, message] properties: code: {type: string, description: 成功为200,业务冲突为A0409} message: {type: string} requestId: {type: string, nullable: true} traceId: {type: string, nullable: true} data: {$ref: '#/components/schemas/FeeDetail'} timestamp: {type: string, format: date-time} FeeListResponse: type: object required: [code, message] properties: code: {type: string, description: 成功为200,业务冲突为A0409} message: {type: string} requestId: {type: string, nullable: true} traceId: {type: string, nullable: true} data: {type: array, items: {$ref: '#/components/schemas/FeeDetail'}} timestamp: {type: string, format: date-time} EmptyResponse: type: object required: [code, message] properties: code: {type: string, description: 成功为200,业务冲突为A0409} message: {type: string} requestId: {type: string, nullable: true} traceId: {type: string, nullable: true} data: {type: object, nullable: true} timestamp: {type: string, format: date-time}