提交
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
package com.baiyue.lastleg.delivery.service;
|
||||
|
||||
import com.baiyue.lastleg.delivery.dto.DeliveryRequests;
|
||||
import com.baiyue.lastleg.delivery.entity.*;
|
||||
import com.baiyue.lastleg.delivery.mapper.*;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import com.baiyue.lastleg.inventory.entity.*;
|
||||
import com.baiyue.lastleg.inventory.mapper.*;
|
||||
import com.baiyue.lastleg.relabel.entity.*;
|
||||
import com.baiyue.lastleg.relabel.mapper.*;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.*;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
|
||||
class RelabelOutboundPickingTest {
|
||||
private DeliveryTransferService service;
|
||||
private InventoryCommandService commands;
|
||||
private InventoryPosition position;
|
||||
private InventoryBalance balance;
|
||||
private OutboundPalletItem item;
|
||||
private OutboundPdaPickMapper picks;
|
||||
private OutboundPdaTaskMapper tasks;
|
||||
private OutboundOrderMapper orders;
|
||||
private OutboundPalletItemMapper pallets;
|
||||
private InventoryPositionMapper positions;
|
||||
private InventoryBalanceMapper balances;
|
||||
private RelabelApplicationMapper applications;
|
||||
private RelabelItemMapper items;
|
||||
|
||||
/** 构造返工后新目标位:两箱冻结,账面混合SKU,与空出库SKU不作通配匹配。 */
|
||||
@BeforeEach void prepare() {
|
||||
service = new DeliveryTransferService(); commands = mock(InventoryCommandService.class);
|
||||
picks=mock(OutboundPdaPickMapper.class); tasks=mock(OutboundPdaTaskMapper.class);
|
||||
orders=mock(OutboundOrderMapper.class); pallets=mock(OutboundPalletItemMapper.class);
|
||||
positions=mock(InventoryPositionMapper.class); balances=mock(InventoryBalanceMapper.class);
|
||||
applications=mock(RelabelApplicationMapper.class); items=mock(RelabelItemMapper.class);
|
||||
Map.of("inventoryCommands",commands,"pdaPicks",picks,"pdaTasks",tasks,"outboundOrders",orders,
|
||||
"outboundPallets",pallets,"inventoryPositions",positions,"inventoryBalances",balances,
|
||||
"relabelApplications",applications,"relabelItems",items,"objectMapper",new ObjectMapper())
|
||||
.forEach((name,value)->ReflectionTestUtils.setField(service,name,value));
|
||||
var outbound=new OutboundOrder(); outbound.setId(1L); outbound.setCustomerId("C");
|
||||
outbound.setOverseasWarehouseId("W"); outbound.setOutboundNo("OUT"); outbound.setOutboundType("RELABEL_OUTBOUND");
|
||||
var task=new OutboundPdaTask(); task.setId(2L); task.setCustomerId("C"); task.setOutboundOrderId(1L);
|
||||
task.setTaskNo("TASK"); task.setStatus("PENDING");
|
||||
item=new OutboundPalletItem(); item.setId(3L); item.setCustomerId("C"); item.setOutboundOrderId(1L);
|
||||
item.setWarehousePalletId(4L); item.setPalletNo("NEW-TARGET"); item.setInboundCode("IN");
|
||||
item.setPackageCount(2); item.setPickedPackageCount(0); item.setCartonMarks("BOX-A,BOX-B");
|
||||
item.setOrderRefs("""
|
||||
{"sourceType":"RELABEL_ORDER","relabelNo":"RL","sourceInventoryPositionId":9,
|
||||
"items":[{"itemId":11,"cartonMark":"BOX-A","packages":1},{"itemId":12,"cartonMark":"BOX-B","packages":1}]}
|
||||
""");
|
||||
position=new InventoryPosition(); position.setId(9L); position.setCustomerId("C"); position.setBalanceId(8L);
|
||||
position.setOverseasWarehouseId("W"); position.setPalletId(4L); position.setPalletNo("NEW-TARGET");
|
||||
position.setAvailablePackages(0L); position.setHoldPackages(2L);
|
||||
balance=new InventoryBalance(); balance.setId(8L); balance.setCustomerId("C");
|
||||
balance.setOverseasWarehouseId("W"); balance.setInboundCode("IN"); balance.setSkuCode("MIXED");
|
||||
when(tasks.lockByNoAndWarehouse("TASK","W")).thenReturn(task);
|
||||
when(tasks.findByNoAndWarehouse("TASK","W")).thenReturn(task);
|
||||
when(orders.selectById(1L)).thenReturn(outbound); when(orders.lockByNo("C","OUT")).thenReturn(outbound);
|
||||
when(pallets.findByOutbound("C",1L)).thenReturn(List.of(item)); when(pallets.selectById(3L)).thenReturn(item);
|
||||
when(positions.listPallet("C",4L)).thenReturn(List.of(position)); when(positions.selectById(9L)).thenReturn(position);
|
||||
when(balances.selectById(8L)).thenReturn(balance);
|
||||
var app=new RelabelApplication(); app.setId(10L); app.setRelabelNo("RL"); app.setStatus("READY_FOR_OUTBOUND");
|
||||
when(applications.lockByOutbound("C","OUT")).thenReturn(app);
|
||||
var first=new RelabelItem(); first.setId(11L); first.setCartonMark("BOX-A"); first.setNotifiedPackageCount(1);
|
||||
var second=new RelabelItem(); second.setId(12L); second.setCartonMark("BOX-B"); second.setNotifiedPackageCount(1);
|
||||
when(items.selectList(any())).thenReturn(List.of(first,second));
|
||||
}
|
||||
|
||||
/** 指定箱从冻结到出库数量守恒;整托重扫不重复释放或出库。 */
|
||||
@Test void picksExactFrozenBoxesAndReplayDoesNotShipAgain() {
|
||||
assertEquals(List.of("BOX-A","BOX-B"),service.pdaTaskDetail("W","TASK").items().get(0).positions()
|
||||
.stream().map(value->value.cartonMark()).toList());
|
||||
service.pdaPickPallet("W","U","TASK","NEW-TARGET",new DeliveryRequests.PdaPalletPick("KEY"));
|
||||
service.pdaPickPallet("W","U","TASK","NEW-TARGET",new DeliveryRequests.PdaPalletPick("KEY"));
|
||||
var release=org.mockito.ArgumentCaptor.forClass(InventoryCommandRequests.Move.class);
|
||||
verify(commands,times(2)).release(eq("C"),eq("U"),release.capture());
|
||||
assertEquals(2,release.getAllValues().stream().mapToLong(InventoryCommandRequests.Move::packages).sum());
|
||||
assertTrue(release.getAllValues().stream().allMatch(move->move.positionId()==9L&&"RELABEL:RL".equals(move.sourceEventId())));
|
||||
var shipped=org.mockito.ArgumentCaptor.forClass(InventoryCommandRequests.Move.class);
|
||||
verify(commands).ship(eq("C"),eq("U"),shipped.capture());
|
||||
assertEquals(2,shipped.getValue().packages()); assertEquals(9L,shipped.getValue().positionId());
|
||||
verify(commands).allocate(eq("C"),eq("U"),any()); verify(commands).pick(eq("C"),eq("U"),any());
|
||||
verify(picks).insert(any(OutboundPdaPick.class)); assertEquals(2,item.getPickedPackageCount());
|
||||
}
|
||||
|
||||
/** 其他位置、客户、仓、入仓或不足冻结均拒绝,不能用空SKU放宽范围。 */
|
||||
@ParameterizedTest @ValueSource(strings={"POSITION","CUSTOMER","WAREHOUSE","INBOUND","HOLD","SNAPSHOT"})
|
||||
void rejectsUnrelatedOrIncompleteSourcesWithoutInventoryEffects(String invalid) {
|
||||
switch(invalid) {
|
||||
case "POSITION" -> position.setId(99L);
|
||||
case "CUSTOMER" -> balance.setCustomerId("OTHER");
|
||||
case "WAREHOUSE" -> position.setOverseasWarehouseId("OTHER");
|
||||
case "INBOUND" -> balance.setInboundCode("OTHER");
|
||||
case "HOLD" -> position.setHoldPackages(1L);
|
||||
case "SNAPSHOT" -> item.setOrderRefs("{\"sourceType\":\"RELABEL_ORDER\"}");
|
||||
}
|
||||
assertTrue(service.pdaTaskDetail("W","TASK").items().get(0).positions().isEmpty());
|
||||
assertThrows(ResponseStatusException.class,()->service.pdaPickPallet("W","U","TASK","NEW-TARGET",new DeliveryRequests.PdaPalletPick("KEY")));
|
||||
verifyNoInteractions(commands,picks);
|
||||
}
|
||||
|
||||
/** 同箱唛也不能代替来源任务主键,损坏ID必须在释放冻结前拒绝。 */
|
||||
@Test void rejectsWrongSnapshotItemIdBeforeReleasingAnyStock() {
|
||||
item.setOrderRefs(item.getOrderRefs().replace("\"itemId\":11", "\"itemId\":999"));
|
||||
assertEquals(2,service.pdaTaskDetail("W","TASK").items().get(0).positions().size());
|
||||
assertThrows(ResponseStatusException.class,()->service.pdaPickPallet("W","U","TASK","NEW-TARGET",new DeliveryRequests.PdaPalletPick("KEY")));
|
||||
verifyNoInteractions(commands); verify(picks,never()).insert(any(OutboundPdaPick.class));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user