277 lines
9.5 KiB
TypeScript
277 lines
9.5 KiB
TypeScript
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<PlatformPriceBookVersion,
|
|
'id' | 'customerId' | 'versionNo' | 'sourceFileId' | 'channelCount' | 'rateCount' | 'blockingErrorCount' | 'warningCount' | 'createdBy' | 'publishedBy'> {
|
|
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<PlatformPriceBookParsedChannel,
|
|
'estimatedDepartureDate' | 'estimatedDepartureText' | 'latestWarehouseEntry' | 'vesselName' | 'voyageNo'
|
|
| 'referenceTransit' | 'surchargeDescription' | 'remark'> {
|
|
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<PlatformPriceBookParsedRate,
|
|
'id' | 'originWarehouseId' | 'destinationWarehouseId' | 'weightFromKg' | 'unitPrice' | 'sourceRowNumber'> {
|
|
id?: ApiId | null
|
|
originWarehouseId?: ApiId | null
|
|
destinationWarehouseId?: ApiId | null
|
|
weightFromKg?: ApiNumber | null
|
|
unitPrice: ApiNumber
|
|
sourceRowNumber: ApiNumber
|
|
}
|
|
|
|
interface PriceBookParseIssuePayload extends Omit<PlatformPriceBookParseIssue, 'sourceRowNumber'> {
|
|
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<PlatformPriceBookVersion[]> {
|
|
const status = query.status?.trim()
|
|
const customerId = query.customerId?.trim() || '0'
|
|
const response = await http.get<ApiEnvelope<PriceBookVersionPayload[]>, ApiEnvelope<PriceBookVersionPayload[]>>(basePath, {
|
|
params: status ? { customerId, status } : { customerId },
|
|
})
|
|
return unwrapApiResponse(response).map(normalizeVersion)
|
|
}
|
|
|
|
export async function getPlatformPriceBook(versionId: string): Promise<PlatformPriceBookDetail> {
|
|
const response = await http.get<ApiEnvelope<PriceBookDetailPayload>, ApiEnvelope<PriceBookDetailPayload>>(
|
|
`${basePath}/${encodeURIComponent(versionId)}`,
|
|
)
|
|
return normalizeDetail(unwrapApiResponse(response))
|
|
}
|
|
|
|
export async function uploadPlatformPriceBook(file: File, customerId = '0'): Promise<PlatformPriceBookDetail> {
|
|
const form = new FormData()
|
|
form.append('file', file)
|
|
const response = await http.post<ApiEnvelope<PriceBookDetailPayload>, ApiEnvelope<PriceBookDetailPayload>>(basePath, form, {
|
|
params: { customerId: customerId.trim() || '0' },
|
|
})
|
|
return normalizeDetail(unwrapApiResponse(response))
|
|
}
|
|
|
|
export async function publishPlatformPriceBook(
|
|
versionId: string,
|
|
request: PriceBookConfirmationRequest,
|
|
): Promise<PlatformPriceBookDetail> {
|
|
const response = await http.post<ApiEnvelope<PriceBookDetailPayload>, ApiEnvelope<PriceBookDetailPayload>>(
|
|
`${basePath}/${encodeURIComponent(versionId)}/publish`,
|
|
request,
|
|
)
|
|
return normalizeDetail(unwrapApiResponse(response))
|
|
}
|
|
|
|
export function downloadPlatformPriceBookSource(versionId: string) {
|
|
return http.get<Blob, Blob>(`${basePath}/${encodeURIComponent(versionId)}/source`, { responseType: 'blob' })
|
|
}
|