fix(order): require dropship SKU before submission
This commit is contained in:
@@ -401,6 +401,7 @@ public class OrderApplicationService {
|
||||
"提交订单前必须完善货物品名、包装类型、箱数、毛重和体积");
|
||||
}
|
||||
validatePersistedCargoQuantity(cargo, cargoIndex, true);
|
||||
requireDropshipSku(order.getFulfillmentType(), cargo.getSkuNo(), cargoIndex);
|
||||
if (Boolean.TRUE.equals(cargo.getWoodenProduct()) && !Boolean.TRUE.equals(order.getCustomsInspection())) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "木制品必须选择商检");
|
||||
}
|
||||
@@ -441,7 +442,8 @@ public class OrderApplicationService {
|
||||
}
|
||||
validateWoodInspection(request);
|
||||
validateRequestCargoQuantities(request.cargoes(), true);
|
||||
for (var cargo : request.cargoes()) {
|
||||
for (int cargoIndex = 0; cargoIndex < request.cargoes().size(); cargoIndex++) {
|
||||
var cargo = request.cargoes().get(cargoIndex);
|
||||
if (!StringUtils.hasText(cargo.cargoName()) || !StringUtils.hasText(cargo.packageType())
|
||||
|| cargo.packageCount() < 1 || cargo.grossWeightKg() == null
|
||||
|| cargo.grossWeightKg().signum() <= 0 || cargo.volumeCbm() == null
|
||||
@@ -449,6 +451,7 @@ public class OrderApplicationService {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED,
|
||||
"提交订单前必须完善货物品名、包装类型、箱数、毛重和体积");
|
||||
}
|
||||
requireDropshipSku(request.fulfillmentType(), cargo.skuNo(), cargoIndex);
|
||||
}
|
||||
validateMixedCartons(request);
|
||||
if ("FBA".equals(request.destinationType())) {
|
||||
@@ -467,6 +470,13 @@ public class OrderApplicationService {
|
||||
}
|
||||
}
|
||||
|
||||
private static void requireDropshipSku(String fulfillmentType, String skuNo, int cargoIndex) {
|
||||
if ("ONE_CLICK_DROPSHIP".equals(fulfillmentType) && !StringUtils.hasText(skuNo)) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED,
|
||||
"第 " + (cargoIndex + 1) + " 条一件代发货物必须填写 SKU NO.");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRequestCargoQuantities(List<OrderRequests.Cargo> cargoes, boolean required) {
|
||||
for (int index = 0; index < cargoes.size(); index++) {
|
||||
OrderRequests.Cargo cargo = cargoes.get(index);
|
||||
|
||||
@@ -1399,7 +1399,7 @@ components:
|
||||
type: object
|
||||
required: [packageCount, woodenProduct]
|
||||
properties:
|
||||
skuNo: {type: string, maxLength: 64}
|
||||
skuNo: {type: string, maxLength: 64, description: 草稿可空;FBA 或 ONE_CLICK_DROPSHIP 订单提交及已提交编辑时每条货物必填}
|
||||
cargoName: {type: string, maxLength: 128}
|
||||
cargoNameEn: {type: string, maxLength: 128}
|
||||
productImageFileId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
|
||||
@@ -375,6 +375,72 @@ class OrderApplicationServiceStateTest {
|
||||
verify(repository).saveOrder(20002L, order);
|
||||
}
|
||||
|
||||
@Test
|
||||
void submittingDropshipDraftRequiresEachPersistedSkuBeforeAnyStateWriteForBothTaxModes() {
|
||||
for (String taxMode : List.of("BUY_DECLARATION", "TAX_REFUND")) {
|
||||
for (String sku : java.util.Arrays.asList(null, "", " ")) {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
OrderEntity order = persistedDraft("SELF_EXPRESS", "OVERSEAS");
|
||||
order.setFulfillmentType("ONE_CLICK_DROPSHIP"); order.setTaxMode(taxMode);
|
||||
OrderCargoEntity valid = completeCargo(); valid.setSkuNo("SKU-VALID");
|
||||
OrderCargoEntity missing = completeCargo(); missing.setSkuNo(sku);
|
||||
when(repository.findOwned(20002L, 1L)).thenReturn(order);
|
||||
when(repository.findCargo(20002L, 1L)).thenReturn(List.of(valid, missing));
|
||||
|
||||
assertThatThrownBy(() -> service(repository, mock(OrderEligibilityGuard.class), mock(OrderMasterDataGuard.class))
|
||||
.submit(20002L, 2L, "u", 1L, "missing-sku"))
|
||||
.isInstanceOf(BizException.class).hasMessage("第 2 条一件代发货物必须填写 SKU NO.")
|
||||
.extracting(exception -> ((BizException) exception).getResultCode()).isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||
assertThat(order.getStatus()).isEqualTo(OrderStatus.DRAFT);
|
||||
assertThat(order.getSubmittedAt()).isNull(); assertThat(order.getInboundCode()).isNull();
|
||||
verify(repository, never()).saveOrder(anyLong(), any());
|
||||
verify(repository, never()).insertOutbox(any());
|
||||
verify(repository, never()).insertHistory(any());
|
||||
verify(repository, never()).completeIdempotency(anyLong(), any(), anyLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void submittingDropshipDraftWithSkuKeepsUpcOptional() {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
OrderEntity order = persistedDraft("SELF_EXPRESS", "OVERSEAS");
|
||||
order.setFulfillmentType("ONE_CLICK_DROPSHIP");
|
||||
OrderCargoEntity cargo = completeCargo(); cargo.setSkuNo("SKU-VALID"); cargo.setUpcNo(null);
|
||||
when(repository.findOwned(20002L, 1L)).thenReturn(order);
|
||||
when(repository.findCargo(20002L, 1L)).thenReturn(List.of(cargo));
|
||||
when(repository.findParties(20002L, 1L)).thenReturn(List.of());
|
||||
when(repository.saveOrder(20002L, order)).thenReturn(1);
|
||||
service(repository, mock(OrderEligibilityGuard.class), mock(OrderMasterDataGuard.class))
|
||||
.transit(20002L, 2L, "u", 1L, OrderStatus.SUBMITTED, null);
|
||||
assertThat(order.getStatus()).isEqualTo(OrderStatus.SUBMITTED);
|
||||
verify(repository).saveOrder(20002L, order);
|
||||
}
|
||||
|
||||
@Test
|
||||
void submittedDropshipEditorCannotRemoveRequiredSku() {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
OrderEntity order = persistedOrder(OrderStatus.SUBMITTED);
|
||||
when(repository.findOwned(10001L, 1L)).thenReturn(order);
|
||||
OrderRequests.SaveRequest source = request();
|
||||
OrderRequests.SaveRequest missingSku = new OrderRequests.SaveRequest(
|
||||
source.customerId(), source.customerName(), source.transportMode(), source.tradeTerm(),
|
||||
source.destinationCountryCode(), source.destinationWarehouseId(), source.destinationWarehouseName(),
|
||||
source.expectedShipDate(), source.customerRemark(), source.sender(), source.receiver(), source.cargoes(),
|
||||
source.pickupMethod(), source.requestedPickupAt(), source.receivingWarehouseId(),
|
||||
source.receivingWarehouseName(), source.channelCode(), source.channelName(), source.carrierId(),
|
||||
source.carrierName(), source.taxMode(), source.pricingTaxMode(), source.containerMode(),
|
||||
source.customsInspection(), source.destinationType(), source.amazonWarehouseCode(), source.shipmentId(),
|
||||
source.referenceId(), source.inboundCode(), "ONE_CLICK_DROPSHIP");
|
||||
assertThatThrownBy(() -> service(repository, mock(OrderEligibilityGuard.class), mock(OrderMasterDataGuard.class))
|
||||
.updateSubmittedOrder(10001L, 2L, 1L, missingSku))
|
||||
.isInstanceOf(BizException.class).hasMessage("第 1 条一件代发货物必须填写 SKU NO.")
|
||||
.extracting(exception -> ((BizException) exception).getResultCode()).isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||
verify(repository, never()).saveOrder(anyLong(), any());
|
||||
verify(repository, never()).replaceDraftChildren(anyLong(), anyLong());
|
||||
verify(repository, never()).insertHistory(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void submittingDraftPreparesInboundForWarehouseMeasurementBeforeReview() {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
|
||||
Reference in New Issue
Block a user