feat(client): align account and order foundations

This commit is contained in:
2026-09-01 19:34:37 +08:00
parent c1e7ba411e
commit 1305ae4669
23 changed files with 602 additions and 258 deletions
@@ -29,6 +29,12 @@ test('declares the documented merchant warehouse and first-leg endpoints', () =>
assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg'/)
assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg\/inbound-code-policy'/)
assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg\/batch'/)
assert.match(apiSource, /url: '\/merchant\/v1\/order-addresses'/)
assert.match(apiSource, /`\/merchant\/v1\/order-addresses\/\$\{id\}`/)
assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg\/batch-imports\/template'/)
assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg\/batch-imports'/)
assert.match(apiSource, /params: \{ mode \}/)
assert.match(apiSource, /data\.append\('file', file\)/)
assert.match(apiSource, /`\/merchant\/v1\/orders\/first-leg\/\$\{id\}`/)
assert.match(apiSource, /`\/merchant\/v1\/orders\/first-leg\/\$\{id\}\/submit`/)
assert.match(apiSource, /`\/merchant\/v1\/warehouses\/\$\{id\}`/)
@@ -134,13 +140,14 @@ test('sends backend-required channel, pricing, customs, destination and cargo fi
assert.doesNotMatch(apiSource, /interface FirstLegOrderSaveRequest \{[^}]*customerId\??:/s)
assert.match(storeSource, /customerName: customerName\.trim\(\)/)
assert.match(storeSource, /destinationWarehouseId: draft\.destinationType === 'PRIVATE' \? undefined : draft\.destinationWarehouseId/)
assert.match(storeSource, /draft\.pickupMethod === 'PICKUP'.*draft\.shipperPartySnapshot/s)
assert.match(storeSource, /draft\.destinationType === 'PRIVATE'.*draft\.consigneePartySnapshot/s)
assert.match(storeSource, /senderAddressId: draft\.pickupMethod === 'PICKUP' \? draft\.senderAddressId : undefined/)
assert.match(storeSource, /privateDestinationAddressId: draft\.destinationType === 'PRIVATE' \? draft\.privateDestinationAddressId : undefined/)
assert.doesNotMatch(storeSource, /sender: partyPayload|receiver: partyPayload/)
for (const field of [
'customsHsCode', 'lengthCm', 'widthCm', 'heightCm', 'unitsPerPackage', 'totalQuantity',
'netWeightKg', 'unitDeclaredValue', 'material', 'usage', 'model', 'brand',
'productLengthCm', 'productWidthCm', 'productHeightCm', 'shipmentId', 'referenceId',
'productImageFileId',
'productImageFileId', 'woodenProduct',
]) {
assert.match(storeSource, new RegExp(`${field}: item\\.`))
}
+4 -35
View File
@@ -11,7 +11,6 @@ import {
submitMerchantFirstLegOrder,
updateMerchantFirstLegOrder,
type ApiFirstLegStatus,
type FirstLegOrderPartyPayload,
type FirstLegOrderSaveRequest,
type MerchantFirstLegOrder,
type MerchantFirstLegOrderRow,
@@ -40,10 +39,6 @@ export type ClientWarehouse = PlatformReceivingWarehouse & {
countryCode: string
}
const countryCodes: Readonly<Record<string, string>> = {
中国: 'CN', 美国: 'US', 德国: 'DE', 英国: 'GB', 加拿大: 'CA', 日本: 'JP', 澳大利亚: 'AU',
}
function clientStatus(status: ApiFirstLegStatus): FirstLegOrder['status'] {
if (status === 'DRAFT') return 'DRAFT'
if (status === 'SUBMITTED') return 'PENDING_REVIEW'
@@ -73,35 +68,7 @@ function apiTransport(draft: FirstLegOrderDraft): FirstLegOrderSaveRequest['tran
return 'SEA'
}
function partyPayload(snapshot: FirstLegPartySnapshot): FirstLegOrderPartyPayload {
return {
companyName: snapshot.name,
contactName: snapshot.contactName,
phone: snapshot.phone,
email: snapshot.email || undefined,
countryCode: countryCodes[snapshot.countryRegion] ?? snapshot.countryRegion.toUpperCase().slice(0, 2),
province: snapshot.province || undefined,
city: snapshot.city,
addressLine: snapshot.address,
postalCode: snapshot.postalCode || undefined,
}
}
function savePayload(draft: FirstLegOrderDraft, warehouse: ClientWarehouse, customerName: string): FirstLegOrderSaveRequest {
const sender = draft.pickupMethod === 'PICKUP' && draft.shipperPartySnapshot
? partyPayload({
...draft.shipperPartySnapshot,
name: draft.shipperPartySnapshot.name || customerName.trim() || '客户',
countryRegion: draft.shipperPartySnapshot.countryRegion || 'CN',
})
: undefined
const receiver = draft.destinationType === 'PRIVATE' && draft.consigneePartySnapshot
? partyPayload({
...draft.consigneePartySnapshot,
name: draft.destinationWarehouseName,
countryRegion: draft.destinationCountryCode.trim().toUpperCase(),
})
: undefined
return {
inboundCode: draft.inboundCode?.trim() || undefined,
customerName: customerName.trim(),
@@ -112,8 +79,8 @@ function savePayload(draft: FirstLegOrderDraft, warehouse: ClientWarehouse, cust
destinationWarehouseName: draft.destinationWarehouseName,
expectedShipDate: draft.expectedDepartureDate || undefined,
customerRemark: draft.customerRemark || undefined,
sender,
receiver,
senderAddressId: draft.pickupMethod === 'PICKUP' ? draft.senderAddressId : undefined,
privateDestinationAddressId: draft.destinationType === 'PRIVATE' ? draft.privateDestinationAddressId : undefined,
cargoes: draft.items.map((item) => ({
cargoName: item.cargoNameCn,
cargoNameEn: item.cargoNameEn || undefined,
@@ -129,6 +96,7 @@ function savePayload(draft: FirstLegOrderDraft, warehouse: ClientWarehouse, cust
declaredValue: item.declaredValue || undefined,
currency: item.currency || undefined,
dangerousGoods: item.dangerousGoods,
woodenProduct: item.woodenProduct,
cargoRemark: item.remark || undefined,
skuNo: item.sku || undefined,
upcNo: item.upc || undefined,
@@ -329,6 +297,7 @@ function mapOrder(order: MerchantFirstLegOrder, warehouses: PlatformReceivingWar
volumeCbm: item.volumeCbm, remark: item.cargoRemark ?? '',
packageType: item.packageType, hsCode: item.hsCode ?? '', declaredValue: item.declaredValue ?? 0,
currency: item.currency ?? 'USD', dangerousGoods: item.dangerousGoods ?? false,
woodenProduct: item.woodenProduct ?? false,
netWeightKg: item.netWeightKg ?? 0, unitDeclaredValue: item.unitDeclaredValue ?? 0,
material: item.material ?? '', usage: item.usage ?? item.productUsage ?? '', model: item.model ?? '', brand: item.brand ?? '',
productLengthCm: item.productLengthCm ?? 0, productWidthCm: item.productWidthCm ?? 0,