import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' export type PriceBookStatus = 'PENDING_CONFIRMATION' | 'ACTIVE' | 'SUPERSEDED' | 'FAILED' export type PriceModel = 'FBA_WAREHOUSE_RATE' | 'REGION_WEIGHT_TIER' export type PricingTaxMode = 'TAX_INCLUDED' | 'TAX_EXCLUDED' export type PricingDestinationType = 'FBA' | 'OVERSEAS' export type PriceBookChargeUnit = 'KG' | 'CBM' type ApiId = string | number type ApiNumber = string | number export interface PlatformPriceBookVersion { id: string customerId: string versionNo: number originalFileName: string sourceFileId: string | null sourceSha256: string | null status: PriceBookStatus channelCount: number rateCount: number blockingErrorCount: number warningCount: number parseIssuesJson: string | null createdBy: string | null createdAt: string | null publishedBy: string | null publishedAt: string | null supersededAt: string | null } /** Read-only channel data parsed from the uploaded workbook. */ export interface PlatformPriceBookParsedChannel { channelCode: string channelName: string priceModel: PriceModel sourceSheetName: string estimatedDepartureDate: string | null estimatedDepartureText: string | null latestWarehouseEntry: string | null vesselName: string | null voyageNo: string | null referenceTransit: string | null surchargeDescription: string | null remark: string | null } /** Read-only price row parsed from the uploaded workbook. */ export interface PlatformPriceBookParsedRate { id: string | null channelCode: string priceModel: PriceModel originWarehouseId: string | null originWarehouseCode: string originWarehouseName: string destinationType: PricingDestinationType | null destinationCode: string | null destinationWarehouseId: string | null destinationName: string | null regionCode: string | null pricingTaxMode: PricingTaxMode | null chargeUnit: PriceBookChargeUnit weightFromKg: number | null unitPrice: number currency: string sourceSheetName: string sourceRowNumber: number sourceDestinationText: string | null remark: string | null } export interface PlatformPriceBookParseIssue { severity: 'BLOCKING' | 'WARNING' | string issueCode: string message: string sourceSheetName: string | null sourceRowNumber: number | null sourceCell: string | null } export interface PlatformPriceBookDetail { version: PlatformPriceBookVersion channels: PlatformPriceBookParsedChannel[] rates: PlatformPriceBookParsedRate[] issues: PlatformPriceBookParseIssue[] } export interface PriceBookConfirmationRequest { originMappings: Array<{ originWarehouseCode: string warehouseId: string }> overseasMappings: Array<{ sourceDestinationName: string warehouseId: string }> } export interface PlatformPriceBookQuery { customerId?: string status?: PriceBookStatus | string } interface PriceBookVersionPayload extends Omit { id: ApiId customerId: ApiId versionNo: ApiNumber sourceFileId?: ApiId | null channelCount?: ApiNumber | null rateCount?: ApiNumber | null blockingErrorCount?: ApiNumber | null warningCount?: ApiNumber | null createdBy?: ApiId | null publishedBy?: ApiId | null } interface PriceBookParsedChannelPayload extends Omit { estimatedDepartureDate?: string | null estimatedDepartureText?: string | null latestWarehouseEntry?: string | null vesselName?: string | null voyageNo?: string | null referenceTransit?: string | null surchargeDescription?: string | null remark?: string | null } interface PriceBookParsedRatePayload extends Omit { id?: ApiId | null originWarehouseId?: ApiId | null destinationWarehouseId?: ApiId | null weightFromKg?: ApiNumber | null unitPrice: ApiNumber sourceRowNumber: ApiNumber } interface PriceBookParseIssuePayload extends Omit { sourceRowNumber?: ApiNumber | null } interface PriceBookDetailPayload { version: PriceBookVersionPayload channels?: PriceBookParsedChannelPayload[] | null rates?: PriceBookParsedRatePayload[] | null issues?: PriceBookParseIssuePayload[] | null } const basePath = '/platform/v1/fba-pricing/carrier-rates/price-books' function nullableId(value: ApiId | null | undefined) { return value == null ? null : String(value) } function nullableText(value: string | null | undefined) { return value ?? null } function numberValue(value: ApiNumber | null | undefined) { const number = Number(value ?? 0) return Number.isFinite(number) ? number : 0 } function normalizeVersion(value: PriceBookVersionPayload): PlatformPriceBookVersion { return { ...value, id: String(value.id), customerId: String(value.customerId), versionNo: numberValue(value.versionNo), sourceFileId: nullableId(value.sourceFileId), channelCount: numberValue(value.channelCount), rateCount: numberValue(value.rateCount), blockingErrorCount: numberValue(value.blockingErrorCount), warningCount: numberValue(value.warningCount), parseIssuesJson: nullableText(value.parseIssuesJson), createdBy: nullableId(value.createdBy), createdAt: nullableText(value.createdAt), publishedBy: nullableId(value.publishedBy), publishedAt: nullableText(value.publishedAt), supersededAt: nullableText(value.supersededAt), } } function normalizeChannel(value: PriceBookParsedChannelPayload): PlatformPriceBookParsedChannel { return { ...value, estimatedDepartureDate: nullableText(value.estimatedDepartureDate), estimatedDepartureText: nullableText(value.estimatedDepartureText), latestWarehouseEntry: nullableText(value.latestWarehouseEntry), vesselName: nullableText(value.vesselName), voyageNo: nullableText(value.voyageNo), referenceTransit: nullableText(value.referenceTransit), surchargeDescription: nullableText(value.surchargeDescription), remark: nullableText(value.remark), } } function normalizeRate(value: PriceBookParsedRatePayload): PlatformPriceBookParsedRate { return { ...value, id: nullableId(value.id), originWarehouseId: nullableId(value.originWarehouseId), destinationWarehouseId: nullableId(value.destinationWarehouseId), destinationType: value.destinationType ?? null, destinationCode: nullableText(value.destinationCode), destinationName: nullableText(value.destinationName), regionCode: nullableText(value.regionCode), pricingTaxMode: value.pricingTaxMode ?? null, weightFromKg: value.weightFromKg == null ? null : numberValue(value.weightFromKg), unitPrice: numberValue(value.unitPrice), sourceRowNumber: numberValue(value.sourceRowNumber), sourceDestinationText: nullableText(value.sourceDestinationText), remark: nullableText(value.remark), } } function normalizeIssue(value: PriceBookParseIssuePayload): PlatformPriceBookParseIssue { return { ...value, sourceSheetName: nullableText(value.sourceSheetName), sourceRowNumber: value.sourceRowNumber == null ? null : numberValue(value.sourceRowNumber), sourceCell: nullableText(value.sourceCell), } } function normalizeDetail(value: PriceBookDetailPayload): PlatformPriceBookDetail { return { version: normalizeVersion(value.version), channels: (value.channels || []).map(normalizeChannel), rates: (value.rates || []).map(normalizeRate), issues: (value.issues || []).map(normalizeIssue), } } export async function listPlatformPriceBooks(query: PlatformPriceBookQuery = {}): Promise { const status = query.status?.trim() const customerId = query.customerId?.trim() || '0' const response = await http.get, ApiEnvelope>(basePath, { params: status ? { customerId, status } : { customerId }, }) return unwrapApiResponse(response).map(normalizeVersion) } export async function getPlatformPriceBook(versionId: string): Promise { const response = await http.get, ApiEnvelope>( `${basePath}/${encodeURIComponent(versionId)}`, ) return normalizeDetail(unwrapApiResponse(response)) } export async function uploadPlatformPriceBook(file: File, customerId = '0'): Promise { const form = new FormData() form.append('file', file) const response = await http.post, ApiEnvelope>(basePath, form, { params: { customerId: customerId.trim() || '0' }, }) return normalizeDetail(unwrapApiResponse(response)) } export async function publishPlatformPriceBook( versionId: string, request: PriceBookConfirmationRequest, ): Promise { const response = await http.post, ApiEnvelope>( `${basePath}/${encodeURIComponent(versionId)}/publish`, request, ) return normalizeDetail(unwrapApiResponse(response)) } export function downloadPlatformPriceBookSource(versionId: string) { return http.get(`${basePath}/${encodeURIComponent(versionId)}/source`, { responseType: 'blob' }) }