贴标申请,仓储转运
This commit is contained in:
-17
@@ -151,12 +151,6 @@ public class WarehouseTransferService {
|
|||||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||||
// A split relabel transfer may intentionally defer the whole-order
|
|
||||||
// destination: relabeled cartons go through relabel dispatch while the
|
|
||||||
// remaining cartons are bound later from warehouse-transfer outbound.
|
|
||||||
// All other transfer types must still select FBA or PRIVATE up front.
|
|
||||||
validateTransferDestinationRequired(request.destinationType(), request.fbaWarehouseCode(),
|
|
||||||
request.needRelabel() && request.relabelItems() != null && !request.relabelItems().isEmpty());
|
|
||||||
String key = request.idempotencyKey().trim();
|
String key = request.idempotencyKey().trim();
|
||||||
String requestHash = hash(request);
|
String requestHash = hash(request);
|
||||||
WarehouseTransferOrder replay = transfers.findKey(customerId, key);
|
WarehouseTransferOrder replay = transfers.findKey(customerId, key);
|
||||||
@@ -292,8 +286,6 @@ public class WarehouseTransferService {
|
|||||||
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
validateDestination(request.destinationType(), request.destinationWarehouseCode(), request.fbaWarehouseCode(),
|
||||||
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
request.privateDestinationAddress(), request.privateDestinationPostalCode(),
|
||||||
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
request.privateDestinationContactName(), request.privateDestinationContactPhone());
|
||||||
validateTransferDestinationRequired(request.destinationType(), request.fbaWarehouseCode(),
|
|
||||||
request.needRelabel() && request.relabelItems() != null && !request.relabelItems().isEmpty());
|
|
||||||
|
|
||||||
LastlegOrder order = requireTailOrder(customerId, transfer);
|
LastlegOrder order = requireTailOrder(customerId, transfer);
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
@@ -1508,15 +1500,6 @@ public class WarehouseTransferService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateTransferDestinationRequired(String destinationType, String fbaWarehouseCode,
|
|
||||||
boolean allowDeferredForRelabel) {
|
|
||||||
String type = trim(destinationType);
|
|
||||||
if (type == null && StringUtils.hasText(fbaWarehouseCode)) type = "FBA";
|
|
||||||
if ((type == null || "NONE".equalsIgnoreCase(type)) && !allowDeferredForRelabel) {
|
|
||||||
throw badRequest("仓储转运必须选择 FBA 仓或私人仓");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void applyDestination(WarehouseTransferOrder transfer, WarehouseTransferRequests.Create request) {
|
private static void applyDestination(WarehouseTransferOrder transfer, WarehouseTransferRequests.Create request) {
|
||||||
String type = trim(request.destinationType());
|
String type = trim(request.destinationType());
|
||||||
if (type == null && StringUtils.hasText(request.fbaWarehouseCode())) type = "FBA";
|
if (type == null && StringUtils.hasText(request.fbaWarehouseCode())) type = "FBA";
|
||||||
|
|||||||
@@ -1214,7 +1214,7 @@ paths:
|
|||||||
responses: { '200': { description: 当前客户仓储转运单分页 }, '403': { description: 缺少仓储转运查看权限 } }
|
responses: { '200': { description: 当前客户仓储转运单分页 }, '403': { description: 缺少仓储转运查看权限 } }
|
||||||
post:
|
post:
|
||||||
summary: 客户创建仓储转运单
|
summary: 客户创建仓储转运单
|
||||||
description: 权限 merchant:overseas-warehouse:transfer:create;客户范围只取认证 Token。
|
description: 权限 merchant:overseas-warehouse:transfer:create;客户范围只取认证 Token。目的去向可暂未确定(destinationType 为 NONE 或不传),允许保存草稿、编辑和提交;入库确认后可在出库时绑定 FBA 仓或私人仓,也可选择入库库存发起贴标申请出库;普通转运出库前必须补齐目的仓。
|
||||||
security: [{ bearerAuth: [] }]
|
security: [{ bearerAuth: [] }]
|
||||||
responses: { '200': { description: 创建后的仓储转运单 }, '403': { description: 缺少仓储转运创建权限 }, '409': { description: 幂等、库存或状态冲突 } }
|
responses: { '200': { description: 创建后的仓储转运单 }, '403': { description: 缺少仓储转运创建权限 }, '409': { description: 幂等、库存或状态冲突 } }
|
||||||
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}:
|
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}:
|
||||||
|
|||||||
+58
-10
@@ -47,6 +47,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.NullSource;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@@ -438,20 +441,65 @@ class WarehouseTransferServiceTest {
|
|||||||
verifyNoInteractions(transfers);
|
verifyNoInteractions(transfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void transferRejectsRequestWithoutDestination() {
|
@NullSource
|
||||||
|
@ValueSource(strings = {"NONE"})
|
||||||
|
void transferWithoutDestinationCanBeCreatedAndSubmitted(String destinationType) {
|
||||||
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
|
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
|
||||||
"IN-1", List.of(11L), "WH-1", null, "123 Pickup Road", 5,
|
"IN-1", List.of(11L), "WH-1", null, destinationType, null, null, null,
|
||||||
|
null, null, null, null, "123 Pickup Road", null, null, 5,
|
||||||
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
|
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
|
||||||
null, null, null, "FBA destination will be supplied later", false, "key-no-fba",
|
null, null, null, null, "Destination will be supplied before outbound", false,
|
||||||
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")));
|
null, null, null, null, null, "key-no-fba",
|
||||||
|
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")), null);
|
||||||
|
|
||||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
when(inboundSnapshots.findId("TENANT-A", "IN-1")).thenReturn(101L);
|
||||||
() -> service.create("TENANT-A", "USER-1", request));
|
when(orders.selectList(any())).thenReturn(List.of(historicalOrder()));
|
||||||
|
WarehouseTransferOrder draft = transfer(WarehouseTransferService.DRAFT);
|
||||||
|
draft.setFbaWarehouseCode(null);
|
||||||
|
when(transfers.findNo(eq("TENANT-A"), anyString())).thenReturn(draft);
|
||||||
|
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(draft);
|
||||||
|
when(orders.selectById(draft.getLastlegOrderId())).thenReturn(tailOrder());
|
||||||
|
when(transferItems.selectList(any())).thenReturn(List.of());
|
||||||
|
when(sourceOrders.selectList(any())).thenReturn(List.of());
|
||||||
|
|
||||||
assertEquals(400, exception.getStatusCode().value());
|
service.create("TENANT-A", "USER-1", request);
|
||||||
assertEquals("仓储转运必须选择 FBA 仓或私人仓", exception.getReason());
|
var result = service.submit("TENANT-A", "WTR-1", "USER-1");
|
||||||
verifyNoInteractions(transfers);
|
|
||||||
|
ArgumentCaptor<WarehouseTransferOrder> created = ArgumentCaptor.forClass(WarehouseTransferOrder.class);
|
||||||
|
verify(transfers).insert(created.capture());
|
||||||
|
assertNull(created.getValue().getDestinationType());
|
||||||
|
assertNull(created.getValue().getFbaWarehouseCode());
|
||||||
|
assertEquals(WarehouseTransferService.PENDING_REVIEW, result.status());
|
||||||
|
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void merchantCanClearDestinationWhenEditingDraft() {
|
||||||
|
WarehouseTransferOrder transfer = transfer(WarehouseTransferService.DRAFT);
|
||||||
|
transfer.setDestinationType("FBA");
|
||||||
|
transfer.setDestinationWarehouseCode("ONT8");
|
||||||
|
LastlegOrder order = tailOrder();
|
||||||
|
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||||
|
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
|
||||||
|
when(orders.selectById(transfer.getLastlegOrderId())).thenReturn(order);
|
||||||
|
when(transferItems.selectList(any())).thenReturn(List.of());
|
||||||
|
when(sourceOrders.selectList(any())).thenReturn(List.of());
|
||||||
|
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
|
||||||
|
"IN-1", List.of(11L), "WH-1", null, "NONE", null, null, null,
|
||||||
|
null, null, null, null, "123 Pickup Road", null, null, 5,
|
||||||
|
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
|
||||||
|
null, null, null, null, null, false, null, null, null, null, null, "edit-no-fba",
|
||||||
|
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")), null);
|
||||||
|
|
||||||
|
var result = service.updateDraft("TENANT-A", "WTR-1", "USER-1", request);
|
||||||
|
|
||||||
|
assertEquals(WarehouseTransferService.DRAFT, result.status());
|
||||||
|
assertNull(transfer.getDestinationType());
|
||||||
|
assertNull(transfer.getDestinationWarehouseCode());
|
||||||
|
assertNull(transfer.getFbaWarehouseCode());
|
||||||
|
assertNull(order.getFbaWarehouseCode());
|
||||||
|
verify(transfers).updateById(transfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user