From 693ed79f7545b7b74884b027f8f4db24c43d4eb0 Mon Sep 17 00:00:00 2001 From: guanyu Date: Sun, 17 May 2026 21:31:20 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#517:=20[=E5=BA=93=E6=88=BF?= =?UTF-8?q?=E7=AE=A1=E7=90=86-=E9=A2=86=E7=94=A8=E7=AE=A1=E7=90=86]=20?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91=E6=A0=A1=E9=AA=8C=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=EF=BC=9A=E5=85=81=E8=AE=B8=E4=BF=9D=E5=AD=98=E5=B9=B6?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=A2=86=E7=94=A8=E6=95=B0=E9=87=8F=E5=A4=A7?= =?UTF-8?q?=E4=BA=8E=E5=BA=93=E5=AD=98=E6=95=B0=E9=87=8F=EF=BC=88=E9=9B=B6?= =?UTF-8?q?=E5=BA=93=E5=AD=98=E9=A2=86=E7=94=A8=EF=BC=89=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因分析: - 前端 handleSubmitApproval(提交审批)未做库存校验,直接调用后端 API - 后端 submitApproval 也未做库存校验,仅在保存时(addOrEditIssueReceipt)有 validateRequisitionStock - 用户可绕过前端保存校验(如编辑已有草稿后直接提交审批),将超库存单据提交审批流 修复方案: 1. 后端:在 submitApproval 方法中增加 validateRequisitionStockByBusNo,通过单据详情查询已保存明细,逐行校验领用数量是否超过源仓库库存 2. 前端:在 handleSubmitApproval 提交前逐行调用 validateRequisitionQtyVsStock 校验库存,超库存时拦截并提示 Co-Authored-By: Claude Opus 4.7 --- .../impl/RequisitionIssueAppServiceImpl.java | 79 +++++++++++++++++++ .../requisitionManagement/index.vue | 10 +++ 2 files changed, 89 insertions(+) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/RequisitionIssueAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/RequisitionIssueAppServiceImpl.java index ed0af2260..33888bab9 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/RequisitionIssueAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/RequisitionIssueAppServiceImpl.java @@ -256,6 +256,9 @@ public class RequisitionIssueAppServiceImpl implements IRequisitionIssueAppServi */ @Override public R submitApproval(String busNo) { + // 提交审批前校验库存,防止超库存单据进入审批流 + this.validateRequisitionStockByBusNo(busNo); + // 单据提交审核 boolean result = supplyRequestService.submitApproval(busNo); return result ? R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null)) @@ -419,4 +422,80 @@ public class RequisitionIssueAppServiceImpl implements IRequisitionIssueAppServi } } } + + /** + * 根据单据号校验领用数量是否超过源仓库实际库存(提交审批前调用) + * + * @param busNo 单据号 + */ + private void validateRequisitionStockByBusNo(String busNo) { + Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); + // 通过单据详情查询已保存的单据明细 + R> detailResult = this.getDetail(busNo); + if (detailResult.getCode() != 200 || detailResult.getData() == null) { + return; + } + List detailList = detailResult.getData(); + for (IssueDetailDto detail : detailList) { + Long itemId = detail.getItemId(); + String lotNumber = detail.getLotNumber(); + Long sourceLocationId = detail.getSourceLocationId(); + BigDecimal reqQuantity = detail.getItemQuantity(); + String itemTable = CommonConstants.TableName.MED_MEDICATION_DEFINITION; + // 根据药品类型判断表名 + if (ItemType.DEVICE.getValue().equals(detail.getItemType())) { + itemTable = CommonConstants.TableName.ADM_DEVICE_DEFINITION; + } + + // 查询定义信息(拆零比、单位) + BigDecimal partPercent = BigDecimal.ONE; + String unitCode = detail.getUnitCode(); + String minUnitCode = detail.getMinUnitCode(); + + if (CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(itemTable)) { + MedicationDefinition medDef = medicationDefinitionService.getById(itemId); + if (medDef != null) { + unitCode = medDef.getUnitCode(); + minUnitCode = medDef.getMinUnitCode(); + if (medDef.getPartPercent() != null) { + partPercent = medDef.getPartPercent(); + } + } + } else if (CommonConstants.TableName.ADM_DEVICE_DEFINITION.equals(itemTable)) { + DeviceDefinition devDef = deviceDefinitionService.getById(itemId); + if (devDef != null) { + unitCode = devDef.getUnitCode(); + minUnitCode = devDef.getMinUnitCode(); + if (devDef.getPartPercent() != null) { + partPercent = devDef.getPartPercent(); + } + } + } + + // 计算领用数量折合最小单位的值 + BigDecimal reqQuantityInMinUnit; + if (unitCode != null && detail.getUnitCode().equals(unitCode)) { + reqQuantityInMinUnit = reqQuantity.multiply(partPercent); + } else { + reqQuantityInMinUnit = reqQuantity; + } + + // 查询源仓库实际库存 + List inventoryItemList = inventoryItemService.selectInventoryByItemId( + itemId, lotNumber, sourceLocationId, tenantId); + + // 累加总库存 + BigDecimal totalStock = BigDecimal.ZERO; + for (InventoryItem inventoryItem : inventoryItemList) { + if (inventoryItem.getLocationId().equals(sourceLocationId)) { + totalStock = totalStock.add(inventoryItem.getQuantity()); + } + } + + // 比较领用数量与库存 + if (reqQuantityInMinUnit.compareTo(totalStock) > 0) { + throw new ServiceException("提交失败,单据中存在领用数量超过库存的明细,请核对后重新保存"); + } + } + } } diff --git a/openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue b/openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue index 1d56b6da6..02c6df268 100755 --- a/openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue +++ b/openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue @@ -982,6 +982,16 @@ function handleSubmitApproval() { } else if (!form.purchaseinventoryList[length - 1].isSave) { proxy.$modal.msgWarning('第' + length + '行单据未保存,请先保存'); } else { + // 提交审批前逐行校验库存,防止超库存单据进入审批 + for (let i = 0; i < form.purchaseinventoryList.length; i++) { + const line = form.purchaseinventoryList[i]; + if (!line) continue; + const err = validateRequisitionQtyVsStock(line, i + 1); + if (err) { + proxy.$modal.msgWarning(err); + return; + } + } submitApproval(receiptHeaderForm.busNo).then((response) => { if (response.code == 200) { proxy.$modal.msgSuccess('提交审批成功');