import { apiRequest } from '@/api/http' import type { MerchantInboundBilling, MerchantInboundFeeSnapshot, MerchantInboundResult, MerchantInboundSummary, MerchantPalletLocation, } from '@/modules/first-leg/types/inboundBilling' interface MerchantInboundBillingWireResponse { inbound: MerchantInboundResult measurementSummary: MerchantInboundBilling['measurementSummary'] measurementItems: MerchantInboundBilling['measurementItems'] billingBasis: MerchantInboundBilling['billingBasis'] latestFee: MerchantInboundFeeSnapshot } function sanitizeSummary(summary: MerchantInboundSummary): MerchantInboundSummary { return { id: summary.id, orderId: summary.orderId, orderNo: summary.orderNo, inboundCode: summary.inboundCode, processStatus: summary.processStatus, feeStatus: summary.feeStatus, notificationStatus: summary.notificationStatus, warehouseName: summary.warehouseName, deliveryWarehouseName: summary.deliveryWarehouseName, destinationCode: summary.destinationCode, destinationName: summary.destinationName, totalPackageCount: summary.totalPackageCount, actualGrossWeightKg: summary.actualGrossWeightKg, actualNetWeightKg: summary.actualNetWeightKg, actualVolumeCbm: summary.actualVolumeCbm, volumeWeightKg: summary.volumeWeightKg, chargeableWeightKg: summary.chargeableWeightKg, chargeableVolumeCbm: summary.chargeableVolumeCbm, palletCount: summary.palletCount, locationCodes: summary.locationCodes, measureCompletedAt: summary.measureCompletedAt, inboundedAt: summary.inboundedAt, } } function sanitizePalletLocation(item: MerchantPalletLocation): MerchantPalletLocation { return { allocationNo: item.allocationNo, warehouseId: item.warehouseId, palletId: item.palletId, palletCode: item.palletCode, palletCapacityCbm: item.palletCapacityCbm, allocatedVolumeCbm: item.allocatedVolumeCbm, allocatedPackageCount: item.allocatedPackageCount, locationId: item.locationId, locationCode: item.locationCode, allocationStatus: item.allocationStatus, } } function sanitizeFee(fee: MerchantInboundFeeSnapshot): MerchantInboundFeeSnapshot { return { feeId: fee.feeId, feeNo: fee.feeNo, feeVersion: fee.feeVersion, feeStatus: fee.feeStatus, inboundId: fee.inboundId, inboundCode: fee.inboundCode, measurementId: fee.measurementId == null ? null : String(fee.measurementId), measurementSource: fee.measurementSource, chargeRuleId: fee.chargeRuleId, ruleCode: fee.ruleCode, chargeRuleVersion: fee.chargeRuleVersion, carrierRateId: fee.carrierRateId, rateCode: fee.rateCode, carrierRateVersion: fee.carrierRateVersion, customerId: fee.customerId, customerName: fee.customerName, originWarehouseId: fee.originWarehouseId, originWarehouseName: fee.originWarehouseName, businessType: fee.businessType, channelCode: fee.channelCode, carrierId: fee.carrierId, carrierName: fee.carrierName, destinationCode: fee.destinationCode, taxMode: fee.taxMode, chargeMode: fee.chargeMode, conversionFactor: fee.conversionFactor, roundingScale: fee.roundingScale, roundingMode: fee.roundingMode, actualWeightKg: fee.actualWeightKg, actualVolumeCbm: fee.actualVolumeCbm, convertedQuantity: fee.convertedQuantity, chargeableQuantity: fee.chargeableQuantity, chargeUnit: fee.chargeUnit, unitPrice: fee.unitPrice, subtotalAmount: fee.subtotalAmount, additionalAmount: fee.additionalAmount, totalAmount: fee.totalAmount, currency: fee.currency, rateEffectiveFrom: fee.rateEffectiveFrom, rateEffectiveTo: fee.rateEffectiveTo, calculatedAt: fee.calculatedAt, confirmedAt: fee.confirmedAt, sentAt: fee.sentAt, nextProcessStatus: fee.nextProcessStatus, customerConfirmationStatus: fee.customerConfirmationStatus, customerConfirmedBy: fee.customerConfirmedBy == null ? null : String(fee.customerConfirmedBy), customerConfirmedByName: fee.customerConfirmedByName, customerConfirmedAt: fee.customerConfirmedAt, } } function sanitizeMeasurementSummary(item: MerchantInboundBilling['measurementSummary']): MerchantInboundBilling['measurementSummary'] { return { measurementId: item.measurementId == null ? null : String(item.measurementId), sourceType: item.sourceType, measurementStatus: item.measurementStatus, actualPackageCount: item.actualPackageCount, actualGrossWeightKg: item.actualGrossWeightKg, actualNetWeightKg: item.actualNetWeightKg, actualVolumeCbm: item.actualVolumeCbm, volumeWeightKg: item.volumeWeightKg, actualGoodsName: item.actualGoodsName, calculationSource: item.calculationSource, } } function sanitizeMeasurementItem(item: MerchantInboundBilling['measurementItems'][number]): MerchantInboundBilling['measurementItems'][number] { return { id: String(item.id), itemNo: item.itemNo, boxSpecification: item.boxSpecification, packageCount: item.packageCount, lengthCm: item.lengthCm, widthCm: item.widthCm, heightCm: item.heightCm, unitVolumeCbm: item.unitVolumeCbm, totalVolumeCbm: item.totalVolumeCbm, volumeWeightKg: item.volumeWeightKg, chargeRuleId: item.chargeRuleId == null ? null : String(item.chargeRuleId), coefficientMode: item.coefficientMode, calculationRuleVersion: item.calculationRuleVersion, } } function sanitizeInboundBilling(response: MerchantInboundBillingWireResponse): MerchantInboundBilling { return { inbound: { summary: sanitizeSummary(response.inbound.summary), publicRemark: response.inbound.publicRemark, palletLocations: response.inbound.palletLocations.map(sanitizePalletLocation), }, measurementSummary: sanitizeMeasurementSummary(response.measurementSummary), measurementItems: (response.measurementItems || []).map(sanitizeMeasurementItem), billingBasis: response.billingBasis, latestFee: sanitizeFee(response.latestFee), } } export async function getMerchantInboundBilling(orderId: string): Promise { const response = await apiRequest({ method: 'GET', url: `/merchant/v1/first-leg/orders/${orderId}/inbound-billing`, }) return sanitizeInboundBilling(response) } function createFeeConfirmationKey(orderId: string, feeVersion: number) { const randomPart = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}` return `inbound-fee-confirm-${orderId}-${feeVersion}-${randomPart}` } export async function confirmMerchantInboundFee(orderId: string, expectedFeeVersion: number, idempotencyKey = createFeeConfirmationKey(orderId, expectedFeeVersion)) { const response = await apiRequest({ method: 'POST', url: `/merchant/v1/first-leg/orders/${encodeURIComponent(orderId)}/inbound-billing/confirm`, headers: { 'Idempotency-Key': idempotencyKey }, data: { expectedFeeVersion }, }) return sanitizeFee(response) }