import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' import { listSharedReceivingOverseasWarehouseOptions } from '@/modules/iam' import { normalizeOverseasPage, type OverseasPagePayload } from './lastMileOrders' export interface RelabelOrderQuery { page: number size: number relabelNo?: string skuCode?: string status?: string } 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 notifiedPackageCount: number shipmentNo: string newSkuCode: string labelRequirement?: string productImageFileId?: string productImageFileName?: string productImageUrl?: string trackingCode?: string relabelFileId?: string relabelFileName?: string relabelFileUrl?: string } export interface CreateRelabelPayload { customerOrderNo: string operationWarehouseId: string deliveryMethod?: 'TRUCK' | 'EXPRESS' | 'PICKUP' | 'AMAZON_PICKUP' | 'CUSTOMER_PICKUP' printShippingLabel?: boolean labelDeadline: string contactName: string contactPhone: string chargeAmount: number remark?: string idempotencyKey: string items: CreateRelabelItemPayload[] } export interface RelabelWarehouseOptionDto { id: string customerId: string warehouseCode: string warehouseName: string warehouseType: string status: string } export interface RelabelSummaryDto { operationTypes?: Array<'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU' | 'RETURN_BULK_SKU_LABEL'> id: string relabelNo: string lastlegOrderId?: string | null lastlegOrderNo?: string | null customerOrderNo?: string | null overseasWarehouseId?: string | null labelDeadline?: string | null contactName?: string | null contactPhone?: string | null status?: string | null itemCount?: number | null totalPackageCount?: number | null notifiedPackageCount?: number | null skuCodes?: string | null productNameZh?: string | null labelRequirement?: string | null totalFileId?: string | null totalFileName?: string | null cartonTotalFileId?: string | null cartonTotalFileName?: string | null skuTotalFileId?: string | null skuTotalFileName?: string | null totalFileUrl?: string | null chargeAmount?: number | string | null chargeCurrency?: string | null walletStatus?: string | null walletErrorMessage?: string | null deliveryMethod?: string | null printShippingLabel?: boolean | null shippingLabelFileId?: string | null shippingLabelFileName?: string | null fulfillmentStatus?: string | null outboundOrderNo?: string | null customerConfirmedAt?: string | null reviewChannel?: 'TAIL_ORDER' | 'RELABEL_ORDER' | null reviewRemark?: string | null createdAt?: string | null } export interface RelabelItemDto { id: string operationType?: 'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU' | 'RETURN_BULK_SKU_LABEL' skuItems?: Array<{ skuCode: string; newSkuCode: string; quantity: number; remark?: string }> skuCode?: string | null productNameZh?: string | null totalPackageCount?: number | null notifiedPackageCount?: number | null newSkuCode?: string | null labelRequirement?: string | null relabelFileId?: string | null relabelFileName?: string | null relabelFileUrl?: string | null shipmentNo?: string | null trackingCode?: string | null remark?: string cartonMark?: string | null fbaCartonMark?: string | null destinationWarehouseId?: string | null destinationFbaWarehouseCode?: string | null } export interface RelabelHistoryDto { fromStatus?: string | null toStatus?: string | null operatorId?: string | null remark?: string createdAt?: string | null } export interface RelabelDetailDto { application: RelabelSummaryDto remark?: string | null items: RelabelItemDto[] history: RelabelHistoryDto[] completionEvidence?: { taskId: string; photoFileIds: string[]; completionRemark?: string | null; reviewerName?: string | null; reviewRemark?: string | null; completedAt?: string | null } | null } export interface RelabelQuantity { sourceUnitCounts?: Record | null labelPieceCount: number | string | null cartonCount: number | string | null skuCount: number | string duplicateCarton: boolean quantityUnit: string sourcePalletCount: number | string | null 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 unknownPieceOrders: number | string knownCartonCount: number | string unknownCartonOrders: number | string orders: Record } 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>('/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>('/platform/v1/last-leg/relabel-applications/quantity-summary', { params: query }) return unwrapApiResponse(response) } export interface ReturnPackingPlanBoxItem { skuCode: string quantity: number } export interface ReturnPackingPlanBox { boxNo?: number items: ReturnPackingPlanBoxItem[] length?: number width?: number height?: number weight?: number } export interface ReturnPackingPlanPayload { returnNos: string[] mode: 'BOX' | 'SKU' boxes: ReturnPackingPlanBox[] remark?: string idempotencyKey: string sourceCartonMarks?: string[] sourceDeductions?: Array<{ cartonMark: string; skuCode: string; quantity: number }> } export interface ReturnPackingPlanDto extends Omit { planNo: string customerId?: string overseasWarehouseId?: string status: string boxes: ReturnPackingPlanBox[] remark?: string | null createdAt?: string | null pushedAt?: string | null completedAt?: string | null resultCartons?: import('./returnInventory').ReturnCartonDetail[] sources?: Array<{ cartonMark: string; sourceReturnNo?: string; participation: 'FULL' | 'PARTIAL' | 'UNKNOWN'; items: ReturnPackingPlanBoxItem[]; originalItems: Array<{ skuCode: string; productNameZh?: string; quantity: number }> }> completionCartons?: import('./returnInventory').ReturnCartonLabel[] transfers?: import('./returnInventory').ReturnCartonMergeHistory[] completedBy?: string } export async function createReturnPackingPlan(payload: ReturnPackingPlanPayload) { const response = await http.post, ApiEnvelope>( '/platform/v1/last-leg/return-packing-plans', payload, ) return unwrapApiResponse(response) as ReturnPackingPlanDto } export interface ReturnPackingPlanQuery { page: number; size: number; status?: string; planNo?: string; customerId?: string; overseasWarehouseId?: string; skuCode?: string } export async function listReturnPackingPlans(query: ReturnPackingPlanQuery) { const response = await http.get>, ApiEnvelope>>('/platform/v1/last-leg/return-packing-plans', { params: query }) return normalizeOverseasPage(unwrapApiResponse(response)) } export async function reviewReturnPackingPlan(planNo: string) { const response = await http.post, ApiEnvelope>(`/platform/v1/last-leg/return-packing-plans/${encodeURIComponent(planNo)}/review`) return unwrapApiResponse(response) as ReturnPackingPlanDto } export async function cancelReturnPackingPlan(planNo: string) { const response = await http.post, ApiEnvelope>(`/platform/v1/last-leg/return-packing-plans/${encodeURIComponent(planNo)}/cancel`) return unwrapApiResponse(response) as ReturnPackingPlanDto } export async function listRelabelOrders(query: RelabelOrderQuery) { const response = await http.get>, ApiEnvelope>>( '/platform/v1/last-leg/relabel-applications', { params: query }, ) return normalizeOverseasPage(unwrapApiResponse(response)) } export async function getRelabelOrder(relabelNo: string) { const response = await http.get, ApiEnvelope>( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}`, ) return unwrapApiResponse(response) } export async function listRelabelWarehouseOptions() { const records: RelabelWarehouseOptionDto[] = (await listSharedReceivingOverseasWarehouseOptions()).map((warehouse) => ({ id: warehouse.id, customerId: '0', warehouseCode: warehouse.warehouseCode, warehouseName: warehouse.warehouseName, warehouseType: 'OVERSEAS', status: 'ACTIVE', })) return { records, page: 1, size: 200, total: records.length, pages: records.length ? 1 : 0 } } export async function createRelabelOrder(customerId: string, payload: CreateRelabelPayload) { const response = await http.post, ApiEnvelope>( '/platform/v1/last-leg/relabel-applications', payload, { params: { customerId } }, ) return unwrapApiResponse(response) } export async function changeRelabelOrderStatus(relabelNo: string, status: string, remark?: string) { const response = await http.put, ApiEnvelope>( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/status`, { status, remark }, ) return unwrapApiResponse(response) } export interface RelabelReviewTask { version: number taskStatus: string items?: Array<{ sourcePalletCode?: string | null; sourcePalletBasis?: string }> history?: Array<{ action: string; detail?: string }> } export async function getRelabelReviewTask(relabelNo: string) { const response = await http.get, ApiEnvelope>(`/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/review-task`) return unwrapApiResponse(response) } export async function returnRelabelForRework(relabelNo: string, expectedVersion: number, remark: string, idempotencyKey: string) { const response = await http.post, ApiEnvelope>(`/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/rework`, { decision: 'REWORK', expectedVersion, remark, idempotencyKey }) return unwrapApiResponse(response) } export async function confirmRelabelCompletion(relabelNo: string, remark?: string) { const response = await http.post, ApiEnvelope>( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/confirm-completion`, { idempotencyKey: crypto.randomUUID(), remark }, ) return unwrapApiResponse(response) } export async function uploadRelabelPdf(relabelNo: string, file: File, scope: 'TOTAL' | 'ITEM', itemId?: string) { const body = new FormData() body.append('file', file) const response = await http.post, ApiEnvelope>( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/files`, body, { params: { scope, itemId }, headers: { 'Content-Type': 'multipart/form-data' } }, ) return unwrapApiResponse(response) } export async function downloadRelabelPdf(relabelNo: string, fileId: string, scope: 'TOTAL' | 'ITEM', itemId?: string) { return http.get( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/files/${encodeURIComponent(fileId)}/download`, { params: { scope, itemId }, responseType: 'blob' }, ) } export async function downloadRelabelCompletionPhoto(relabelNo: string, fileId: string) { return http.get( `/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}/completion-photos/${encodeURIComponent(fileId)}/download`, { responseType: 'blob' }, ) } /** 通过平台授权入口读取贴标资料或面单。 */ export async function downloadRelabelDocument(relabelNo: string, fileId: string, scope: string, itemId?: string) { const base = '/platform/v1/last-leg/relabel-applications/' + encodeURIComponent(relabelNo) return http.get(scope === 'SHIPPING' ? base + '/shipping-label/' + encodeURIComponent(fileId) + '/download' : base + '/files/' + encodeURIComponent(fileId) + '/download', { params: scope === 'SHIPPING' ? undefined : { scope, itemId }, responseType: 'blob' }) }