fix: support id-only overseas destinations
This commit is contained in:
+5
-2
@@ -188,8 +188,11 @@ public class InboundStockReceiptService {
|
||||
if (packages <= 0 || pallet.getWarehousePalletId() == null) {
|
||||
throw conflict("Stock-in requires picked packages and an assigned warehouse pallet");
|
||||
}
|
||||
String destinationCode = required(first(line.getDestinationWarehouseCode(), order.getFbaWarehouseCode()),
|
||||
"Destination warehouse code is required for stock-in");
|
||||
String destinationCode = first(line.getDestinationWarehouseCode(), order.getFbaWarehouseCode());
|
||||
if (destinationCode == null && trim(line.getDestinationWarehouseId()) == null) {
|
||||
throw conflict("Destination warehouse ID or code is required for stock-in");
|
||||
}
|
||||
if (destinationCode == null) destinationCode = "";
|
||||
String operationKey = "PDA_STOCK_IN:" + task.getId() + ":" + allocation.getId() + ":" + sku.skuCode();
|
||||
String overseasWarehouseId = required(order.getOverseasWarehouseId(),
|
||||
"Actual overseas warehouse is required for stock-in");
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ public final class InventoryCommandRequests {
|
||||
String overseasWarehouseCode,
|
||||
String overseasWarehouseName,
|
||||
String destinationWarehouseId,
|
||||
@NotBlank String destinationWarehouseCode,
|
||||
String destinationWarehouseCode,
|
||||
String destinationWarehouseName,
|
||||
@NotNull Long inboundOrderId,
|
||||
@NotBlank String inboundOrderNo,
|
||||
@@ -44,7 +44,7 @@ public final class InventoryCommandRequests {
|
||||
String overseasWarehouseCode,
|
||||
String overseasWarehouseName,
|
||||
String destinationWarehouseId,
|
||||
@NotBlank String destinationWarehouseCode,
|
||||
String destinationWarehouseCode,
|
||||
String destinationWarehouseName,
|
||||
@NotNull Long inboundOrderId,
|
||||
@NotBlank String inboundOrderNo,
|
||||
|
||||
+10
-4
@@ -233,8 +233,14 @@ public class InventoryCommandService {
|
||||
String inboundCode, String requestedSkuCode, String productNameZh,
|
||||
String productNameEn) {
|
||||
String skuCode = sku(requestedSkuCode);
|
||||
String destinationId = trim(destinationWarehouseId);
|
||||
String destinationCode = trim(destinationWarehouseCode);
|
||||
if (destinationId == null && destinationCode == null) {
|
||||
throw badRequest("Destination warehouse ID or code is required");
|
||||
}
|
||||
if (destinationCode == null) destinationCode = "";
|
||||
InventoryBalance balance = balances.lockDimension(customerId, overseasWarehouseId,
|
||||
destinationWarehouseCode, inboundOrderId, inboundCode);
|
||||
destinationCode, inboundOrderId, inboundCode);
|
||||
if (balance != null) {
|
||||
mergeSkuSnapshot(balance, skuCode);
|
||||
return balance;
|
||||
@@ -246,8 +252,8 @@ public class InventoryCommandService {
|
||||
balance.setOverseasWarehouseId(overseasWarehouseId.trim());
|
||||
balance.setOverseasWarehouseCode(trim(overseasWarehouseCode));
|
||||
balance.setOverseasWarehouseName(trim(overseasWarehouseName));
|
||||
balance.setDestinationWarehouseId(trim(destinationWarehouseId));
|
||||
balance.setDestinationWarehouseCode(destinationWarehouseCode.trim());
|
||||
balance.setDestinationWarehouseId(destinationId);
|
||||
balance.setDestinationWarehouseCode(destinationCode);
|
||||
balance.setDestinationWarehouseName(trim(destinationWarehouseName));
|
||||
balance.setInboundOrderId(inboundOrderId);
|
||||
balance.setInboundOrderNo(inboundOrderNo.trim());
|
||||
@@ -264,7 +270,7 @@ public class InventoryCommandService {
|
||||
return balance;
|
||||
} catch (DuplicateKeyException exception) {
|
||||
InventoryBalance concurrent = balances.lockDimension(customerId, overseasWarehouseId,
|
||||
destinationWarehouseCode, inboundOrderId, inboundCode);
|
||||
destinationCode, inboundOrderId, inboundCode);
|
||||
if (concurrent != null) {
|
||||
mergeSkuSnapshot(concurrent, skuCode);
|
||||
return concurrent;
|
||||
|
||||
+16
@@ -92,6 +92,22 @@ class InventoryCommandServiceTest {
|
||||
verify(ledgers).insert(any(InventoryLedger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registersIdOnlyOverseasDestinationWithoutInventingAWarehouseCode() {
|
||||
when(balances.lockDimension("100", "WH-1", "", 101L, "IN-CODE-1")).thenReturn(null);
|
||||
InventoryCommandRequests.InTransit request = new InventoryCommandRequests.InTransit(
|
||||
"Customer 100", "WH-1", "WH1", "Warehouse 1", "DEST-OVERSEAS", null,
|
||||
"New York warehouse", 101L, "IN-1", "IN-CODE-1", "SKU-1", "Product 1", null,
|
||||
1, "ORD-1", "EVT-1", "transit-id-only", null);
|
||||
|
||||
service.registerInTransit("100", "SYSTEM", request);
|
||||
|
||||
ArgumentCaptor<InventoryBalance> inserted = ArgumentCaptor.forClass(InventoryBalance.class);
|
||||
verify(balances).insert(inserted.capture());
|
||||
assertEquals("DEST-OVERSEAS", inserted.getValue().getDestinationWarehouseId());
|
||||
assertEquals("", inserted.getValue().getDestinationWarehouseCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void stockInTransfersPackagesFromInTransitToAvailable() {
|
||||
InventoryBalance balance = balance(200L, 0);
|
||||
|
||||
@@ -56,9 +56,9 @@ function oceanDraft(context, suffix, master) {
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureOceanBooking(context, suffix, master) {
|
||||
if (context.createdResources?.oceanBooking?.id) {
|
||||
const existingId = id(context.createdResources.oceanBooking.id)
|
||||
async function ensureOceanBooking(context, suffix, master, preferredOceanBookingId = null) {
|
||||
const existingId = id(preferredOceanBookingId || context.createdResources?.oceanBooking?.id)
|
||||
if (existingId) {
|
||||
const response = await request(context, 'GET', `${OCEAN}/${existingId}`)
|
||||
const detail = context.apiSuccess(MODULE, '读取船期关联订舱真实柜次分配', response,
|
||||
`oceanBookingId=${existingId}`, OPTIONS)
|
||||
@@ -118,10 +118,10 @@ export async function run(context) {
|
||||
|
||||
const master = await resolveOceanBookingMaster(context, MODULE)
|
||||
if (!master) return
|
||||
const ocean = await ensureOceanBooking(context, suffix, master)
|
||||
if (!ocean?.id) return
|
||||
const flow = context.createdResources?.phase3Loading
|
||||
|| context.createdResources?.phase3ContainerLoading
|
||||
const ocean = await ensureOceanBooking(context, suffix, master, flow?.oceanBookingId)
|
||||
if (!ocean?.id) return
|
||||
const containerBookingId = ['COMPLETED', 'LOADED'].includes(flow?.finalStatus)
|
||||
? id(flow.containerBookingId) : null
|
||||
const knownAllocation = containerBookingId
|
||||
|
||||
@@ -503,6 +503,7 @@ test('downstream container consumers prefer the phase3Loading fixture', () => {
|
||||
assert.doesNotMatch(serviceCenter, /链路承运商-/)
|
||||
assert.doesNotMatch(serviceCenter, /customerName: `服务中心链路客户-/)
|
||||
const schedules = source('./phase3-port-schedules.mjs')
|
||||
assert.match(schedules, /ensureOceanBooking\(context, suffix, master, flow\?\.oceanBookingId\)/)
|
||||
assert.match(schedules, /allocatedContainerBookingIds: \(detail\.containers \|\| \[\]\)/)
|
||||
assert.match(schedules, /ocean\.allocatedContainerBookingIds\?\.includes\(containerBookingId\)/)
|
||||
assert.match(schedules, /if \(containerBookingId && !knownAllocation\)/)
|
||||
|
||||
Reference in New Issue
Block a user