修改提交
This commit is contained in:
+3
-3
@@ -25,7 +25,7 @@ public interface InventoryQueryMapper {
|
||||
"<if test='q.batchNo != null and q.batchNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p WHERE p.balance_id=ll_inventory_balance.id AND p.customer_id=ll_inventory_balance.customer_id AND p.batch_no LIKE CONCAT('%',#{q.batchNo},'%'))</if>" +
|
||||
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p WHERE p.balance_id=ll_inventory_balance.id AND p.customer_id=ll_inventory_balance.customer_id AND p.warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%'))</if>" +
|
||||
"<choose>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND available_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND (available_packages+allocated_packages+picked_packages) > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"HOLD\"'> AND hold_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"ALLOCATED\"'> AND allocated_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"PICKED\"'> AND picked_packages > 0</when>" +
|
||||
@@ -48,7 +48,7 @@ public interface InventoryQueryMapper {
|
||||
"<if test='q.batchNo != null and q.batchNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p2 WHERE p2.balance_id=b.id AND p2.customer_id=b.customer_id AND p2.batch_no LIKE CONCAT('%',#{q.batchNo},'%'))</if>" +
|
||||
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p2 WHERE p2.balance_id=b.id AND p2.customer_id=b.customer_id AND p2.warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%'))</if>" +
|
||||
"<choose>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND b.available_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND (b.available_packages+b.allocated_packages+b.picked_packages) > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"HOLD\"'> AND b.hold_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"ALLOCATED\"'> AND b.allocated_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"PICKED\"'> AND b.picked_packages > 0</when>" +
|
||||
@@ -92,7 +92,7 @@ public interface InventoryQueryMapper {
|
||||
"<if test='q.skuCode != null and q.skuCode != \"\"'> AND sku_code LIKE CONCAT('%',#{q.skuCode},'%')</if>" +
|
||||
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%')</if>" +
|
||||
"<choose>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND available_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND (available_packages+allocated_packages+picked_packages) > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"HOLD\"'> AND hold_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"ALLOCATED\"'> AND allocated_packages > 0</when>" +
|
||||
"<when test='q.inventoryStatus == \"PICKED\"'> AND picked_packages > 0</when>" +
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ public class InventoryQueryService {
|
||||
long inTransitPackages = number(value.getInTransitPackages());
|
||||
long receivingPackages = number(value.getReceivingPackages());
|
||||
long procurementPackages = number(value.getProcurementPackages());
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages + receivingPackages;
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages;
|
||||
return new InventoryDashboardResponse(number(value.getDimensionCount()), mapper.countPallets(customerId, query),
|
||||
totalPackages, Math.max(0, totalPackages - holdPackages), holdPackages, allocatedPackages,
|
||||
pickedPackages, inTransitPackages,
|
||||
@@ -117,7 +117,7 @@ public class InventoryQueryService {
|
||||
long inTransitPackages = number(value.getInTransitPackages());
|
||||
long receivingPackages = number(value.getReceivingPackages());
|
||||
long procurementPackages = number(value.getProcurementPackages());
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages + receivingPackages;
|
||||
long totalPackages = availablePackages + holdPackages + allocatedPackages + pickedPackages;
|
||||
return new InventoryBalanceView(value.getId(), value.getCustomerId(), value.getCustomerName(), value.getOverseasWarehouseId(),
|
||||
value.getOverseasWarehouseCode(), value.getOverseasWarehouseName(), value.getDestinationWarehouseId(),
|
||||
value.getDestinationWarehouseCode(), value.getDestinationWarehouseName(), value.getInboundOrderId(),
|
||||
@@ -133,7 +133,7 @@ public class InventoryQueryService {
|
||||
long allocated = number(value.getAllocatedPackages());
|
||||
long picked = number(value.getPickedPackages());
|
||||
long receiving = number(value.getReceivingPackages());
|
||||
long total = available + hold + allocated + picked + receiving;
|
||||
long total = available + hold + allocated + picked;
|
||||
return new InventoryPositionView(value.getId(), value.getBalanceId(), value.getCustomerId(),
|
||||
value.getOverseasWarehouseId(), value.getDestinationWarehouseCode(), balance.getInboundOrderId(),
|
||||
balance.getInboundOrderNo(), balance.getInboundCode(),
|
||||
|
||||
+34
@@ -77,6 +77,34 @@ class InventoryCommandServiceTest {
|
||||
verify(operations).updateById(any(InventoryOperation.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registersInTransitWithoutCreatingAPalletPosition() {
|
||||
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
|
||||
|
||||
InventoryCommandResponse result = service.registerInTransit("100", "SYSTEM", inTransit("transit-1", 12));
|
||||
|
||||
assertEquals(0, result.availablePackages());
|
||||
assertEquals(null, result.positionId());
|
||||
ArgumentCaptor<InventoryBalance> balance = ArgumentCaptor.forClass(InventoryBalance.class);
|
||||
verify(balances).updateById(balance.capture());
|
||||
assertEquals(12L, balance.getValue().getInTransitPackages());
|
||||
verify(positions, never()).insert(any(InventoryPosition.class));
|
||||
verify(ledgers).insert(any(InventoryLedger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void stockInTransfersPackagesFromInTransitToAvailable() {
|
||||
InventoryBalance balance = balance(200L, 0);
|
||||
balance.setInTransitPackages(12L);
|
||||
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(balance);
|
||||
|
||||
InventoryCommandResponse result = service.stockIn("100", "7", stockIn("IN-1", "stock-in-transit", 12, 24));
|
||||
|
||||
assertEquals(12, result.availablePackages());
|
||||
assertEquals(0L, balance.getInTransitPackages());
|
||||
assertEquals(12L, balance.getAvailablePackages());
|
||||
}
|
||||
|
||||
@Test
|
||||
void stockInAllowsPalletPositionWithZeroOptionalPackages() {
|
||||
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
|
||||
@@ -161,6 +189,12 @@ class InventoryCommandServiceTest {
|
||||
packages, "ORD-1", "EVT-1", key, null);
|
||||
}
|
||||
|
||||
private InventoryCommandRequests.InTransit inTransit(String key, long packages) {
|
||||
return new InventoryCommandRequests.InTransit("Customer 100", "WH-1", "WH1", "Warehouse 1",
|
||||
"DEST-1", "ONT8", "ONT8", 101L, "IN-1", "IN-CODE-1", "SKU-1", "Product 1", null,
|
||||
packages, "ORD-1", "EVT-1", key, null);
|
||||
}
|
||||
|
||||
private InventoryBalance balance(Long id, long available) {
|
||||
InventoryBalance value = new InventoryBalance();
|
||||
value.setId(id); value.setCustomerId("100"); value.setOverseasWarehouseId("WH-1");
|
||||
|
||||
+40
@@ -50,6 +50,25 @@ class InventoryQueryServiceTest {
|
||||
assertEquals(12, result.totalPackages());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dashboardExcludesTransitAndProcurementFromOnHandAndDerivesAvailableFromHold() {
|
||||
InventoryQueryRequests.BalanceQuery query = balanceQuery(null);
|
||||
InventoryAggregateProjection aggregate = new InventoryAggregateProjection();
|
||||
aggregate.setAvailablePackages(8L);
|
||||
aggregate.setHoldPackages(2L);
|
||||
aggregate.setAllocatedPackages(3L);
|
||||
aggregate.setInTransitPackages(7L);
|
||||
aggregate.setProcurementPackages(5L);
|
||||
when(mapper.aggregate("100", query)).thenReturn(aggregate);
|
||||
|
||||
InventoryDashboardResponse result = service.dashboard("100", query);
|
||||
|
||||
assertEquals(13L, result.totalPackages());
|
||||
assertEquals(11L, result.availablePackages());
|
||||
assertEquals(7L, result.inTransitPackages());
|
||||
assertEquals(5L, result.procurementPackages());
|
||||
}
|
||||
|
||||
@Test
|
||||
void balancePageIsBoundToRequestedTenant() {
|
||||
InventoryQueryRequests.BalanceQuery query = balanceQuery(null);
|
||||
@@ -65,6 +84,27 @@ class InventoryQueryServiceTest {
|
||||
verify(mapper).balances("100", query, 0, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceViewUsesTheSameOnHandAndAvailableFormulaAsDashboard() {
|
||||
InventoryQueryRequests.BalanceQuery query = balanceQuery(null);
|
||||
InventoryBalance balance = new InventoryBalance();
|
||||
balance.setId(1L);
|
||||
balance.setCustomerId("100");
|
||||
balance.setAvailablePackages(8L);
|
||||
balance.setHoldPackages(2L);
|
||||
balance.setAllocatedPackages(3L);
|
||||
balance.setInTransitPackages(7L);
|
||||
balance.setProcurementPackages(5L);
|
||||
when(mapper.countBalances("100", query)).thenReturn(1L);
|
||||
when(mapper.balances("100", query, 0, 20)).thenReturn(List.of(balance));
|
||||
|
||||
var result = service.balances("100", query).records().get(0);
|
||||
|
||||
assertEquals(13L, result.totalPackages());
|
||||
assertEquals(11L, result.availablePackages());
|
||||
assertEquals(7L, result.inTransitPackages());
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformBalancePagePassesNoCustomerFilter() {
|
||||
InventoryQueryRequests.BalanceQuery query = balanceQuery(null);
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
import com.baiyue.common.event.*; import com.baiyue.lastleg.entity.*; import com.baiyue.lastleg.mapper.*; import com.baiyue.lastleg.dropship.dto.DropshipRequests; import com.baiyue.lastleg.dropship.service.DropshipOrderService; import com.baiyue.lastleg.inboundplan.mapper.InboundPlanCartonMapper; import com.baiyue.lastleg.inboundplan.service.InboundPlanService; import com.fasterxml.jackson.databind.*; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.*; import org.springframework.test.util.ReflectionTestUtils; import java.math.BigDecimal; import java.time.Instant; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*;
|
||||
import com.baiyue.common.event.*; import com.baiyue.lastleg.entity.*; import com.baiyue.lastleg.mapper.*; import com.baiyue.lastleg.dropship.dto.DropshipRequests; import com.baiyue.lastleg.dropship.service.DropshipOrderService; import com.baiyue.lastleg.inboundplan.dto.InboundPlanResponse; import com.baiyue.lastleg.inboundplan.mapper.InboundPlanCartonMapper; import com.baiyue.lastleg.inboundplan.service.InboundPlanService; import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests; import com.baiyue.lastleg.inventory.command.service.InventoryCommandService; import com.fasterxml.jackson.databind.*; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.*; import org.springframework.test.util.ReflectionTestUtils; import java.math.BigDecimal; import java.time.Instant; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*;
|
||||
@ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class)
|
||||
class LoadingCompletedConsumerTest {
|
||||
@Mock LastlegEventInboxMapper inbox; @Mock LastlegOrderMapper orders; @Mock LastlegOrderDetailMapper details; @Mock LastlegOrderStatusHistoryMapper histories; @Mock InboundPlanCartonMapper cartons; @Mock DropshipOrderService dropshipOrders; @Mock InboundPlanService inboundPlans; ObjectMapper json=new ObjectMapper().findAndRegisterModules(); LoadingCompletedConsumer service;
|
||||
@BeforeEach void setup(){ service=new LoadingCompletedConsumer(); ReflectionTestUtils.setField(service,"inboxMapper",inbox); ReflectionTestUtils.setField(service,"orderMapper",orders); ReflectionTestUtils.setField(service,"detailMapper",details); ReflectionTestUtils.setField(service,"historyMapper",histories); ReflectionTestUtils.setField(service,"cartonMapper",cartons); ReflectionTestUtils.setField(service,"dropshipOrders",dropshipOrders); ReflectionTestUtils.setField(service,"inboundPlans",inboundPlans); ReflectionTestUtils.setField(service,"objectMapper",json); ReflectionTestUtils.setField(service,"orderNoGenerator",new LastlegOrderNoGenerator()); lenient().doAnswer(i->{ LastlegOrder o=i.getArgument(0); o.setId(Math.abs(o.getSourceInboundId().hashCode())+1L); return 1; }).when(orders).insert(any(LastlegOrder.class)); }
|
||||
@Test void createsOneOrderAndOneInboundPlanPerInbound(){ EventEnvelope event=event(); assertEquals(2,service.consume(event)); verify(orders,times(2)).insert(any(LastlegOrder.class)); verify(inboundPlans,times(2)).convert(eq("100"),anyLong()); verify(details,times(2)).insert(any(LastlegOrderDetail.class)); verify(histories,times(2)).insert(any(LastlegOrderStatusHistory.class)); ArgumentCaptor<LastlegOrder> c=ArgumentCaptor.forClass(LastlegOrder.class); verify(orders,times(2)).insert(c.capture()); assertEquals(Set.of("in-1","in-2"),c.getAllValues().stream().map(LastlegOrder::getSourceInboundId).collect(java.util.stream.Collectors.toSet())); assertTrue(c.getAllValues().stream().allMatch(o->"PENDING_ASSIGNMENT".equals(o.getCurrentStatus()))); assertTrue(c.getAllValues().stream().allMatch(o->o.getId()!=null)); assertTrue(c.getAllValues().stream().allMatch(o->"MSCU1".equals(o.getContainerNo()))); assertEquals(1,c.getAllValues().stream().map(LastlegOrder::getOrderNo).distinct().count()); assertEquals(new LastlegOrderNoGenerator().forContainerNo("MSCU1"),c.getAllValues().get(0).getOrderNo()); }
|
||||
@Mock LastlegEventInboxMapper inbox; @Mock LastlegOrderMapper orders; @Mock LastlegOrderDetailMapper details; @Mock LastlegOrderStatusHistoryMapper histories; @Mock InboundPlanCartonMapper cartons; @Mock DropshipOrderService dropshipOrders; @Mock InboundPlanService inboundPlans; @Mock InventoryCommandService inventory; ObjectMapper json=new ObjectMapper().findAndRegisterModules(); LoadingCompletedConsumer service;
|
||||
@BeforeEach void setup(){ service=new LoadingCompletedConsumer(); ReflectionTestUtils.setField(service,"inboxMapper",inbox); ReflectionTestUtils.setField(service,"orderMapper",orders); ReflectionTestUtils.setField(service,"detailMapper",details); ReflectionTestUtils.setField(service,"historyMapper",histories); ReflectionTestUtils.setField(service,"cartonMapper",cartons); ReflectionTestUtils.setField(service,"dropshipOrders",dropshipOrders); ReflectionTestUtils.setField(service,"inboundPlans",inboundPlans); ReflectionTestUtils.setField(service,"inventory",inventory); ReflectionTestUtils.setField(service,"objectMapper",json); ReflectionTestUtils.setField(service,"orderNoGenerator",new LastlegOrderNoGenerator()); lenient().doAnswer(i->{ LastlegOrder o=i.getArgument(0); o.setId(Math.abs(o.getSourceInboundId().hashCode())+1L); return 1; }).when(orders).insert(any(LastlegOrder.class)); lenient().when(inboundPlans.convert(anyString(),anyLong())).thenReturn(new InboundPlanResponse(1L,"LIP-1","100",1L,"MSCU1","PENDING_ASSIGNMENT",1,1,BigDecimal.ONE,List.of(),List.of(),List.of())); }
|
||||
@Test void createsOneOrderAndOneInboundPlanPerInbound(){ EventEnvelope event=event(); assertEquals(2,service.consume(event)); verify(orders,times(2)).insert(any(LastlegOrder.class)); verify(inboundPlans,times(2)).convert(eq("100"),anyLong()); verify(inventory,times(2)).registerInTransit(eq("100"),eq("SYSTEM"),any(InventoryCommandRequests.InTransit.class)); verify(details,times(2)).insert(any(LastlegOrderDetail.class)); verify(histories,times(2)).insert(any(LastlegOrderStatusHistory.class)); ArgumentCaptor<LastlegOrder> c=ArgumentCaptor.forClass(LastlegOrder.class); verify(orders,times(2)).insert(c.capture()); assertEquals(Set.of("in-1","in-2"),c.getAllValues().stream().map(LastlegOrder::getSourceInboundId).collect(java.util.stream.Collectors.toSet())); assertTrue(c.getAllValues().stream().allMatch(o->"PENDING_ASSIGNMENT".equals(o.getCurrentStatus()))); assertTrue(c.getAllValues().stream().allMatch(o->o.getId()!=null)); assertTrue(c.getAllValues().stream().allMatch(o->"MSCU1".equals(o.getContainerNo()))); assertEquals(1,c.getAllValues().stream().map(LastlegOrder::getOrderNo).distinct().count()); assertEquals(new LastlegOrderNoGenerator().forContainerNo("MSCU1"),c.getAllValues().get(0).getOrderNo()); }
|
||||
@Test void repeatedEventReturnsExistingEffect(){ EventEnvelope event=event(); LastlegEventInbox row=new LastlegEventInbox(); row.setPayloadHash(event.payloadHash()); when(inbox.selectById(event.eventId())).thenReturn(row); when(orders.countContainer("100","MSCU1")).thenReturn(2L); assertEquals(2,service.consume(event)); verify(orders).countContainer("100","MSCU1"); verify(orders,never()).insert(any(LastlegOrder.class)); verify(details,never()).insert(any(LastlegOrderDetail.class)); }
|
||||
@Test void sameEventWithDifferentHashIsRejected(){ EventEnvelope event=event(); LastlegEventInbox row=new LastlegEventInbox(); row.setPayloadHash("sha256:"+"0".repeat(64)); when(inbox.selectById(event.eventId())).thenReturn(row); assertThrows(IllegalArgumentException.class,()->service.consume(event)); }
|
||||
@Test void rejectsEventFromWrongRegion(){ EventEnvelope source=event(); EventEnvelope invalid=new EventEnvelope(source.eventId(),source.eventType(),source.eventVersion(),source.occurredAt(),RegionCode.OVERSEAS,RegionCode.CN,source.tenantId(),source.aggregateType(),source.aggregateId(),source.aggregateVersion(),source.handoverVersion(),source.traceId(),source.correlationId(),source.causationId(),source.payload(),source.payloadHash()); assertThrows(IllegalArgumentException.class,()->service.consume(invalid)); }
|
||||
|
||||
Reference in New Issue
Block a user