diff --git a/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClient.java b/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClient.java index 1c875b747..7d6973124 100644 --- a/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClient.java +++ b/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClient.java @@ -48,6 +48,8 @@ public interface IErpDataWmsClient { String GET_MAINTEN_STATUS = API_PREFIX + "/getMaintenStatus"; + String SEND_RBFIRT_CHK = API_PREFIX + "/sendRbfirtChk"; + /** * 到期送检发送erp */ @@ -90,6 +92,12 @@ public interface IErpDataWmsClient { @RequestParam("needDate") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date needDate, @RequestParam("flag")String flag ,@RequestParam("checkQty")Double checkQty, @RequestParam("quantityLevel")String quantityLevel) throws BusinessException; + /** + * 额外玻璃粉验证 + */ + @PostMapping(SEND_RBFIRT_CHK) + R sendRbfirtChk(@RequestParam("goodsCode")String goodsCode, @RequestParam("goodsName")String goodsName) throws BusinessException; + /** * 物料状态监控同步 */ diff --git a/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClientFallback.java b/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClientFallback.java index 3c8a59b41..0af8ef1ed 100644 --- a/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClientFallback.java +++ b/blade-service-api/blade-erpdata-api/src/main/java/org/springblade/erpdata/feign/IErpDataWmsClientFallback.java @@ -57,6 +57,11 @@ public class IErpDataWmsClientFallback implements IErpDataWmsClient{ return R.fail("获取数据失败"); } + @Override + public R sendRbfirtChk(@RequestParam("goodsCode")String goodsCode, @RequestParam("goodsName")String goodsName) { + return R.fail("获取数据失败"); + } + @Override public R getGoodsExtStatus(@RequestParam("goodsCode")String goodsCode) { return R.fail("获取数据失败"); diff --git a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/StockOccupyTempDTO.java b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/StockOccupyTempDTO.java new file mode 100644 index 000000000..e37f5a25c --- /dev/null +++ b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/StockOccupyTempDTO.java @@ -0,0 +1,20 @@ +package org.springblade.wms.pojo.dto; + +import lombok.Data; +import org.springblade.wms.pojo.entity.StRealtimeStock; + +/** + * @version 1.0 + * @program: jonhon-mes-svr + * @ClassName StockOccupyTempDTO + * @description: + * @autor: WuSiYu + * @create 2026-06-02 18:06 + **/ +@Data +public class StockOccupyTempDTO { + + private String subGoodsCode; + private StRealtimeStock stock; + private Double takeQty; +} diff --git a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/SubItemStockDTO.java b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/SubItemStockDTO.java new file mode 100644 index 000000000..e31781f1e --- /dev/null +++ b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/dto/SubItemStockDTO.java @@ -0,0 +1,25 @@ +package org.springblade.wms.pojo.dto; + +import lombok.Data; +import org.springblade.wms.pojo.entity.StRealtimeStock; + +import java.util.List; + +/** + * @version 1.0 + * @program: jonhon-mes-svr + * @ClassName SubItemStockDTO + * @description: + * @autor: WuSiYu + * @create 2026-06-02 17:49 + **/ +@Data +public class SubItemStockDTO { + + private String subGoodsCode; + // 单套耗用 + private Double singleUseQty; + private List stockList; + // 剩余可用总库存(动态扣减) + private Double remainUsable; +} diff --git a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGlassCakeOut.java b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGlassCakeOut.java index e8eb2ab01..1c048670d 100644 --- a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGlassCakeOut.java +++ b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGlassCakeOut.java @@ -138,6 +138,11 @@ public class StGlassCakeOut extends TenantEntity { */ @Schema(description = "领料人ID") private Long picker; + /** + * 预出数量 + */ + @Schema(description = "预出数量") + private Double preOutQty; /** * 是否印字 */ diff --git a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGoods.java b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGoods.java index 399f5aa7e..3aeac2dd6 100644 --- a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGoods.java +++ b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGoods.java @@ -277,6 +277,11 @@ public class StGoods extends BaseEntity { */ @Schema(description = "生产批量") private Double productionBatch; + /** + * 换算比例 + */ + @Schema(description = "换算比例") + private Double conversionRatio; @TableField(exist = false) private Double avlQuantity; diff --git a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGraphiteMoldOut.java b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGraphiteMoldOut.java index 0b91e948b..0351d8457 100644 --- a/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGraphiteMoldOut.java +++ b/blade-service-api/blade-wms-api/src/main/java/org/springblade/wms/pojo/entity/StGraphiteMoldOut.java @@ -148,4 +148,16 @@ public class StGraphiteMoldOut extends TenantEntity { @Schema(description = "领料人ID") private Long picker; + /** + * 父出库号 + */ + @Schema(description = "父出库号") + private String parentOutCode; + + /** + * 齐套数量 + */ + @Schema(description = "齐套数量") + private Double completeQuantity; + } diff --git a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/feign/ErpDataWmsClient.java b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/feign/ErpDataWmsClient.java index 2cb8b02c7..f390bbb99 100644 --- a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/feign/ErpDataWmsClient.java +++ b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/feign/ErpDataWmsClient.java @@ -62,6 +62,11 @@ public class ErpDataWmsClient implements IErpDataWmsClient{ return R.data(erpDataWmsService.sendErpMaintenance(goodsCode, shCode, location, needDate, flag, checkQty, quantityLevel)); } + @Override + public R sendRbfirtChk(String goodsCode, String goodsName) throws BusinessException { + return R.data(erpDataWmsService.sendRbfirtChk(goodsCode, goodsName)); + } + @Override public R getGoodsExtStatus(String goodsCode) { return R.data(erpDataWmsService.getGoodsExtStatus(goodsCode)); diff --git a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/IErpDataWmsService.java b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/IErpDataWmsService.java index 76f732480..e635390fb 100644 --- a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/IErpDataWmsService.java +++ b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/IErpDataWmsService.java @@ -36,4 +36,6 @@ public interface IErpDataWmsService { String sendErpMaintenance(String goodsCode, String shCode, String location, Date needDate, String flag, Double checkQty, String quantityLevel) throws BusinessException; MeasuringToolMaintainVO getMaintenStatus(String goodsCode, String shCode, String location, Date createTime); + + String sendRbfirtChk(String goodsCode, String goodsName) throws BusinessException; } diff --git a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/impl/ErpDataWmsServiceImpl.java b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/impl/ErpDataWmsServiceImpl.java index 3af57fa64..2dd6379fb 100644 --- a/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/impl/ErpDataWmsServiceImpl.java +++ b/blade-service/blade-erpdata/src/main/java/org/springblade/erpdata/service/impl/ErpDataWmsServiceImpl.java @@ -305,6 +305,42 @@ public class ErpDataWmsServiceImpl implements IErpDataWmsService { return excflag; } + @Override + public String sendRbfirtChk(String goodsCode, String goodsName) throws BusinessException { + SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate) + // 指定存储过程名 + DB Link(和原代码一致) + .withProcedureName("dba_mgr.pro_firtchk") + // DBLink 必须关闭元数据访问 + .withoutProcedureColumnMetaDataAccess() + // 显式声明参数(顺序必须和存储过程定义一致) + .declareParameters( + new SqlParameter("v_prtno", Types.VARCHAR), // IN 物料号 + new SqlParameter("v_prtdesc", Types.VARCHAR), // IN 物料名称 + new SqlParameter("v_desno", Types.VARCHAR), // IN 设计通知单号 + new SqlOutParameter("v_excnote", Types.VARCHAR),// OUT 异常信息 + new SqlOutParameter("v_excflag", Types.VARCHAR) // OUT 执行标识 + ); + + // 封装输入参数 + Map inParams = new HashMap<>(); + inParams.put("v_prtno", goodsCode == null ? "" : goodsCode); + inParams.put("v_prtdesc", goodsName == null ? "" : goodsName); + inParams.put("v_desno", null); + + // 执行存储过程 + Map resultMap = jdbcCall.execute(inParams); + + // 提取输出参数 + String excflag = (String) resultMap.get("v_excflag"); + String excnote = (String) resultMap.get("v_excnote"); + + // 业务异常判断 + if ("0".equals(excflag)) { + throw new BusinessException("玻璃粉验证通知单发送失败,请联系信息部!!! " + excnote); + } + return excflag; + } + @Override public StGoodsExtStatusVO getGoodsExtStatus(String goodsCode){ return erpDataWmsMapper.getGoodsExtStatus(goodsCode); diff --git a/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StBuyOrderServiceImpl.java b/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StBuyOrderServiceImpl.java index cb1564add..d9cd2fc67 100644 --- a/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StBuyOrderServiceImpl.java +++ b/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StBuyOrderServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springblade.common.exception.BusinessException; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.secure.BladeUser; @@ -193,7 +194,7 @@ public class StBuyOrderServiceImpl extends BaseServiceImpl roleName = sysClient.getRoleName(approvalRecord.getCurrentRoleId()); String roleNameData = roleName.getData(); approvalRecord.setCurrentRoleName(roleNameData); - approvalRecord.setNextRoleName("热表-核算员"); + approvalRecord.setNextRoleName("厂内核算员"); //租户ID 000000 R roleIdResp = sysClient.getRoleIds("000000", approvalRecord.getNextRoleName()); String roleId = roleIdResp.getData(); @@ -207,10 +208,17 @@ public class StBuyOrderServiceImpl extends BaseServiceImpl ruser = userClient.userInfoById(bo.getDeclareMan()); User declareMan = ruser.getData(); dto.setReqctlr(declareMan.getAccount()); // 当前登录人 - dto.setCheckdate(bo.getCheckDate() == null ? "" : DateUtil.format(bo.getCheckDate(), "yyyy-MM-dd")); - R userR = userClient.userInfoById(bo.getCheckMan()); + dto.setCheckdate(bo.getCheckDate() == null ? "" : DateUtil.format(bo.getApprovalTime2(), "yyyy-MM-dd")); + R userR = userClient.userInfoById(bo.getApprovalMan2()); User checkMan = userR.getData(); dto.setCheckman(checkMan.getAccount() == null ? "" : checkMan.getAccount()); pdList.add(dto); @@ -349,7 +357,7 @@ public class StBuyOrderServiceImpl extends BaseServiceImpl list = new ArrayList<>(); diff --git a/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StGlassCakeOutServiceImpl.java b/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StGlassCakeOutServiceImpl.java index 1780f5fd3..a6a4d1b00 100644 --- a/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StGlassCakeOutServiceImpl.java +++ b/blade-service/blade-wms/src/main/java/org/springblade/wms/service/impl/StGlassCakeOutServiceImpl.java @@ -283,10 +283,12 @@ public class StGlassCakeOutServiceImpl extends BaseServiceImpl realtimeStock.getQuantity()) { + if (param.getOutQty() > realtimeStock.getQuantity() - realtimeStock.getOccupyQuantity()) { throw new ServiceException("流程卡号【"+outEntity.getCardNo()+"】出库数量不能大于库存总数量,库存数量为:" + realtimeStock.getQuantity()); } // ============ 步骤2:根据出库单数据 构建 出入库记录对象【核心】 ============ @@ -565,7 +567,7 @@ public class StGlassCakeOutServiceImpl extends BaseServiceImpl selectStGraphiteMoldOutPage(IPage page, StGraphiteMoldOutVO stGraphiteMoldOut) { @@ -71,44 +83,108 @@ public class StGraphiteMoldOutServiceImpl extends BaseServiceImpl preOutStockList = new ArrayList<>(); + // 整套数量:需要生产多少套 + Double totalNeedSet = faYieldOrder.getYpQty(); + int totalNeedSetInt = Double.valueOf(Math.ceil(totalNeedSet)).intValue(); + + DsPartEntity partOne = partClient.getPart(faYieldOrder.getPartCode(), faYieldOrder.getPartVersion()); + List partRelationEntityList = partClient.getSubPart(partOne.getId()); + List allChildPartList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(partRelationEntityList)) { + // 提取所有非空的childPartId并去重(避免null和重复ID,减少查询压力) + List childPartIdList = partRelationEntityList.stream() + .map(DsPartRelationEntity::getChildPartId) // 提取子件ID + .filter(Objects::nonNull) // 过滤null的ID + .distinct() // 去重,避免重复查询 + .collect(Collectors.toList()); + + // 批量查询DS_PART表 + if (!CollectionUtils.isEmpty(childPartIdList)) { + // 调用dsPartService的批量查询方法(根据ID列表查DS_PART) + allChildPartList = partClient.batchParts(childPartIdList); + } + } - String goodsCode = faYieldOrder.getPartCode(); - Double requireQty = faYieldOrder.getYpQty(); - System.out.println("石墨模编码:" + goodsCode + ",总需求:" + requireQty); + List sourceSubStockList = new ArrayList<>(); + for (DsPartEntity bom : allChildPartList) { + SubItemStockDTO item = new SubItemStockDTO(); + item.setSubGoodsCode(bom.getPartCode()); + // 单套子件耗用数量 + DsPartRelationEntity dsPartRelation = stGlassCakeOutMapper.getPartQuota(faYieldOrder.getPartCode(), item.getSubGoodsCode()); + item.setSingleUseQty(dsPartRelation.getQuota()); + // 查询该子件所有可用库存 + List stockAll = stRealtimeStockMapper.selectMaxUsableStockByMoldAttr(item.getSubGoodsCode()); + item.setStockList(stockAll); + // 实时剩余可用总库存(动态变化,逐套消耗) + double usableTotal = 0D; + if (CollUtil.isNotEmpty(stockAll)) { + usableTotal = stockAll.stream() + .mapToDouble(s -> s.getQuantity() - Optional.ofNullable(s.getOccupyQuantity()).orElse(0D)) + .sum(); + } + item.setRemainUsable(usableTotal); + sourceSubStockList.add(item); + } - List candidateStockList = new ArrayList<>(); - double totalUsable = 0.0; - List allUsableStockList = stRealtimeStockMapper.selectMaxUsableStockByMoldAttr(goodsCode); + // 存放最终要生成的出库明细、占用、锁库数据 + List needOccupyList = new ArrayList<>(); + // 成功齐套的套数 + int successSet = 0; + + // 2、逐套循环校验,能齐一套算一套 + for (; successSet < totalNeedSetInt; ) { + boolean oneSetAllOk = true; + // 本套需要占用的临时数据 + List tempOccupy = new ArrayList<>(); + + for (SubItemStockDTO sub : sourceSubStockList) { + double needPerSet = sub.getSingleUseQty(); + // 当前子件剩余可用不足单套用量 → 本套不齐套,跳出 + if (sub.getRemainUsable() < needPerSet - 0.001) { + oneSetAllOk = false; + break; + } - if (allUsableStockList == null || allUsableStockList.isEmpty()) { - log.info("石墨模【{}】库存不足,无法生成!需求数量:{}", goodsCode, requireQty); - return Collections.emptyList(); - } - for (StRealtimeStock stock : allUsableStockList) { - double occupy = Optional.ofNullable(stock.getOccupyQuantity()).orElse(0D); - double usableQty = stock.getQuantity() - occupy; - if (usableQty <= 0) { - continue; + // 从子件库存里扣对应数量,优先耗前面批次库存 + double surplusNeed = needPerSet; + List stockList = sub.getStockList(); + for (StRealtimeStock stock : stockList) { + if (surplusNeed <= 0.001) break; + double occ = Optional.ofNullable(stock.getOccupyQuantity()).orElse(0D); + double usable = stock.getQuantity() - occ; + if (usable <= 0) continue; + + double takeQty = Math.min(usable, surplusNeed); + // 暂存本次占用信息,齐套成功才真正落库 + StockOccupyTempDTO temp = new StockOccupyTempDTO(); + temp.setStock(stock); + temp.setTakeQty(takeQty); + temp.setSubGoodsCode(sub.getSubGoodsCode()); + tempOccupy.add(temp); + + surplusNeed -= takeQty; + } + // 子件总可用扣减本套耗用 + sub.setRemainUsable(sub.getRemainUsable() - needPerSet); } - candidateStockList.add(stock); - totalUsable += usableQty; - - if (totalUsable >= requireQty) { + if (oneSetAllOk) { + // 本套齐套,临时占用转正,加入最终列表 + needOccupyList.addAll(tempOccupy); + successSet++; + } else { + // 任意子件不齐套,终止整套循环,不再尝试后续套数 break; } } -// if (totalUsable < requireQty - 0.001) { -// throw new ServiceException("石墨模【" + goodsCode + "】库存不足,无法生成!需求数量:" + requireQty + ",可用库存:" + totalUsable); -// } - if (candidateStockList.isEmpty()) { - log.info("石墨模【{}】库存不足,无法生成!需求数量:{}", goodsCode, requireQty); + if (CollUtil.isEmpty(needOccupyList)) { + log.info("父件{}无任何可齐套库存,需求{}套,生成0套预出库", partOne.getPartCode(), totalNeedSet); return Collections.emptyList(); } - double remainingQty = requireQty; + List preOutStockList = new ArrayList<>(); + String datePrefix = DateUtil.format(new Date(), "yyyyMMdd"); // 2. 当天最大序号 String maxCode = stStockInoutRecordMapper.getMaxCheckCode(datePrefix); @@ -117,23 +193,29 @@ public class StGraphiteMoldOutServiceImpl extends BaseServiceImpl s.getSubGoodsCode().equals(temp.getSubGoodsCode())) + .findFirst().get(); + preOutStock.setNeedQuantity(currSub.getSingleUseQty() * totalNeedSet); + preOutStock.setCompleteQuantity(takeQty); preOutStock.setGoodsId(maxStock.getGoodsId()); StGoods stGoods = stGoodsService.getById(maxStock.getGoodsId()); preOutStock.setGoodsName(stGoods.getGoodsName()); @@ -152,22 +234,22 @@ public class StGraphiteMoldOutServiceImpl extends BaseServiceImpl realtimeStock.getQuantity()) { + if (param.getOutedQuantity() > realtimeStock.getQuantity() - realtimeStock.getOccupyQuantity()) { throw new ServiceException("流程卡号【"+outEntity.getCardNo()+"】出库数量不能大于库存总数量,库存数量为:" + realtimeStock.getQuantity()); } @@ -320,7 +402,7 @@ public class StGraphiteMoldOutServiceImpl extends BaseServiceImpl Integer.parseInt(p.substring(9))) .orElse(0); - + // 当天最大序号 + String maxCode = stStockInoutRecordMapper.getMaxCheckCode(datePrefix); + int lastNum = 0; + if (StrUtil.isNotBlank(maxCode)) { + String number = maxCode.substring(datePrefix.length()); + lastNum = Integer.parseInt(number); + } // 主账单字段 = 明细合计 for (StStockInoutRecord detail : inoutList) { // 校验单条明细的必传字段 @@ -127,7 +136,11 @@ public class StOtherReceiptRecordServiceImpl extends BaseServiceImpl= 0) { + if (StrUtil.isNotBlank(lockNo)) { + if (newOccupy < 0) { + newOccupy = 0; + } db.setOccupyQuantity(newOccupy); this.updateRealTimeLock(lockNo, db.getId(), quantity); // NumberUtil.add(