This commit is contained in:
2026-10-20 14:00:49 +08:00
parent 3879a47673
commit 5679b156ca
28 changed files with 803 additions and 515 deletions
@@ -2,7 +2,7 @@ import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import { normalizeOverseasPage, type OverseasPagePayload } from './lastMileOrders'
export interface CargoDiscardQuery { page: number; size: number; customerId?: string; discardNo?: string; inboundCode?: string; overseasWarehouseId?: string; status?: string }
export interface CargoDiscardSummary { id: string; customerId?: string | null; discardNo: string; outboundNo?: string | null; inboundCodes?: string | null; overseasWarehouseId?: string | null; overseasWarehouseName?: string | null; totalPackages?: number | null; totalFee?: number | string | null; currency?: string | null; status?: string | null; createdBy?: string | null; createdAt?: string | null; reviewedAt?: string | null }
export interface CargoDiscardSummary { skuCodes?: string[]; skuCount?: number; requestedPackages?: number; actualPackages?: number | null; id: string; customerId?: string | null; discardNo: string; outboundNo?: string | null; inboundCodes?: string | null; overseasWarehouseId?: string | null; overseasWarehouseName?: string | null; totalPackages?: number | null; totalFee?: number | string | null; currency?: string | null; status?: string | null; createdBy?: string | null; createdAt?: string | null; reviewedAt?: string | null }
export interface CargoDiscardItem { quantityUnit?: string | null; actualDiscardPackages?: number | null; pdaStatus?: string | null; cartonMarks?: string[]; id: string; orderId?: string | null; inboundCode?: string | null; skuCode?: string | null; productName?: string | null; availablePackagesSnapshot?: number | null; discardPackages?: number | null }
export interface CargoDiscardStatistics { orderCount: number; skuCount: number; requestedPackages: number; actualPackages: number | null; requestedPieces: number | null; actualPieces: number | null; palletCount: number | null }
export async function getCargoDiscardStatistics(query: CargoDiscardQuery) {
@@ -42,6 +42,36 @@ export interface InventoryDashboardDto {
procurementPackages: number
}
export interface InventoryPieceGroup {
customerId: string
warehouseId: string
destinationCode: string | null
anchorSkuCode: string | null
availablePieceCount: number | string | null
knownAvailablePieceCount: number | string
unknownCartonCount: number | string
availableCartonCount: number | string
availablePalletCount: number | string
skuContents: { skuCode: string; quantity: number | string }[]
cartons: { cartonMark: string; inventoryPositionId: string; palletId: string | null; quantity: number | string | null; items: { skuCode: string; quantity: number | string }[] }[]
}
export interface InventoryPieceProjection {
availablePieceCount: number | string | null
knownAvailablePieceCount: number | string
unknownCartonCount: number | string
availableCartonCount: number | string
availablePalletCount: number | string
groups: InventoryPieceGroup[]
}
/** 完整筛选的真实可用件数及混箱全部SKU,不按记账SKU换算。 */
export async function getInventoryPieceProjection(query: InventoryBalanceQuery) {
const response = await http.get<ApiEnvelope<InventoryPieceProjection>, ApiEnvelope<InventoryPieceProjection>>(
'/platform/v1/last-leg/inventory/piece-projection', { params: params(query) },
)
return unwrapApiResponse(response)
}
export interface InventoryPositionDto {
id: string
balanceId: string
@@ -51,8 +51,17 @@ export interface LastMileBusinessSource {
businessOrderNo: string | null
externalBookingNo: string | null
}
export interface ReturnOrderStatistics {
totals: { orderCount: number; skuTypes: number; expectedQuantity: number; receivedQuantity: number; remainingQuantity: number; receivedBoxCount: number; receivedParcelCount: number; receivedPalletCount: number }
pageDimensions: Array<{ returnNo: string; lastlegOrderId: string; expectedQuantity: number; receivedQuantity: number; remainingQuantity: number; receivedBoxCount: number; receivedParcelCount: number; receivedPalletCount: number }>
}
export async function getReturnOrderStatistics(params: LastMileOrderQuery) {
const response = await http.get<ApiEnvelope<ReturnOrderStatistics>, ApiEnvelope<ReturnOrderStatistics>>('/platform/v1/last-leg/return-orders/statistics', { params })
return unwrapApiResponse(response)
}
export interface LastMileOrderSummaryDto {
warehouseTransferQuantity?: import('./transferQuantityFacts').TransferQuantityFacts | null
businessSource?: LastMileBusinessSource | null
id: string
customerId: string
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { listSharedReceivingOverseasWarehouseOptions } from '@/modules/iam'
import { http } from '@/shared/api/http'
import { changeRelabelOrderStatus, createRelabelOrder, getRelabelOrder, listRelabelOrders, listRelabelWarehouseOptions } from './relabelOrders'
import { changeRelabelOrderStatus, createRelabelOrder, getRelabelOrder, listRelabelOrders, listRelabelWarehouseOptions, type CreateRelabelPayload } from './relabelOrders'
vi.mock('@/modules/iam', () => ({ listSharedReceivingOverseasWarehouseOptions: vi.fn() }))
vi.mock('@/shared/api/http', async (importOriginal) => ({ ...(await importOriginal<typeof import('@/shared/api/http')>()), http: { get: vi.fn(), post: vi.fn(), put: vi.fn() } }))
@@ -29,7 +29,7 @@ describe('relabel overseas API', () => {
it('uses the real create and status endpoints', async () => {
vi.mocked(http.post).mockResolvedValue({ code: '200', data: {} })
vi.mocked(http.put).mockResolvedValue({ code: '200', data: {} })
const payload = { customerOrderNo: 'CO-1', overseasWarehouseId: '9', labelDeadline: '2026-08-20T10:00:00', contactName: 'A', contactPhone: '1', chargeAmount: 20, idempotencyKey: 'k', items: [{ skuCode: 'SKU', productNameZh: 'P', totalPackageCount: 1, notifiedPackageCount: 1, shipmentNo: 'S', newSkuCode: 'SKU-2' }] }
const payload: CreateRelabelPayload = { customerOrderNo: 'CO-1', operationWarehouseId: '9', labelDeadline: '2026-08-20T10:00:00', contactName: 'A', contactPhone: '1', chargeAmount: 20, idempotencyKey: 'k', items: [{ sourceInventoryBalanceId: '11', sourceInventoryPositionId: '12', sourceInboundOrderId: '13', cartonMark: 'BOX', operationType: 'SKU_LABEL', skuItems: [{ skuCode: 'SKU', newSkuCode: 'SKU-2', quantity: 8 }], skuCode: 'SKU', productNameZh: 'P', totalPackageCount: 1, notifiedPackageCount: 1, shipmentNo: 'S', newSkuCode: 'SKU-2' }] }
await createRelabelOrder('10001', payload)
await changeRelabelOrderStatus('RL/1', 'PROCESSING', 'ok')
expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/relabel-applications', payload, { params: { customerId: '10001' } })
@@ -12,6 +12,15 @@ export interface RelabelOrderQuery {
}
export interface CreateRelabelItemPayload {
sourceInventoryBalanceId?: string
sourceInventoryPositionId?: string
sourceInboundOrderId?: string
returnCartonId?: string
cartonMark: string
fbaCartonMark?: string
destinationFbaWarehouseCode?: string
operationType: 'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU'
skuItems: Array<{ skuCode: string; newSkuCode: string; quantity: number; remark?: string }>
skuCode: string
productNameZh: string
totalPackageCount: number
@@ -30,9 +39,9 @@ export interface CreateRelabelItemPayload {
export interface CreateRelabelPayload {
customerOrderNo: string
operationWarehouseId?: string
/** @deprecated compatibility alias for older platform fixtures */
overseasWarehouseId?: string
operationWarehouseId: string
deliveryMethod?: 'TRUCK' | 'EXPRESS' | 'PICKUP' | 'AMAZON_PICKUP' | 'CUSTOMER_PICKUP'
printShippingLabel?: boolean
labelDeadline: string
contactName: string
contactPhone: string
@@ -131,6 +140,7 @@ export interface RelabelDetailDto {
}
export interface RelabelQuantity {
sourceUnitCounts?: Record<string, number | string> | null
labelPieceCount: number | string | null
cartonCount: number | string | null
skuCount: number | string
@@ -140,6 +150,10 @@ export interface RelabelQuantity {
targetPalletCount: number | string | null
}
export interface RelabelQuantityTotals {
knownSourcePalletCount?: number | string
unknownSourcePalletOrders?: number | string
knownTargetPalletCount?: number | string
unknownTargetPalletOrders?: number | string
orderCount: number | string
skuCount: number | string
knownLabelPieceCount: number | string
@@ -148,6 +162,26 @@ export interface RelabelQuantityTotals {
unknownCartonOrders: number | string
orders: Record<string, RelabelQuantity>
}
export interface RelabelInventoryCandidate {
inventoryBalanceId?: string | null
inboundOrderId?: string | null
inboundOrderNo: string
inboundCode?: string
skuCode: string
productNameZh: string
availablePackages: number | string
sourceType: string
returnCartonId?: string | null
cartonMark?: string | null
cartonAvailablePackages?: number | string
cartonSkuItems?: Array<{ skuCode: string; quantity: number }>
positions: Array<{ positionId: string; palletNo: string; availablePackages: number | string }>
}
/** 候选归属和可用量由服务端决定,长整型来源ID按字符串原样提交。 */
export async function listRelabelInventoryCandidates(customerId: string, operationWarehouseId: string) {
const response = await http.get<ApiEnvelope<RelabelInventoryCandidate[]>, ApiEnvelope<RelabelInventoryCandidate[]>>('/platform/v1/last-leg/relabel-applications/inventory-candidates', { params: { customerId, operationWarehouseId } })
return unwrapApiResponse(response)
}
/** 汇总同一筛选下全部单据,不以当前页合计代替。 */
export async function getRelabelQuantities(query: RelabelOrderQuery) {
const response = await http.get<ApiEnvelope<RelabelQuantityTotals>, ApiEnvelope<RelabelQuantityTotals>>('/platform/v1/last-leg/relabel-applications/quantity-summary', { params: query })
@@ -0,0 +1,26 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import type { LastMileOrderQuery } from './lastMileOrders'
export interface TransferQuantityFacts {
declaredPieceCount: number | string | null
receivedPieceCount: number | string | null
declaredCartonCount: number | string
receivedCartonCount: number | string
receivedPalletCount: number | string
availableCartonCount: number | string
shippedCartonCount: number | string
remainingPalletCount: number | string
shippedPalletCount: number | string
}
export interface TransferQuantityTotals extends TransferQuantityFacts {
orderCount: number | string
knownDeclaredPieceCount: number | string
unknownDeclaredOrderCount: number | string
knownReceivedPieceCount: number | string
unknownReceivedOrderCount: number | string
}
export async function getTransferQuantityTotals(query: LastMileOrderQuery) {
const response = await http.get<ApiEnvelope<TransferQuantityTotals>, ApiEnvelope<TransferQuantityTotals>>(
'/platform/v1/last-leg/orders/warehouse-transfer-quantities', { params: query },
)
return unwrapApiResponse(response)
}