2026-08-19 16:24:02 +08:00
|
|
|
package com.baiyue.warehouse.service;
|
|
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.IdType;
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.TableId;
|
|
|
|
|
import com.baiyue.common.core.exception.BizException;
|
|
|
|
|
import com.baiyue.warehouse.dto.LocationRequests;
|
|
|
|
|
import com.baiyue.warehouse.dto.PalletRequests;
|
|
|
|
|
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
|
|
|
|
|
import com.baiyue.warehouse.entity.WarehousePalletEntity;
|
|
|
|
|
import com.baiyue.warehouse.entity.WarehouseEntity;
|
|
|
|
|
import com.baiyue.warehouse.repository.WarehouseResourceRepository;
|
|
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
|
|
import org.mockito.ArgumentCaptor;
|
|
|
|
|
import org.mockito.Mock;
|
|
|
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
|
|
|
import static org.mockito.Mockito.doAnswer;
|
|
|
|
|
import static org.mockito.Mockito.never;
|
|
|
|
|
import static org.mockito.Mockito.verify;
|
|
|
|
|
import static org.mockito.Mockito.when;
|
|
|
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
|
|
|
class WarehouseResourceServiceTest {
|
|
|
|
|
private static final long TENANT = 10001L;
|
|
|
|
|
private static final long WAREHOUSE = 20001L;
|
|
|
|
|
|
|
|
|
|
@Mock
|
|
|
|
|
private WarehouseResourceRepository repository;
|
|
|
|
|
|
|
|
|
|
private WarehouseResourceService service;
|
|
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
|
void setUp() {
|
|
|
|
|
service = new WarehouseResourceService();
|
|
|
|
|
ReflectionTestUtils.setField(service, "repository", repository);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void resourceIdsUseMybatisSnowflakeAssignment() throws NoSuchFieldException {
|
|
|
|
|
assertThat(WarehouseLocationEntity.class.getDeclaredField("id").getAnnotation(TableId.class).type())
|
|
|
|
|
.isEqualTo(IdType.ASSIGN_ID);
|
|
|
|
|
assertThat(WarehousePalletEntity.class.getDeclaredField("id").getAnnotation(TableId.class).type())
|
|
|
|
|
.isEqualTo(IdType.ASSIGN_ID);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 10:31:04 +08:00
|
|
|
@Test
|
|
|
|
|
void resolvesOnlyPublicLocationInsideBoundWarehouse() {
|
|
|
|
|
WarehouseLocationEntity location = new WarehouseLocationEntity(); location.setId(30001L);
|
|
|
|
|
location.setWarehouseId(WAREHOUSE); location.setCustomerId(0L); location.setLocationCode("A-01");
|
|
|
|
|
location.setLocationType("STORAGE"); location.setMaxLoadKg(new BigDecimal("1000")); location.setStatus("IDLE");
|
|
|
|
|
when(repository.findLocationByCode(0L, WAREHOUSE, "A-01")).thenReturn(location);
|
|
|
|
|
|
|
|
|
|
var result = service.resolvePublicLocation(WAREHOUSE, " A-01 ");
|
|
|
|
|
|
|
|
|
|
assertThat(result.locationId()).isEqualTo(30001L);
|
|
|
|
|
assertThat(result.locationCode()).isEqualTo("A-01");
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 16:24:02 +08:00
|
|
|
@Test
|
|
|
|
|
void createsLocationWithSnowflakeAssignedByMapper() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
doAnswer(invocation -> {
|
|
|
|
|
((WarehouseLocationEntity) invocation.getArgument(0)).setId(2089000000000000001L);
|
|
|
|
|
return null;
|
|
|
|
|
}).when(repository).insertLocation(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
|
|
|
|
|
Long id = service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01"));
|
|
|
|
|
|
|
|
|
|
assertThat(id).isEqualTo(2089000000000000001L);
|
|
|
|
|
ArgumentCaptor<WarehouseLocationEntity> captor = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
|
|
|
|
|
verify(repository).insertLocation(captor.capture());
|
|
|
|
|
assertThat(captor.getValue().getCustomerId()).isEqualTo(TENANT);
|
|
|
|
|
assertThat(captor.getValue().getWarehouseId()).isEqualTo(WAREHOUSE);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-20 18:02:38 +08:00
|
|
|
@Test
|
|
|
|
|
void platformPalletUsesPublicScopeAgainstAnyExistingWarehouse() {
|
|
|
|
|
WarehouseEntity warehouse = warehouse();
|
|
|
|
|
when(repository.findWarehouseById(WAREHOUSE)).thenReturn(warehouse);
|
|
|
|
|
WarehouseLocationEntity location = new WarehouseLocationEntity();
|
|
|
|
|
location.setId(30001L);
|
|
|
|
|
when(repository.findLocation(0L, WAREHOUSE, 30001L)).thenReturn(location);
|
|
|
|
|
|
|
|
|
|
service.createPallet(0L, WAREHOUSE, new PalletRequests.SaveRequest(
|
|
|
|
|
"PLT-PUBLIC", "STANDARD", null, BigDecimal.ONE, "IDLE", 30001L, null));
|
|
|
|
|
|
|
|
|
|
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
|
|
|
|
|
verify(repository).insertPallet(captor.capture());
|
|
|
|
|
assertThat(captor.getValue().getCustomerId()).isZero();
|
|
|
|
|
assertThat(captor.getValue().getWarehouseId()).isEqualTo(WAREHOUSE);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 16:24:02 +08:00
|
|
|
@Test
|
|
|
|
|
void rejectsResourceAccessWhenWarehouseDoesNotBelongToTenant() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(false);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01")))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("仓库不存在");
|
|
|
|
|
verify(repository, never()).insertLocation(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void rejectsDuplicateLocationCodeWithinWarehouse() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
when(repository.locationCodeExists(TENANT, WAREHOUSE, "A-01", null)).thenReturn(true);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01")))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("库位编码已存在");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void palletDefaultsToTwoCubicMetresAndBindsOwnedLocation() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
WarehouseLocationEntity location = new WarehouseLocationEntity();
|
|
|
|
|
location.setId(30001L);
|
|
|
|
|
when(repository.findLocation(TENANT, WAREHOUSE, 30001L)).thenReturn(location);
|
|
|
|
|
doAnswer(invocation -> {
|
|
|
|
|
((WarehousePalletEntity) invocation.getArgument(0)).setId(2089000000000000002L);
|
|
|
|
|
return null;
|
|
|
|
|
}).when(repository).insertPallet(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
|
|
|
|
|
Long id = service.createPallet(TENANT, WAREHOUSE,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
|
|
|
|
|
new BigDecimal("12.50"), "IDLE", 30001L, null));
|
|
|
|
|
|
|
|
|
|
assertThat(id).isEqualTo(2089000000000000002L);
|
|
|
|
|
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
|
|
|
|
|
verify(repository).insertPallet(captor.capture());
|
|
|
|
|
assertThat(captor.getValue().getCapacityCbm()).isEqualByComparingTo("2.0000");
|
|
|
|
|
assertThat(captor.getValue().getLocationId()).isEqualTo(30001L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void rejectsLocationFromAnotherWarehouseWhenBindingPallet() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
when(repository.findLocation(TENANT, WAREHOUSE, 30002L)).thenReturn(null);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.createPallet(TENANT, WAREHOUSE,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
|
|
|
|
|
BigDecimal.ONE, "IDLE", 30002L, null)))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("库位不存在");
|
|
|
|
|
verify(repository, never()).insertPallet(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void preventsDeletingLocationThatStillHasPallets() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
when(repository.findLocation(TENANT, WAREHOUSE, 30001L)).thenReturn(new WarehouseLocationEntity());
|
|
|
|
|
when(repository.locationHasPallets(TENANT, WAREHOUSE, 30001L)).thenReturn(true);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.deleteLocation(TENANT, WAREHOUSE, 30001L))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("仍绑定托盘");
|
|
|
|
|
verify(repository, never()).deleteLocation(30001L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void preventsDeletingOccupiedPallet() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity pallet = new WarehousePalletEntity();
|
|
|
|
|
pallet.setStatus("OCCUPIED");
|
|
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40001L)).thenReturn(pallet);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.deletePallet(TENANT, WAREHOUSE, 40001L))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("占用中的托盘");
|
|
|
|
|
verify(repository, never()).deletePallet(40001L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void preventsDeletingScrappedPalletSoItsIdentityCannotBeReused() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity pallet = new WarehousePalletEntity();
|
|
|
|
|
pallet.setStatus("SCRAPPED");
|
|
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40003L)).thenReturn(pallet);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.deletePallet(TENANT, WAREHOUSE, 40003L))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("cannot be reused");
|
|
|
|
|
verify(repository, never()).deletePallet(40003L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void rejectsUnknownPalletStatus() {
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.createPallet(TENANT, WAREHOUSE,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
|
|
|
|
|
BigDecimal.ONE, "BROKEN", null, null)))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("IDLE、OCCUPIED、SCRAPPED");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static LocationRequests.SaveRequest locationRequest(String code) {
|
|
|
|
|
return new LocationRequests.SaveRequest("A", code, "STORAGE",
|
|
|
|
|
new BigDecimal("1000.000"), "ACTIVE", null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void generatesRestartSafePalletCodeAndUsesDefaultVirtualLocation() {
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
WarehouseLocationEntity location = new WarehouseLocationEntity();
|
|
|
|
|
location.setId(30003L);
|
|
|
|
|
location.setLocationType("VIRTUAL");
|
|
|
|
|
location.setStatus("ACTIVE");
|
|
|
|
|
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
|
|
|
|
|
.thenReturn(location);
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
when(repository.findLocation(TENANT, WAREHOUSE, 30003L)).thenReturn(location);
|
|
|
|
|
|
|
|
|
|
service.createPallet(TENANT, WAREHOUSE,
|
|
|
|
|
new PalletRequests.SaveRequest(null, "STANDARD", null, BigDecimal.ONE, "IDLE", null, null));
|
|
|
|
|
|
|
|
|
|
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
|
|
|
|
|
verify(repository).insertPallet(captor.capture());
|
|
|
|
|
assertThat(captor.getValue().getPalletCode()).matches("PLT-US-LAX-01-[0-9]{8}-[0-9]+");
|
|
|
|
|
assertThat(captor.getValue().getLocationId()).isEqualTo(30003L);
|
|
|
|
|
assertThat(captor.getValue().getId()).isNotNull();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void defaultVirtualLocationIsInsertedIdempotentlyForAnExistingWarehouse() {
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
WarehouseLocationEntity resolved = new WarehouseLocationEntity();
|
|
|
|
|
resolved.setId(30004L);
|
|
|
|
|
resolved.setLocationType("VIRTUAL");
|
|
|
|
|
resolved.setStatus("ACTIVE");
|
|
|
|
|
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
|
|
|
|
|
.thenReturn(null, resolved);
|
|
|
|
|
|
|
|
|
|
WarehouseLocationEntity result = service.ensureDefaultVirtualLocation(TENANT, WAREHOUSE);
|
|
|
|
|
|
|
|
|
|
assertThat(result.getId()).isEqualTo(30004L);
|
|
|
|
|
ArgumentCaptor<WarehouseLocationEntity> location = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
|
|
|
|
|
verify(repository).insertDefaultVirtualLocationIgnore(location.capture());
|
|
|
|
|
assertThat(location.getValue().getLocationCode()).isEqualTo("LOC-US-LAX-01-DEFAULT");
|
|
|
|
|
assertThat(location.getValue().getLocationType()).isEqualTo("VIRTUAL");
|
|
|
|
|
assertThat(location.getValue().getStatus()).isEqualTo("ACTIVE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void rejectsAConflictingDefaultLocationCode() {
|
|
|
|
|
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
|
|
|
|
|
WarehouseLocationEntity conflict = new WarehouseLocationEntity();
|
|
|
|
|
conflict.setLocationType("STORAGE");
|
|
|
|
|
conflict.setStatus("ACTIVE");
|
|
|
|
|
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
|
|
|
|
|
.thenReturn(conflict);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.ensureDefaultVirtualLocation(TENANT, WAREHOUSE))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("Default virtual location code");
|
|
|
|
|
verify(repository, never()).insertDefaultVirtualLocationIgnore(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void scrappedPalletCannotBeReactivated() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity existing = new WarehousePalletEntity();
|
|
|
|
|
existing.setId(40002L);
|
|
|
|
|
existing.setPalletCode("PLT-OLD");
|
|
|
|
|
existing.setStatus("SCRAPPED");
|
2026-08-24 15:14:18 +08:00
|
|
|
existing.setVersion(0);
|
2026-08-19 16:24:02 +08:00
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40002L)).thenReturn(existing);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.updatePallet(TENANT, WAREHOUSE, 40002L,
|
2026-08-24 15:14:18 +08:00
|
|
|
new PalletRequests.SaveRequest(null, "STANDARD", null, BigDecimal.ONE, "IDLE", null, null, null, 0)))
|
2026-08-19 16:24:02 +08:00
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("cannot be reused");
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-21 11:43:03 +08:00
|
|
|
@Test
|
|
|
|
|
void updatePalletChangesCodeAndFbaWarehouseCode() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity existing = new WarehousePalletEntity();
|
|
|
|
|
existing.setId(40003L);
|
|
|
|
|
existing.setPalletCode("PLT-OLD");
|
|
|
|
|
existing.setStatus("IDLE");
|
2026-08-24 15:14:18 +08:00
|
|
|
existing.setVersion(2);
|
2026-08-21 11:43:03 +08:00
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40003L)).thenReturn(existing);
|
2026-08-24 15:14:18 +08:00
|
|
|
when(repository.updatePallet(org.mockito.ArgumentMatchers.any())).thenReturn(1);
|
2026-08-21 11:43:03 +08:00
|
|
|
|
|
|
|
|
service.updatePallet(TENANT, WAREHOUSE, 40003L,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-NEW", "WOOD", new BigDecimal("2.5000"),
|
2026-08-24 15:14:18 +08:00
|
|
|
new BigDecimal("25.00"), "IDLE", null, "updated", "ONT8", 2));
|
2026-08-21 11:43:03 +08:00
|
|
|
|
|
|
|
|
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
|
|
|
|
|
verify(repository).palletCodeExists(TENANT, WAREHOUSE, "PLT-NEW", 40003L);
|
|
|
|
|
verify(repository).updatePallet(captor.capture());
|
|
|
|
|
assertThat(captor.getValue().getPalletCode()).isEqualTo("PLT-NEW");
|
|
|
|
|
assertThat(captor.getValue().getFbaWarehouseCode()).isEqualTo("ONT8");
|
2026-08-24 15:14:18 +08:00
|
|
|
assertThat(captor.getValue().getVersion()).isEqualTo(2);
|
2026-08-21 11:43:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void updatePalletRejectsDuplicateCodeInSameWarehouse() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity existing = new WarehousePalletEntity();
|
|
|
|
|
existing.setId(40004L);
|
|
|
|
|
existing.setPalletCode("PLT-OLD");
|
|
|
|
|
existing.setStatus("IDLE");
|
2026-08-24 15:14:18 +08:00
|
|
|
existing.setVersion(0);
|
2026-08-21 11:43:03 +08:00
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40004L)).thenReturn(existing);
|
|
|
|
|
when(repository.palletCodeExists(TENANT, WAREHOUSE, "PLT-DUP", 40004L)).thenReturn(true);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.updatePallet(TENANT, WAREHOUSE, 40004L,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-DUP", "WOOD", BigDecimal.ONE,
|
2026-08-24 15:14:18 +08:00
|
|
|
BigDecimal.ONE, "IDLE", null, null, "ONT8", 0)))
|
2026-08-21 11:43:03 +08:00
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("托盘编码已存在");
|
|
|
|
|
verify(repository, never()).updatePallet(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 15:14:18 +08:00
|
|
|
@Test
|
|
|
|
|
void updatePalletRejectsStaleVersionBeforeMutation() {
|
|
|
|
|
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
|
|
|
|
|
WarehousePalletEntity existing = new WarehousePalletEntity();
|
|
|
|
|
existing.setId(40005L);
|
|
|
|
|
existing.setPalletCode("PLT-OLD");
|
|
|
|
|
existing.setStatus("IDLE");
|
|
|
|
|
existing.setVersion(3);
|
|
|
|
|
when(repository.findPallet(TENANT, WAREHOUSE, 40005L)).thenReturn(existing);
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> service.updatePallet(TENANT, WAREHOUSE, 40005L,
|
|
|
|
|
new PalletRequests.SaveRequest("PLT-STALE", "WOOD", BigDecimal.ONE,
|
|
|
|
|
BigDecimal.ONE, "IDLE", null, null, "ONT8", 2)))
|
|
|
|
|
.isInstanceOf(BizException.class)
|
|
|
|
|
.hasMessageContaining("版本已变化");
|
|
|
|
|
verify(repository, never()).palletCodeExists(org.mockito.ArgumentMatchers.anyLong(),
|
|
|
|
|
org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString(),
|
|
|
|
|
org.mockito.ArgumentMatchers.any());
|
|
|
|
|
verify(repository, never()).updatePallet(org.mockito.ArgumentMatchers.any());
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 16:24:02 +08:00
|
|
|
private static WarehouseEntity warehouse() {
|
|
|
|
|
WarehouseEntity warehouse = new WarehouseEntity();
|
|
|
|
|
warehouse.setId(WAREHOUSE);
|
|
|
|
|
warehouse.setCustomerId(TENANT);
|
|
|
|
|
warehouse.setWarehouseCode("US-LAX-01");
|
|
|
|
|
return warehouse;
|
|
|
|
|
}
|
|
|
|
|
}
|