package cn.iocoder.yudao.module.mes.service.pro.scan; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.scan.MesProScanEventMapper; import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; import org.junit.jupiter.api.*; import org.springframework.test.util.ReflectionTestUtils; import java.math.BigDecimal; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class MobileScanValidityTest { private final MesProScanEventServiceImpl service=new MesProScanEventServiceImpl(); private final MesProScanEventMapper events=mock(MesProScanEventMapper.class); private final MesProBagCodeMapper bags=mock(MesProBagCodeMapper.class); private final MesProPalletMapper pallets=mock(MesProPalletMapper.class); private final MesProBatchMapper batches=mock(MesProBatchMapper.class); private final MesWmTransactionService transactions=mock(MesWmTransactionService.class); private MesProScanEventCreateReqVO req; @BeforeEach void setup(){ ReflectionTestUtils.setField(service,"eventMapper",events); ReflectionTestUtils.setField(service,"bagCodeMapper",bags); ReflectionTestUtils.setField(service,"palletMapper",pallets); ReflectionTestUtils.setField(service,"batchMapper",batches); ReflectionTestUtils.setField(service,"transactionService",transactions); req=new MesProScanEventCreateReqVO();req.setEventId("new");req.setBagCode("123"); req.setQuantity(BigDecimal.ONE);req.setLocationId(2L);req.setAreaId(3L); } @Test void canceledBagDoesNotCreateStock(){ when(bags.selectByScanCodeForUpdate("123")).thenReturn(new MesProBagCodeDO().setStatus(30)); assertFailed("MES_SCAN_BAG_CANCELED"); } @Test void canceledPalletDoesNotCreateStock(){ req.setScanType("PALLET"); when(pallets.selectByScanCodeForUpdate("123")).thenReturn(new MesProPalletDO().setStatus(20)); assertFailed("MES_SCAN_PALLET_CANCELED"); } @Test void canceledBatchDoesNotCreateStock(){ req.setScanType("BATCH"); when(batches.selectByScanCodeForUpdate("123")).thenReturn(new MesProBatchDO().setId(2L).setStatus(40).setBagQuantity(1)); assertFailed("MES_SCAN_BATCH_CANCELED"); } @Test void bagInCanceledBatchDoesNotCreateStock(){ when(bags.selectByScanCodeForUpdate("123")).thenReturn(new MesProBagCodeDO().setId(1L).setBatchId(2L).setStatus(20)); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(40)); assertFailed("MES_SCAN_BATCH_CANCELED"); } @Test void invalidTypeCannotFallThroughToBagInbound(){ req.setScanType("UNKNOWN"); assertThrows(ServiceException.class,()->service.createInbound(req)); verifyNoInteractions(events,bags,pallets,batches,transactions); } @Test void businessScanWritesCanonicalTraceCodeAndSameBagId(){ req.setBagCode("8888888888888888"); when(bags.selectByScanCodeForUpdate(req.getBagCode())).thenReturn(new MesProBagCodeDO() .setId(1L).setCode("9999999999999999").setBusinessCode(req.getBagCode()).setBatchId(2L).setStatus(20)); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setCode("BATCH").setItemId(3L)); var warehouse=mock(cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService.class); var wmBatch=mock(cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService.class); ReflectionTestUtils.setField(service,"warehouseService",warehouse); ReflectionTestUtils.setField(service,"mesWmBatchService",wmBatch); when(warehouse.getWarehouseByCode(anyString())).thenReturn(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO().setId(4L)); when(wmBatch.getOrCreateByProBatch(any())).thenReturn(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO().setId(5L)); var result=service.createInbound(req); assertEquals("SUCCESS",result.getStatus()); var tx=org.mockito.ArgumentCaptor.forClass(cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO.class); verify(transactions).createTransaction(tx.capture()); assertEquals(1L,tx.getValue().getBagCodeId()); assertEquals("9999999999999999",tx.getValue().getBagCode()); assertEquals("8888888888888888",result.getBagCode()); } @Test void palletBusinessScanUsesActualBagCountAndOriginalBatch(){ req.setScanType("PALLET");req.setBagCode("7777777777777777"); when(pallets.selectByScanCodeForUpdate(req.getBagCode())).thenReturn(new MesProPalletDO().setId(7L).setBatchId(2L).setStatus(0).setBagCount(25)); when(bags.selectPendingInboundCountByPalletId(7L)).thenReturn(25); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setCode("BATCH").setItemId(3L)); assertWholeInbound(7L,25); verify(pallets).markInbound(eq(7L),any()); } @Test void batchBusinessScanUsesActualBagCountAndOriginalBatch(){ req.setScanType("BATCH");req.setBagCode("6666666666666666"); when(batches.selectByScanCodeForUpdate(req.getBagCode())).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setCode("BATCH").setItemId(3L).setBagQuantity(50)); when(bags.selectPendingInboundCountByBatchId(2L)).thenReturn(50); assertWholeInbound(2L,50); verify(batches).refreshInboundProgress(eq(2L),any(),any(),any()); } @Test void partlyInboundPalletWritesOnlyPendingBags(){ req.setScanType("PALLET"); when(pallets.selectByScanCodeForUpdate("123")).thenReturn(new MesProPalletDO().setId(7L).setBatchId(2L).setStatus(10).setBagCount(25)); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setCode("BATCH").setItemId(3L)); when(bags.selectPendingInboundCountByPalletId(7L)).thenReturn(24); assertWholeInbound(7L,24); } @Test void partlyInboundBatchIsNotRejectedByEarlierBagEvent(){ req.setScanType("BATCH"); when(batches.selectByScanCodeForUpdate("123")).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setInboundEventId(9L).setCode("BATCH").setItemId(3L).setBagQuantity(25)); when(bags.selectPendingInboundCountByBatchId(2L)).thenReturn(24); assertWholeInbound(2L,24); } @Test void fullyInboundBatchDoesNotCreateAnotherStockTransaction(){ req.setScanType("BATCH"); when(batches.selectByScanCodeForUpdate("123")).thenReturn(new MesProBatchDO().setId(2L).setStatus(30).setCode("BATCH")); assertFailed("MES_SCAN_NO_PENDING_BAGS"); } private void assertWholeInbound(Long targetId,int quantity){ var warehouse=mock(cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService.class); var wmBatch=mock(cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService.class); ReflectionTestUtils.setField(service,"warehouseService",warehouse); ReflectionTestUtils.setField(service,"mesWmBatchService",wmBatch); when(warehouse.getWarehouseByCode(anyString())).thenReturn(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO().setId(4L)); when(wmBatch.getOrCreateByProBatch(any())).thenReturn(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO().setId(5L)); var result=service.createInbound(req);assertEquals("SUCCESS",result.getStatus()); assertEquals(req.getBagCode(),result.getBagCode());assertEquals(BigDecimal.valueOf(quantity),result.getQuantity()); var tx=org.mockito.ArgumentCaptor.forClass(cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO.class); verify(transactions).createTransaction(tx.capture());assertEquals(targetId,tx.getValue().getBizLineId()); assertEquals("BATCH",tx.getValue().getBatchCode());assertEquals(BigDecimal.valueOf(quantity),tx.getValue().getQuantity()); } private void assertFailed(String code){ MesProScanEventDO result=service.createInbound(req); assertEquals("FAILED",result.getStatus());assertEquals(code,result.getErrorCode()); verify(events).insert(result);verify(events).updateById(result);verifyNoInteractions(transactions); } }