fix: 使用锁定库存快照保存出库明细
This commit is contained in:
@@ -54,7 +54,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public String createOutbound(IostorInvCreateReqVO reqVO) {
|
public String createOutbound(IostorInvCreateReqVO reqVO) {
|
||||||
validateAndLockInventory(reqVO);
|
Map<Long, AvailableInventoryRespVO> inventorySnapshot = validateAndLockInventory(reqVO);
|
||||||
|
|
||||||
String ruleCode = "IO_CODE";
|
String ruleCode = "IO_CODE";
|
||||||
CodeGenerateReqDTO codeGenerateReqDTO = new CodeGenerateReqDTO();
|
CodeGenerateReqDTO codeGenerateReqDTO = new CodeGenerateReqDTO();
|
||||||
@@ -95,24 +95,25 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
detailDO.setIostorinvdtlId(IdUtil.fastSimpleUUID());
|
detailDO.setIostorinvdtlId(IdUtil.fastSimpleUUID());
|
||||||
detailDO.setIostorinvId(iostorInv.getIostorinvId());
|
detailDO.setIostorinvId(iostorInv.getIostorinvId());
|
||||||
detailDO.setSeqNo(index + 1);
|
detailDO.setSeqNo(index + 1);
|
||||||
detailDO.setMaterialCode(detail.getMaterialCode());
|
AvailableInventoryRespVO inventory = inventorySnapshot.get(detail.getGroupId());
|
||||||
detailDO.setMaterialId(detail.getMaterialId());
|
detailDO.setMaterialCode(inventory == null ? detail.getMaterialCode() : inventory.getMaterialCode());
|
||||||
detailDO.setPcsn(detail.getPcsn());
|
detailDO.setMaterialId(inventory == null ? detail.getMaterialId() : inventory.getMaterialId());
|
||||||
|
detailDO.setPcsn(inventory == null ? detail.getPcsn() : inventory.getPcsn());
|
||||||
detailDO.setPlanQty(detail.getPlanQty());
|
detailDO.setPlanQty(detail.getPlanQty());
|
||||||
detailDO.setUnassignQty(detail.getPlanQty());
|
detailDO.setUnassignQty(detail.getPlanQty());
|
||||||
detailDO.setAssignQty(BigDecimal.ZERO);
|
detailDO.setAssignQty(BigDecimal.ZERO);
|
||||||
detailDO.setQtyUnitId(detail.getQtyUnitId());
|
detailDO.setQtyUnitId(inventory == null ? detail.getQtyUnitId() : inventory.getQtyUnitId());
|
||||||
detailDO.setQtyUnitName(detail.getQtyUnitName());
|
detailDO.setQtyUnitName(inventory == null ? detail.getQtyUnitName() : inventory.getQtyUnitName());
|
||||||
detailDO.setSourceBillCode(detail.getSourceBillCode());
|
detailDO.setSourceBillCode(inventory == null ? detail.getSourceBillCode() : inventory.getExtCode());
|
||||||
detailDO.setSourceBillType(detail.getSourceBillType());
|
detailDO.setSourceBillType(inventory == null ? detail.getSourceBillType() : inventory.getExtType());
|
||||||
detailDO.setSourceBilldtlId(detail.getSourceBilldtlId());
|
detailDO.setSourceBilldtlId(inventory == null ? detail.getSourceBilldtlId() : inventory.getExtDtlCode());
|
||||||
detailDO.setRemark(detail.getRemark());
|
detailDO.setRemark(detail.getRemark());
|
||||||
iostorinvDtlMapper.insert(detailDO);
|
iostorinvDtlMapper.insert(detailDO);
|
||||||
}
|
}
|
||||||
return iostorInv.getIostorinvId();
|
return iostorInv.getIostorinvId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateAndLockInventory(IostorInvCreateReqVO reqVO) {
|
private Map<Long, AvailableInventoryRespVO> validateAndLockInventory(IostorInvCreateReqVO reqVO) {
|
||||||
List<IostorInvCreateReqVO.Detail> inventoryDetails = new ArrayList<>();
|
List<IostorInvCreateReqVO.Detail> inventoryDetails = new ArrayList<>();
|
||||||
Set<Long> requestedGroupIds = new HashSet<>();
|
Set<Long> requestedGroupIds = new HashSet<>();
|
||||||
Set<String> vehicleCodes = new LinkedHashSet<>();
|
Set<String> vehicleCodes = new LinkedHashSet<>();
|
||||||
@@ -132,7 +133,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
vehicleCodes.add(detail.getVehicleCode());
|
vehicleCodes.add(detail.getVehicleCode());
|
||||||
}
|
}
|
||||||
if (inventoryDetails.isEmpty()) {
|
if (inventoryDetails.isEmpty()) {
|
||||||
return;
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AvailableInventoryRespVO> lockedRows =
|
List<AvailableInventoryRespVO> lockedRows =
|
||||||
@@ -158,6 +159,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 建单仅验证库存快照;库存占用和冻结数量由后续分配流程处理。
|
// 建单仅验证库存快照;库存占用和冻结数量由后续分配流程处理。
|
||||||
|
return lockedByGroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class IostorInvServiceLocalSpringTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCreateOutboundHeaderAndDetailsInOneTransaction() {
|
void shouldCreateOutboundUsingLockedInventoryIdentityInsteadOfClientUnitAndSource() {
|
||||||
String id = iostorInvService.createOutbound(buildRequest());
|
String id = iostorInvService.createOutbound(buildRequest());
|
||||||
|
|
||||||
assertNotNull(id);
|
assertNotNull(id);
|
||||||
@@ -101,10 +101,10 @@ class IostorInvServiceLocalSpringTest {
|
|||||||
assertEquals(id, details.get(1).headerId());
|
assertEquals(id, details.get(1).headerId());
|
||||||
assertEquals(new DetailRow(details.get(0).detailId(), id, 1, "MAT-01", "MID-01", "PCSN-01",
|
assertEquals(new DetailRow(details.get(0).detailId(), id, 1, "MAT-01", "MID-01", "PCSN-01",
|
||||||
new BigDecimal("12.500"), BigDecimal.ZERO.setScale(3), new BigDecimal("12.500"),
|
new BigDecimal("12.500"), BigDecimal.ZERO.setScale(3), new BigDecimal("12.500"),
|
||||||
"KG", "千克", "SRC-01", "ORDER", "SRC-DTL-01", "明细一"), details.get(0));
|
"DB-KG", "数据库千克", "DB-SRC-01", "DB-TYPE", "DB-DTL-01", "明细一"), details.get(0));
|
||||||
assertEquals(new DetailRow(details.get(1).detailId(), id, 2, "MAT-02", "MID-02", "PCSN-02",
|
assertEquals(new DetailRow(details.get(1).detailId(), id, 2, "MAT-02", "MID-02", "PCSN-02",
|
||||||
new BigDecimal("7.250"), BigDecimal.ZERO.setScale(3), new BigDecimal("7.250"),
|
new BigDecimal("7.250"), BigDecimal.ZERO.setScale(3), new BigDecimal("7.250"),
|
||||||
"KG", "千克", "SRC-02", "ORDER", "SRC-DTL-02", "明细二"), details.get(1));
|
"DB-KG", "数据库千克", "DB-SRC-02", "DB-TYPE", "DB-DTL-02", "明细二"), details.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -200,6 +200,14 @@ class IostorInvServiceLocalSpringTest {
|
|||||||
assertInventoryRejected(buildRequest());
|
assertInventoryRejected(buildRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectInventoryChangedToPartiallyFrozenBelowRequestedQuantity() {
|
||||||
|
jdbcTemplate.update("UPDATE wms_group_plate SET qty = 10, frozen_qty = 6 WHERE group_id = 101");
|
||||||
|
IostorInvCreateReqVO request = buildRequest();
|
||||||
|
request.getDetails().get(0).setPlanQty(new BigDecimal("5"));
|
||||||
|
assertInventoryRejected(request);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertInventoryRejected(IostorInvCreateReqVO request) {
|
private void assertInventoryRejected(IostorInvCreateReqVO request) {
|
||||||
ServiceException ex = assertThrows(ServiceException.class, () -> iostorInvService.createOutbound(request));
|
ServiceException ex = assertThrows(ServiceException.class, () -> iostorInvService.createOutbound(request));
|
||||||
assertEquals("出库库存已变化,请刷新后重新选择完整箱库存", ex.getMessage());
|
assertEquals("出库库存已变化,请刷新后重新选择完整箱库存", ex.getMessage());
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ CREATE TABLE wms_group_plate (group_id BIGINT PRIMARY KEY, vehicle_code VARCHAR(
|
|||||||
INSERT INTO base_materialbase VALUES ('MID-01','物料一',FALSE), ('MID-02','物料二',FALSE);
|
INSERT INTO base_materialbase VALUES ('MID-01','物料一',FALSE), ('MID-02','物料二',FALSE);
|
||||||
INSERT INTO wms_structattr VALUES ('S1','BOX-01','STOR-01','S01','一号仓',FALSE), ('S2','BOX-02','STOR-01','S01','一号仓',FALSE), ('S3','BOX-X','STOR-02','S02','二号仓',FALSE);
|
INSERT INTO wms_structattr VALUES ('S1','BOX-01','STOR-01','S01','一号仓',FALSE), ('S2','BOX-02','STOR-01','S01','一号仓',FALSE), ('S3','BOX-X','STOR-02','S02','二号仓',FALSE);
|
||||||
INSERT INTO wms_group_plate VALUES
|
INSERT INTO wms_group_plate VALUES
|
||||||
(101,'BOX-01','可用','MID-01','MAT-01','PCSN-01',12.500,NULL,'KG','千克',NULL,NULL,NULL,FALSE),
|
(101,'BOX-01','可用','MID-01','MAT-01','PCSN-01',12.500,NULL,'DB-KG','数据库千克','DB-SRC-01','DB-TYPE','DB-DTL-01',FALSE),
|
||||||
(102,'BOX-02','可用','MID-02','MAT-02','PCSN-02',8.000,0.750,'KG','千克',NULL,NULL,NULL,FALSE),
|
(102,'BOX-02','可用','MID-02','MAT-02','PCSN-02',8.000,0.750,'DB-KG','数据库千克','DB-SRC-02','DB-TYPE','DB-DTL-02',FALSE),
|
||||||
(103,'BOX-X','可用','MID-01','MAT-01','PCSN-X',5.000,0,'KG','千克',NULL,NULL,NULL,FALSE);
|
(103,'BOX-X','可用','MID-01','MAT-01','PCSN-X',5.000,0,'KG','千克',NULL,NULL,NULL,FALSE);
|
||||||
|
|||||||
Reference in New Issue
Block a user