Files
admin-web/src/modules/overseas-warehouse/api/warehouseTransfers.ts
T
2026-10-15 18:25:23 +08:00

230 lines
9.3 KiB
TypeScript

import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import { normalizeOverseasPage, type OverseasPagePayload } from './lastMileOrders'
import { normalizeWarehouseTransferQuantity } from './warehouseTransferQuantities'
import type { ReceivingOverseasWarehouseDto } from './pallets'
export interface WarehouseTransferQuery {
page: number
size: number
transferNo?: string
operatingWarehouseId?: string
fbaWarehouseCode?: string
status?: string
needRelabel?: boolean
}
export interface WarehouseTransferSummaryDto {
id: string
transferNo: string
inboundCode?: string | null
lastlegOrderId?: string | null
lastlegOrderNo?: string | null
operatingWarehouseId?: string | null
fbaWarehouseCode?: string | null
packageCount: number
deliveryMethod?: string | null
needRelabel: boolean
relabelNo?: string | null
status: string
estimatedArrivalAt?: string | null
createdAt?: string | null
}
export interface WarehouseTransferItemDto {
id: string
skuCode: string
productNameZh: string
quantity?: number | string | null
packageCount: number
shippingMark?: string | null
}
export interface WarehouseTransferCartonDto {
items?: Array<{ transferItemId: string; skuCode: string; quantity: number | string }>
transferItemId?: string | null
transferPalletId?: string | null
length?: number | string | null
width?: number | string | null
height?: number | string | null
weight?: number | string | null
quantity?: number | string | null
countedAt?: string | null
discrepancyReason?: string | null
cartonMark?: string | null
customerCartonMark?: string | null
cartonIndex: number
totalCartons: number
status: string
}
export interface WarehouseTransferCartonLabelDto {
transferNo: string
cartons: WarehouseTransferCartonDto[]
printableHtml: string
}
export async function getWarehouseTransferCartonLabel(customerId: string, transferNo: string) {
const response = await http.get<ApiEnvelope<WarehouseTransferCartonLabelDto>, ApiEnvelope<WarehouseTransferCartonLabelDto>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/carton-label`, { params: { customerId } },
)
return unwrapApiResponse(response)
}
export interface WarehouseTransferRelabelBindingDto {
operationType: string
customerCartonMark: string
originalSku: string
newSku?: string | null
quantity: number
fbaCartonMark?: string | null
destinationWarehouseCode?: string | null
remark?: string | null
}
export interface WarehouseTransferPalletDto {
id: string
sequenceNo: number
warehouseLocationCode?: string | null
palletNo?: string | null
cartonCount: number
grossWeightKg: number
volumeCbm: number
status: string
}
export interface WarehouseTransferDetailDto extends WarehouseTransferSummaryDto {
inboundOrderId?: string | null
orderType?: string | null
orderStatus?: string | null
relabelApplicationId?: string | null
relabelStatus?: string | null
deliveryAddress?: string | null
estimatedShipAt?: string | null
fbaShipmentNo?: string | null
trackingNo?: string | null
fbaBoxNos?: string | null
remark?: string | null
items: WarehouseTransferItemDto[]
cartons: WarehouseTransferCartonDto[]
pallets: WarehouseTransferPalletDto[]
relabelBindings: WarehouseTransferRelabelBindingDto[]
customerConfirmedAt?: string | null
actualShipAt?: string | null
destinationWarehouseId?: string | null
destinationWarehouseCode?: string | null
destinationWarehouseName?: string | null
sourceOrders?: Array<{ lastlegOrderId: string; lastlegOrderNo?: string | null; inboundOrderId: string; inboundCode?: string | null }>
allocations?: Array<{ inventoryPositionId: string; inboundOrderId: string; inboundCode?: string | null; palletId: string; palletNo?: string | null; packageCount: number; status: string; destinationInventoryPositionId?: string | null; skuCode?: string | null }>
receiptCommands?: WarehouseTransferReceiptCommandDto[]
}
export interface WarehouseTransferReceiptCommandDto {
sourceInventoryPositionId: string
status: 'PREPARED' | 'RESERVED' | 'COMPLETED'
requestHash: string
request: ReceiveWarehouseTransferPayload
target: { warehouseId: string; warehouseCode: string; warehouseName: string; palletId: string; palletNo: string; locationId: string; locationCode: string; fbaId?: string | null; fbaCode: string; fbaName?: string | null }
}
export interface AllocateWarehouseTransferPayload {
idempotencyKey: string
allocations: Array<{ inventoryPositionId: string; palletId: string; packageCount: number }>
}
export interface OutboundWarehouseTransferPayload { idempotencyKey: string; remark?: string }
export interface ReceiveWarehouseTransferPayload {
sourceInventoryPositionId: string
destinationOverseasWarehouseId: string
destinationOverseasWarehouseCode: string
destinationOverseasWarehouseName: string
destinationWarehouseId?: string
destinationWarehouseCode: string
destinationWarehouseName?: string
destinationPalletId: string
destinationPalletNo: string
warehouseLocationId: string
warehouseLocationCode: string
batchNo: string
idempotencyKey: string
remark?: string
}
export async function outboundWarehouseTransfer(customerId: string, transferNo: string, payload: OutboundWarehouseTransferPayload) {
const response = await http.post<ApiEnvelope<WarehouseTransferDetailWire>, ApiEnvelope<WarehouseTransferDetailWire>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/outbound`, payload, { params: { customerId } },
)
return normalizeTransferDetail(unwrapApiResponse(response))
}
export async function receiveWarehouseTransfer(customerId: string, transferNo: string, payload: ReceiveWarehouseTransferPayload) {
const response = await http.post<ApiEnvelope<WarehouseTransferDetailWire>, ApiEnvelope<WarehouseTransferDetailWire>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/receipts`, payload, { params: { customerId } },
)
return normalizeTransferDetail(unwrapApiResponse(response))
}
export async function listTransferReceivingWarehouses() {
const records: ReceivingOverseasWarehouseDto[] = []
let total: number | undefined
for (let page = 1; page <= 100; page++) {
const response = await http.get<ApiEnvelope<OverseasPagePayload<ReceivingOverseasWarehouseDto>>, ApiEnvelope<OverseasPagePayload<ReceivingOverseasWarehouseDto>>>(
'/platform/v1/warehouse-admin/overseas-receiving-options', { params: { page, size: 200 } },
)
const value = normalizeOverseasPage(unwrapApiResponse(response))
if (total !== undefined && total !== value.total) throw new Error('收货仓列表已变化,请重新查询')
total = value.total
records.push(...value.records.map(w => ({ ...w, id: String(w.id), customerId: String(w.customerId) })))
if (records.length === total) {
if (new Set(records.map(w => w.id)).size !== records.length || records.some(w => w.customerId !== '0' || w.warehouseType !== 'OVERSEAS' || w.status !== 'ACTIVE')) throw new Error('收货仓返回范围不符合要求')
return { records }
}
if (!value.records.length || records.length > total) throw new Error('收货仓分页不完整,请重新查询')
}
throw new Error('收货仓分页超出查询限制')
}
type WarehouseTransferDetailWire = Omit<WarehouseTransferDetailDto, 'packageCount'> & { packageCount: number | string }
function normalizeTransferDetail(value: WarehouseTransferDetailWire): WarehouseTransferDetailDto {
return { ...value, packageCount: normalizeWarehouseTransferQuantity(value.packageCount, '本票转运箱数') }
}
/** The caller must use confirmed source positions, never aggregate inventory rows. */
export async function allocateWarehouseTransfer(customerId: string, transferNo: string, payload: AllocateWarehouseTransferPayload) {
const response = await http.post<ApiEnvelope<WarehouseTransferDetailWire>, ApiEnvelope<WarehouseTransferDetailWire>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/allocations`,
payload,
{ params: { customerId } },
)
return normalizeTransferDetail(unwrapApiResponse(response))
}
export async function listWarehouseTransfers(customerId: string, query: WarehouseTransferQuery) {
const response = await http.get<ApiEnvelope<OverseasPagePayload<WarehouseTransferSummaryDto>>, ApiEnvelope<OverseasPagePayload<WarehouseTransferSummaryDto>>>(
'/platform/v1/last-leg/warehouse-transfers',
{ params: { ...query, customerId } },
)
return normalizeOverseasPage(unwrapApiResponse(response))
}
export async function getWarehouseTransfer(customerId: string, transferNo: string) {
const response = await http.get<ApiEnvelope<WarehouseTransferDetailWire>, ApiEnvelope<WarehouseTransferDetailWire>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}`,
{ params: { customerId } },
)
return normalizeTransferDetail(unwrapApiResponse(response))
}
export async function reviewWarehouseTransfer(
customerId: string,
transferNo: string,
decision: 'APPROVE' | 'REJECT',
remark?: string,
) {
const response = await http.post<ApiEnvelope<WarehouseTransferDetailWire>, ApiEnvelope<WarehouseTransferDetailWire>>(
`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/review`,
{ decision, remark },
{ params: { customerId } },
)
return normalizeTransferDetail(unwrapApiResponse(response))
}