160 lines
16 KiB
TypeScript
160 lines
16 KiB
TypeScript
import { apiRequest, apiBlobRequest } from '@/api/http'
|
|
import { createIdempotencyKey } from '@/utils/idempotencyKey'
|
|
|
|
export type RelabelDeliveryMethod = 'TRUCK' | 'EXPRESS' | 'AMAZON_PICKUP' | 'CUSTOMER_PICKUP'
|
|
|
|
export interface PageResult<T> { records: T[]; page: number; size: number; total: number; pages: number }
|
|
export interface LastlegOrderSummary { id: string; orderNo: string; inboundCode: string; orderType: string; packageCount: number; overseasWarehouseId?: string }
|
|
export interface HistoricalOrderSummary { id: string | null; orderNo: string; sourceOrderNo?: string; outboundOrderNo?: string; relabelOrderNo?: string; orderType?: string; status?: string; packageCount?: number }
|
|
export interface TransferSummary { id: string; transferNo: string; inboundCode: string; lastlegOrderId: string; lastlegOrderNo: string; operatingWarehouseId: string; fbaWarehouseCode?: string; packageCount: number; deliveryMethod: string; needRelabel: boolean; relabelNo?: string; relabelStatus?: string; relabelFulfillmentStatus?: string; status: string; estimatedArrivalAt: string; estimatedShipAt?: string; actualShipAt?: string; reviewedAt?: string; measuredAt?: string; outboundAt?: string; relabelCompletedAt?: string; createdAt: string }
|
|
export interface RelabelSummary { relatedBusinessOrderNos?: string[]; operationTypes?: Array<'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU' | 'RETURN_BULK_SKU_LABEL'>; id: string; relabelNo: string; lastlegOrderId: string; lastlegOrderNo: string; customerOrderNo: string; overseasWarehouseId: string; status: string; itemCount: number; totalPackageCount: number; notifiedPackageCount: number; skuCodes: string; createdAt: string; reviewedAt?: string; relabelCompletedAt?: string; outboundAt?: string; totalFileId?: string; totalFileName?: string; totalFileUrl?: string; cartonTotalFileId?: string; cartonTotalFileName?: string; cartonTotalFileUrl?: string; skuTotalFileId?: string; skuTotalFileName?: string; skuTotalFileUrl?: string; deliveryMethod?: RelabelDeliveryMethod; printShippingLabel?: boolean; shippingLabelFileId?: string; shippingLabelFileName?: string; shippingLabelFileUrl?: string; fulfillmentStatus?: string; outboundOrderNo?: string; customerConfirmedAt?: string; reviewChannel?: 'TAIL_ORDER' | 'RELABEL_ORDER'; reviewRemark?: string }
|
|
export interface RelabelItemDetail {
|
|
id: string; skuCode?: string; productNameZh: string; totalPackageCount: number; notifiedPackageCount: number
|
|
shipmentNo: string; trackingCode?: string; newSkuCode?: string; labelRequirement?: string; remark?: string
|
|
relabelFileId?: string; relabelFileName?: string; relabelFileUrl?: 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 }>
|
|
sourceInventoryBalanceId: string; sourceInventoryPositionId: string; sourceInboundOrderId: string
|
|
sourceInboundOrderNo?: string; sourceInboundCode?: string; sourceWarehouseId: string; sourceWarehouseCode?: string; sourceWarehouseName?: string
|
|
}
|
|
export interface RelabelDetail {
|
|
application: RelabelSummary & { labelDeadline?: string; contactName?: string; contactPhone?: string; chargeAmount?: number; chargeCurrency?: string; walletStatus?: string; walletErrorMessage?: string }
|
|
remark?: string; items: RelabelItemDetail[]
|
|
history: Array<{ fromStatus?: string; toStatus?: string; operatorId?: string; remark?: string; createdAt: string }>
|
|
tailOrders?: Array<{ id: string; orderNo: string; overseasWarehouseId: string; packageCount: number; status: string }>
|
|
completionEvidence?: { taskId: string; photoFileIds: string[]; completionRemark?: string; reviewerName?: string; reviewRemark?: string; completedAt?: string; customerConfirmedAt?: string }
|
|
}
|
|
export interface DiscardSummary { id: string; discardNo: string; outboundNo?: string; inboundCodes: string; overseasWarehouseId: string; overseasWarehouseName: string; totalPackages: number; totalFee: number; currency: string; status: string; createdAt: string; completedAt?: string; customerConfirmedAt?: string; skuCodes: string[]; skuCount: number; requestedPackages: number; actualPackages: number | null }
|
|
export interface DiscardBox { cartonId: string; cartonMark: string; palletNo?: string; skus: Array<{ skuCode: string; productName: string; availablePackages: number; availablePieces?: number | null }> }
|
|
export interface DiscardCandidate { cartons?: DiscardBox[]; cartonSelectionSupported?: boolean; cartonSelectionBlockReason?: string; orderId: string; sourceType?: 'DROPSHIP' | 'RETURN_CARTON' | 'INVENTORY'; inventoryPositionId?: string; palletNo?: string; returnCartonId?: string; cartonMark?: string; inboundCode: string; inboundOrderNo: string; overseasWarehouseId: string; overseasWarehouseName: string; availablePackages: number; skus: Array<{ skuCode: string; productName: string; availablePackages: number }> }
|
|
export interface RelabelInventoryCandidate {
|
|
cartonSkuItems: Array<{ skuCode: string; quantity: number }>; cartonAvailablePackages: number | string
|
|
skuConversions?: import('./returnInventory').ReturnSkuConversion[]
|
|
stockQuantity?: number; inventoryStatus?: string; occupiedRelabelNo?: string; mergedIntoCartonMarks?: string[]; hasOutbound?: boolean
|
|
inventoryBalanceId: string; inboundOrderId: string; inboundOrderNo?: string; inboundCode?: string
|
|
overseasWarehouseId: string; overseasWarehouseCode?: string; overseasWarehouseName?: string
|
|
destinationFbaWarehouseCode?: string; destinationFbaWarehouseName?: string; skuCode?: string; productNameZh?: string
|
|
availablePackages: number | string; version: number
|
|
positions: Array<{ positionId: string; palletNo?: string; batchNo?: string; warehouseLocationCode?: string; availablePackages: number }>
|
|
sourceType?: 'INBOUND' | 'RETURN_CARTON' | 'RETURN_PARCEL'; returnCartonId?: string; cartonMark?: string
|
|
skuItems?: Array<{ skuCode: string; newSkuCode?: string; quantity: number; remark?: string }>; relabelledPackages?: number; relabelledQuantity?: number; relabelReservedQuantity?: number; relabelStatus?: string
|
|
}
|
|
|
|
export interface TransferCreate {
|
|
inboundCode?: string; sourceLastlegOrderIds?: string[]; operatingWarehouseId: string; fbaWarehouseCode?: string; destinationType?: 'NONE' | 'FBA' | 'PRIVATE'
|
|
destinationWarehouseId?: string; destinationWarehouseCode?: string; destinationWarehouseName?: string
|
|
privateDestinationAddress?: string; privateDestinationPostalCode?: string; privateDestinationContactName?: string; privateDestinationContactPhone?: string
|
|
deliveryAddress?: string; packageCount: number; volumeCbm: number; deliveryMethod?: string; estimatedArrivalAt?: string; estimatedShipAt?: string
|
|
fbaShipmentNo?: string; trackingNo?: string; fbaBoxNos?: string; remark?: string; needRelabel: boolean; idempotencyKey: string
|
|
labelDeadline?: string; pickupContactName?: string; pickupContactPhone?: string; relabelContactName?: string; relabelContactPhone?: string; relabelChargeAmount?: number; relabelDeliveryMethod?: RelabelDeliveryMethod
|
|
items: Array<{ skuCode?: string; productNameZh?: string; quantity: number; packageCount: number; shippingMark?: string; cartonMarks?: string[] }>
|
|
relabelItems?: Array<{ operationType: 'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU'; customerCartonMarks: string[]; originalSku?: string; newSku?: string; quantity: number; fbaCartonMark?: string; destinationWarehouseCode?: string; remark?: string }>
|
|
}
|
|
export interface RelabelCreate {
|
|
customerOrderNo: string; operationWarehouseId: string; labelDeadline: string; contactName: string; contactPhone: string; chargeAmount?: number
|
|
remark?: string; idempotencyKey: string; deliveryMethod?: RelabelDeliveryMethod; printShippingLabel?: boolean
|
|
items: Array<{ sourceInventoryBalanceId?: string; sourceInventoryPositionId?: string; sourceInboundOrderId?: string; returnCartonId?: string; skuCode?: string; productNameZh: string; totalPackageCount: number; notifiedPackageCount: number; shipmentNo: string; trackingCode?: string; newSkuCode?: string; labelRequirement?: 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 }>; remark?: string }>
|
|
}
|
|
|
|
export function listRelabelHistoricalOrders(operationWarehouseId: string, keyword?: string) {
|
|
return apiRequest<HistoricalOrderSummary[]>({ method: 'GET', url: '/merchant/v1/last-leg/relabel-applications/historical-orders', params: { operationWarehouseId, keyword: keyword || undefined } })
|
|
}
|
|
export function listTransfers(params: Record<string, unknown>) {
|
|
return apiRequest<PageResult<TransferSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/warehouse-transfers', params })
|
|
}
|
|
export function getTransfer(transferNo: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'GET', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}` })
|
|
}
|
|
export function createTransfer(payload: TransferCreate) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: '/merchant/v1/last-leg/warehouse-transfers', data: payload })
|
|
}
|
|
export function updateTransfer(transferNo: string, payload: TransferCreate) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/save-draft`, data: payload })
|
|
}
|
|
export function submitTransfer(transferNo: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/submit` })
|
|
}
|
|
/** 补充既有SKU的预报箱数,原箱数快照用于并发核对与安全重试。 */
|
|
export function increaseTransferForecastCartons(transferNo: string, payload: { expectedPackageCount: number; packageCount: number }) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/increase-forecast-cartons`, data: payload })
|
|
}
|
|
export function listRelabels(params: Record<string, unknown>) {
|
|
return apiRequest<PageResult<RelabelSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/relabel-applications', params })
|
|
}
|
|
export function getRelabel(relabelNo: string) {
|
|
return apiRequest<RelabelDetail>({ method: 'GET', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}` })
|
|
}
|
|
export function confirmTransferMeasurement(transferNo: string, remark?: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/confirm-measurement`, data: { idempotencyKey: createIdempotencyKey(), remark } })
|
|
}
|
|
export interface TransferCartonLabel { transferNo: string; cartons: Array<{ cartonMark: string; customerCartonMark?: string; cartonIndex: number; totalCartons: number; status: string }>; printableHtml: string }
|
|
export function getTransferCartonLabel(transferNo: string) {
|
|
return apiRequest<TransferCartonLabel>({ method: 'GET', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/carton-label` })
|
|
}
|
|
export function downloadTransferMeasurementPhoto(transferNo: string, fileId: string | number) {
|
|
return apiBlobRequest({ method: 'GET', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/measurement-photos/files/${encodeURIComponent(fileId)}/download` })
|
|
}
|
|
|
|
export function listRelabelInventoryCandidates(operationWarehouseId: string, inboundCode?: string) {
|
|
return apiRequest<RelabelInventoryCandidate[]>({
|
|
method: 'GET', url: '/merchant/v1/last-leg/relabel-applications/inventory-candidates',
|
|
params: { operationWarehouseId, inboundCode: inboundCode || undefined },
|
|
})
|
|
}
|
|
export function createRelabel(payload: RelabelCreate) {
|
|
return apiRequest<RelabelDetail>({ method: 'POST', url: '/merchant/v1/last-leg/relabel-applications', data: payload })
|
|
}
|
|
export function resubmitRelabel(relabelNo: string, payload: { labelDeadline: string; contactName: string; contactPhone: string; remark?: string; idempotencyKey: string }) {
|
|
return apiRequest<RelabelDetail>({ method: 'PUT', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/resubmit`, data: payload })
|
|
}
|
|
export function uploadRelabelShippingLabel(relabelNo: string, file: File) {
|
|
const data = new FormData(); data.append('file', file)
|
|
return apiRequest<RelabelDetail>({ method: 'POST', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/shipping-label`, data, headers: { 'Content-Type': 'multipart/form-data' } })
|
|
}
|
|
export function uploadRelabelFile(relabelNo: string, scope: 'CARTON_TOTAL' | 'SKU_TOTAL' | 'ITEM', file: File, itemId?: string) {
|
|
const data = new FormData(); data.append('file', file)
|
|
return apiRequest<RelabelDetail>({ method: 'POST', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/files`, params: { scope, itemId }, data, headers: { 'Content-Type': 'multipart/form-data' } })
|
|
}
|
|
export function confirmRelabelCompletion(relabelNo: string, remark?: string) {
|
|
return apiRequest<RelabelDetail>({ method: 'POST', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/confirm-completion`, data: { idempotencyKey: createIdempotencyKey(), remark } })
|
|
}
|
|
export function downloadRelabelCompletionPhoto(relabelNo: string, fileId: string) {
|
|
return apiBlobRequest({ method: 'GET', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/completion-photos/${encodeURIComponent(fileId)}/download` })
|
|
}
|
|
export function listDiscards(params: Record<string, unknown>) {
|
|
return apiRequest<PageResult<DiscardSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards', params })
|
|
}
|
|
export function getDiscard(discardNo: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'GET', url: `/merchant/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}` })
|
|
}
|
|
export function listDiscardCandidates(inboundCode?: string) {
|
|
return apiRequest<DiscardCandidate[]>({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards/inventory-candidates', params: { inboundCode: inboundCode || undefined } })
|
|
}
|
|
export function createDiscard(payload: { items: Array<{ orderId?: string; returnCartonId?: string; inventoryPositionId?: string; cartonIds?: string[]; skuCode: string; discardPackages: number }>; idempotencyKey: string; remark?: string }) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: '/merchant/v1/last-leg/cargo-discards', data: payload })
|
|
}
|
|
export function cancelDiscard(discardNo: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/cancel` })
|
|
}
|
|
export function confirmDiscardCompletion(discardNo: string) {
|
|
return apiRequest<Record<string, unknown>>({ method: 'POST', url: `/merchant/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/confirm-completion` })
|
|
}
|
|
|
|
export interface DiscardPhoto { fileId: string; palletNo: string; fileName: string; createdAt: string; submittedAt: string }
|
|
export function downloadDiscardPhoto(discardNo: string, fileId: string) {
|
|
return apiBlobRequest({ method: 'GET', url: `/merchant/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/photos/files/${encodeURIComponent(fileId)}/download` })
|
|
}
|
|
|
|
export interface TransferSourceCarton { cartonMark: string; productNameZh?: string; quantity?: number; destinationWarehouseCode?: string; destinationWarehouseName?: string; items: Array<{skuCode?: string; quantity?: number}> }
|
|
export function getTransferSourceCartons(operatingWarehouseId: string, inboundCode: string) {
|
|
return apiRequest<TransferSourceCarton[]>({method:'GET',url:'/merchant/v1/last-leg/warehouse-transfers/inventory-source-cartons',params:{operatingWarehouseId,inboundCode}})
|
|
}
|
|
|
|
/** 通过客户授权入口读取申请绑定的资料,不使用平台文件地址。 */
|
|
export function downloadRelabelDocument(relabelNo: string, fileId: string, scope: string, itemId?: string) {
|
|
const base = '/merchant/v1/last-leg/relabel-applications/' + encodeURIComponent(relabelNo)
|
|
return apiBlobRequest({ method: 'GET', url: scope === 'SHIPPING' ? base + '/shipping-label/' + encodeURIComponent(fileId) + '/download' : base + '/files/' + encodeURIComponent(fileId) + '/download', params: scope === 'SHIPPING' ? undefined : { scope, itemId } })
|
|
}
|