Browse Source

feat(inspection): 新增验货申请明细功能

- 在InspectionController中添加addDetails和editDetails接口
- 创建InspectionRequestAddDetailVO和InspectionRequestEditVO数据传输对象
- 在InspectionRequestDetailMapper中增加查询最大行号的方法
- 实现新增明细的业务逻辑,支持按PO明细分组累加数量
- 实现编辑明细的业务逻辑,先回滚原数量再插入新明细
- 添加站点验证和状态校验确保操作合规性
- 完善日志记录和异常处理机制
master
qiankanghui 4 days ago
parent
commit
9717670f76
  1. 18
      src/main/java/com/xujie/modules/inspection/controller/InspectionController.java
  2. 27
      src/main/java/com/xujie/modules/inspection/data/InspectionRequestAddDetailVO.java
  3. 27
      src/main/java/com/xujie/modules/inspection/data/InspectionRequestEditVO.java
  4. 5
      src/main/java/com/xujie/modules/inspection/mapper/InspectionRequestDetailMapper.java
  5. 10
      src/main/java/com/xujie/modules/inspection/service/InspectionRequestService.java
  6. 267
      src/main/java/com/xujie/modules/inspection/service/impl/InspectionRequestServiceImpl.java
  7. 8
      src/main/resources/mapper/inspection/InspectionRequestDetailMapper.xml

18
src/main/java/com/xujie/modules/inspection/controller/InspectionController.java

@ -134,6 +134,24 @@ public class InspectionController {
return R.ok();
}
/**
* 新增验货申请明细
*/
@PostMapping("/addDetails")
public R addDetails(@RequestBody InspectionRequestAddDetailVO vo){
inspectionRequestService.addDetails(vo);
return R.ok();
}
/**
* 编辑验货申请明细
*/
@PostMapping("/editDetails")
public R editDetails(@RequestBody InspectionRequestEditVO vo){
inspectionRequestService.editDetails(vo);
return R.ok();
}
/**
* 修改验货申请仅允许修改建议验货日期验货地址和联系人
*/

27
src/main/java/com/xujie/modules/inspection/data/InspectionRequestAddDetailVO.java

@ -0,0 +1,27 @@
package com.xujie.modules.inspection.data;
import lombok.Data;
import java.util.List;
/**
* 新增验货申请明细VO
*/
@Data
public class InspectionRequestAddDetailVO {
/**
* 申请单号
*/
private String requestNo;
/**
* 站点/工厂编码
*/
private String site;
/**
* 要新增的明细列表
*/
private List<InspectionRequestItemVO> items;
}

27
src/main/java/com/xujie/modules/inspection/data/InspectionRequestEditVO.java

@ -0,0 +1,27 @@
package com.xujie.modules.inspection.data;
import lombok.Data;
import java.util.List;
/**
* 编辑验货申请明细VO
*/
@Data
public class InspectionRequestEditVO {
/**
* 申请单号前端传入
*/
private String requestNo;
/**
* 站点/工厂编码前端传入可选
*/
private String site;
/**
* 新的明细列表替换原有明细
*/
private List<InspectionRequestItemVO> items;
}

5
src/main/java/com/xujie/modules/inspection/mapper/InspectionRequestDetailMapper.java

@ -104,4 +104,9 @@ public interface InspectionRequestDetailMapper extends BaseMapper<InspectionRequ
@Param("requestNo") String requestNo,
@Param("itemNo") Integer itemNo,
@Param("approveQty") BigDecimal approveQty);
/**
* 查询指定申请单的最大行号
*/
Integer selectMaxItemNo(@Param("site") String site, @Param("requestNo") String requestNo);
}

10
src/main/java/com/xujie/modules/inspection/service/InspectionRequestService.java

@ -29,6 +29,16 @@ public interface InspectionRequestService extends IService<InspectionRequestHead
void saveRequest(InspectionRequestSaveVO vo);
/**
* 新增验货申请明细
*/
void addDetails(InspectionRequestAddDetailVO vo);
/**
* 编辑验货申请明细先删除原有明细并回滚数量再插入新明细
*/
void editDetails(InspectionRequestEditVO vo);
void cancel(String requestNo);
void delete(String requestNo);

267
src/main/java/com/xujie/modules/inspection/service/impl/InspectionRequestServiceImpl.java

@ -180,7 +180,7 @@ public class InspectionRequestServiceImpl extends ServiceImpl<InspectionRequestH
// 4.1 生成Sub
for (InspectionRequestItemVO item : subList) {
// 查询当前PO明细的待验货数量信息
java.util.Map<String, Object> poInfo = poDetailMapper.getPoDetailInspectInfo(
Map<String, Object> poInfo = poDetailMapper.getPoDetailInspectInfo(
site, item.getOrderNo(), item.getItemNo());
int updateCount = poDetailMapper.lockInspectQty(
@ -279,6 +279,271 @@ public class InspectionRequestServiceImpl extends ServiceImpl<InspectionRequestH
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void addDetails(InspectionRequestAddDetailVO vo) {
if (vo == null || !StringUtils.hasText(vo.getRequestNo())) {
throw new RuntimeException("申请单号不能为空");
}
if (vo.getItems() == null || vo.getItems().isEmpty()) {
throw new RuntimeException("明细列表不能为空");
}
String site = vo.getSite();
if (!StringUtils.hasText(site)) {
SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
site = user.getSite();
}
String requestNo = vo.getRequestNo().trim();
// 1. 校验头表是否存在
InspectionRequestHeader header = baseMapper.selectByRequestNo(site, requestNo);
if (header == null) {
throw new RuntimeException("检验申请单不存在: " + requestNo);
}
// 2. 只有草稿和待审核状态允许新增明细
if (!"Draft".equals(header.getStatusDb()) && !"Confirmed".equals(header.getStatusDb())) {
throw new RuntimeException("只有草稿和待审核状态的验货申请允许新增明细,当前状态:" + header.getStatus());
}
log.info("开始新增验货申请明细: site={}, requestNo={}", site, requestNo);
// 3. 获取当前最大的itemNo用于生成新的行号
Integer maxItemNo = inspectionRequestDetailMapper.selectMaxItemNo(site, requestNo);
int requestItemNo = (maxItemNo == null ? 0 : maxItemNo) + 1;
// 4. 按真正的PO明细orderNo + itemNo分组相同的PO明细累加数量
// 如果itemNo为null则使用poItemNo
Map<String, List<InspectionRequestItemVO>> groupMap =
vo.getItems()
.stream()
.peek(item -> {
// 如果itemNo为null使用poItemNo作为备选
if (item.getItemNo() == null && item.getPoItemNo() != null) {
item.setItemNo(item.getPoItemNo());
}
})
.collect(Collectors.groupingBy(
item -> item.getOrderNo() + "|" + item.getItemNo(),
LinkedHashMap::new,
Collectors.toList()
));
// 5. 遍历分组生成Detail和Sub
for (Map.Entry<String, List<InspectionRequestItemVO>> entry : groupMap.entrySet()) {
List<InspectionRequestItemVO> subList = entry.getValue();
InspectionRequestItemVO firstItem = subList.get(0);
// 计算该PO明细的总数量累加
BigDecimal totalQty = BigDecimal.ZERO;
for (InspectionRequestItemVO item : subList) {
totalQty = totalQty.add(item.getQty());
}
// 使用第一条数据的其他字段但数量用累加后的总数
InspectionRequestItemVO mergedItem = new InspectionRequestItemVO();
mergedItem.setOrderNo(firstItem.getOrderNo());
mergedItem.setItemNo(firstItem.getItemNo());
mergedItem.setOrderRef1(firstItem.getOrderRef1());
mergedItem.setOrderRef2(firstItem.getOrderRef2());
mergedItem.setPartNo(firstItem.getPartNo());
mergedItem.setQty(totalQty); // 使用累加后的数量
mergedItem.setShipMethod(firstItem.getShipMethod());
mergedItem.setCrd(firstItem.getCrd());
// 查询当前PO明细的待验货数量信息
java.util.Map<String, Object> poInfo = poDetailMapper.getPoDetailInspectInfo(
site, mergedItem.getOrderNo(), mergedItem.getItemNo());
// 锁定验货数量使用累加后的总数量
int updateCount = poDetailMapper.lockInspectQty(
site,
mergedItem.getOrderNo(),
mergedItem.getItemNo(),
mergedItem.getQty()
);
if (updateCount == 0) {
log.error("锁定验货数量失败: orderNo={}, itemNo={}, qty={}",
mergedItem.getOrderNo(), mergedItem.getItemNo(), mergedItem.getQty());
throw new RuntimeException(
"PO:" + mergedItem.getOrderNo()
+ " 行:" + mergedItem.getItemNo()
+ " 待验货数量不足"
);
}
// 保存Sub
InspectionRequestDetailSub sub = new InspectionRequestDetailSub();
sub.setSite(site);
sub.setRequestNo(requestNo);
sub.setItemNo(requestItemNo);
sub.setPoNo(mergedItem.getOrderNo());
sub.setPoItemNo(mergedItem.getItemNo());
sub.setPartNo(mergedItem.getPartNo());
sub.setQty(mergedItem.getQty()); // 使用累加后的数量
sub.setOrderRef1(mergedItem.getOrderRef1());
sub.setOrderRef2(mergedItem.getOrderRef2());
sub.setShipMothod(mergedItem.getShipMethod());
sub.setCrd(mergedItem.getCrd());
// 计算数量是否变更
Object qtyObj = poInfo.get("waitInspectQty");
BigDecimal originQty = qtyObj == null ? BigDecimal.ZERO : new BigDecimal(qtyObj.toString());
BigDecimal reqQty = mergedItem.getQty() == null ? BigDecimal.ZERO : mergedItem.getQty();
boolean isChanged = reqQty.compareTo(originQty) != 0;
sub.setIsChanged(isChanged ? "Y" : "N");
inspectionRequestDetailSubMapper.insert(sub);
// 5.2 保存Detail汇总
InspectionRequestDetail detail = new InspectionRequestDetail();
detail.setSite(site);
detail.setRequestNo(requestNo);
detail.setItemNo(requestItemNo);
detail.setOrderRef1(mergedItem.getOrderRef1());
detail.setPartNo(mergedItem.getPartNo());
detail.setQty(totalQty); // 使用累加后的数量
detail.setStatus("草稿");
detail.setStatusDb("Draft");
inspectionRequestDetailMapper.insert(detail);
requestItemNo++;
}
log.info("新增验货申请明细完成: site={}, requestNo={}, 新增明细数量={}",
site, requestNo, vo.getItems().size());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void editDetails(InspectionRequestEditVO vo) {
if (vo == null || !StringUtils.hasText(vo.getRequestNo())) {
throw new RuntimeException("申请单号不能为空");
}
if (vo.getItems() == null || vo.getItems().isEmpty()) {
throw new RuntimeException("明细列表不能为空");
}
String site = vo.getSite();
if (!StringUtils.hasText(site)) {
SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
site = user.getSite();
}
String requestNo = vo.getRequestNo().trim();
// 1. 校验头表是否存在
InspectionRequestHeader header = baseMapper.selectByRequestNo(site, requestNo);
if (header == null) {
throw new RuntimeException("检验申请单不存在: " + requestNo);
}
// 2. 只有草稿和待审核状态允许编辑明细
if (!"Draft".equals(header.getStatusDb()) && !"Confirmed".equals(header.getStatusDb())) {
throw new RuntimeException("只有草稿和待审核状态的验货申请允许编辑明细,当前状态:" + header.getStatus());
}
log.info("开始编辑验货申请明细: site={}, requestNo={}", site, requestNo);
// 3. 回滚原明细数量并删除
List<InspectionRequestDetailSub> oldSubList =
inspectionRequestDetailSubMapper.selectByRequestNo(site, requestNo);
for (InspectionRequestDetailSub sub : oldSubList) {
int count = poDetailMapper.rollbackInspectQty(
site,
sub.getPoNo(),
sub.getPoItemNo(),
sub.getQty()
);
if (count == 0) {
throw new RuntimeException(
"回退PO待验货数量失败:" + sub.getPoNo()
+ "-" + sub.getPoItemNo()
);
}
}
// 4. 删除原有明细记录
int subDeleteCount = inspectionRequestDetailSubMapper.deleteByRequestNo(site, requestNo);
int detailDeleteCount = inspectionRequestDetailMapper.deleteByRequestNo(site, requestNo);
log.info("删除原有明细完成: subDeleteCount={}, detailDeleteCount={}", subDeleteCount, detailDeleteCount);
// 5. 插入新明细并锁定数量不复用addDetails避免分组合并
int requestItemNo = 1;
for (InspectionRequestItemVO item : vo.getItems()) {
// 如果itemNo为null使用poItemNo作为备选
if (item.getItemNo() == null && item.getPoItemNo() != null) {
item.setItemNo(item.getPoItemNo());
}
// 查询当前PO明细的待验货数量信息
java.util.Map<String, Object> poInfo = poDetailMapper.getPoDetailInspectInfo(
site, item.getOrderNo(), item.getItemNo());
// 锁定验货数量
int updateCount = poDetailMapper.lockInspectQty(
site,
item.getOrderNo(),
item.getItemNo(),
item.getQty()
);
if (updateCount == 0) {
log.error("锁定验货数量失败: orderNo={}, itemNo={}, qty={}",
item.getOrderNo(), item.getItemNo(), item.getQty());
throw new RuntimeException(
"PO:" + item.getOrderNo()
+ " 行:" + item.getItemNo()
+ " 待验货数量不足"
);
}
// 保存Sub
InspectionRequestDetailSub sub = new InspectionRequestDetailSub();
sub.setSite(site);
sub.setRequestNo(requestNo);
sub.setItemNo(requestItemNo);
sub.setPoNo(item.getOrderNo());
sub.setPoItemNo(item.getItemNo());
sub.setPartNo(item.getPartNo());
sub.setQty(item.getQty());
sub.setOrderRef1(item.getOrderRef1());
sub.setOrderRef2(item.getOrderRef2());
sub.setShipMothod(item.getShipMethod());
sub.setCrd(item.getCrd());
// 计算数量是否变更
Object qtyObj = poInfo.get("waitInspectQty");
BigDecimal originQty = qtyObj == null ? BigDecimal.ZERO : new BigDecimal(qtyObj.toString());
BigDecimal reqQty = item.getQty() == null ? BigDecimal.ZERO : item.getQty();
boolean isChanged = reqQty.compareTo(originQty) != 0;
sub.setIsChanged(isChanged ? "Y" : "N");
inspectionRequestDetailSubMapper.insert(sub);
// 保存Detail汇总
InspectionRequestDetail detail = new InspectionRequestDetail();
detail.setSite(site);
detail.setRequestNo(requestNo);
detail.setItemNo(requestItemNo);
detail.setOrderRef1(item.getOrderRef1());
detail.setPartNo(item.getPartNo());
detail.setQty(item.getQty());
detail.setStatus("草稿");
detail.setStatusDb("Draft");
inspectionRequestDetailMapper.insert(detail);
requestItemNo++;
}
log.info("编辑验货申请明细完成: site={}, requestNo={}, 新明细数量={}",
site, requestNo, vo.getItems().size());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void cancel(String requestNo) {

8
src/main/resources/mapper/inspection/InspectionRequestDetailMapper.xml

@ -278,4 +278,12 @@
AND item_no = #{itemNo}
</update>
<!-- 查询指定申请单的最大行号 -->
<select id="selectMaxItemNo" resultType="java.lang.Integer">
SELECT MAX(item_no)
FROM srm_inspect_request_detail
WHERE site = #{site}
AND request_no = #{requestNo}
</select>
</mapper>
Loading…
Cancel
Save