diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index 192bf778..8c427ce6 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -1201,6 +1201,47 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID chargeItem.setProductTable(adviceSaveDto.getAdviceTableName());// 产品所在表 chargeItem.setProductId(adviceSaveDto.getAdviceDefinitionId());// 收费项id + + // 🔧 Bug Fix: 如果 definitionId 或 definitionDetailId 为 null,从定价信息中获取 + if (chargeItem.getDefinitionId() == null || chargeItem.getDefDetailId() == null) { + log.warn("耗材的 definitionId 或 definitionDetailId 为 null,尝试从定价信息中获取: deviceDefId={}", + adviceSaveDto.getAdviceDefinitionId()); + // 查询耗材定价信息 + IPage devicePage = doctorStationAdviceAppMapper.getAdviceBaseInfo( + new Page<>(1, 1), + PublicationStatus.ACTIVE.getValue(), + orgId, + CommonConstants.TableName.ADM_DEVICE_DEFINITION, + null, + null, + null, + Arrays.asList(adviceSaveDto.getAdviceDefinitionId()), + null, + null, + null, + null); + if (devicePage != null && !devicePage.getRecords().isEmpty()) { + AdviceBaseDto deviceBaseInfo = devicePage.getRecords().get(0); + if (deviceBaseInfo.getPriceList() != null && !deviceBaseInfo.getPriceList().isEmpty()) { + AdvicePriceDto devicePrice = deviceBaseInfo.getPriceList().get(0); + if (chargeItem.getDefinitionId() == null) { + chargeItem.setDefinitionId(devicePrice.getDefinitionId()); + log.info("从定价信息中获取 definitionId: {}", devicePrice.getDefinitionId()); + } + if (chargeItem.getDefDetailId() == null) { + chargeItem.setDefDetailId(devicePrice.getDefinitionDetailId()); + log.info("从定价信息中获取 definitionDetailId: {}", devicePrice.getDefinitionDetailId()); + } + } + } + } + + // 🔧 Bug Fix: 确保定义ID不为null + if (chargeItem.getDefinitionId() == null) { + log.error("无法获取耗材的 definitionId: deviceDefId={}", adviceSaveDto.getAdviceDefinitionId()); + throw new ServiceException("无法获取耗材的定价信息,请联系管理员"); + } + // 🔧 Bug Fix: 如果accountId为null,从就诊中获取账户ID,如果没有则自动创建 Long accountId = adviceSaveDto.getAccountId(); if (accountId == null) { diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/utils/AdviceUtils.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/utils/AdviceUtils.java index 3278124b..5e4e9c28 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/utils/AdviceUtils.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/utils/AdviceUtils.java @@ -115,6 +115,15 @@ public class AdviceUtils { matched = true; // 检查库存是否充足 BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity(); + // 🔧 Bug Fix: 对于耗材类型,如果没有设置minUnitQuantity,则使用quantity作为默认值 + if (minUnitQuantity == null) { + if (CommonConstants.TableName.ADM_DEVICE_DEFINITION.equals(inventoryDto.getItemTable())) { + // 耗材只有一个单位,minUnitQuantity等于quantity + minUnitQuantity = saveDto.getQuantity(); + } else { + return saveDto.getAdviceName() + "的小单位数量不能为空"; + } + } BigDecimal chineseHerbsDoseQuantity = saveDto.getChineseHerbsDoseQuantity(); // 中药付数 // 中草药医嘱的情况 if (chineseHerbsDoseQuantity != null && chineseHerbsDoseQuantity.compareTo(BigDecimal.ZERO) > 0) { diff --git a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue index e8516337..7a994694 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue @@ -273,7 +273,10 @@ function fetchFromApi(searchKey) { minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '', volume: item.size || item.totalVolume || '', partPercent: item.partPercent || 1, - inventoryList: [], + // 🔧 Bug Fix: 如果后端提供了inventoryList,则使用;否则为空数组 + inventoryList: item.inventoryList || [], + // 🔧 Bug Fix: 构造stockList用于库存显示 + stockList: item.inventoryList || [], adviceDefinitionId: item.id, chargeItemDefinitionId: item.id, positionId: item.locationId, @@ -354,6 +357,11 @@ function handleQuantity(row) { const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0); return totalQuantity.toString() + row.minUnitCode_dictText; } + // 🔧 Bug Fix: 耗材类型可能没有inventoryList,但可能有stockList + if (row.stockList && row.stockList.length > 0) { + const totalQuantity = row.stockList.reduce((sum, item) => sum + (item.quantity || 0), 0); + return totalQuantity.toString() + row.minUnitCode_dictText; + } return 0; } diff --git a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue index 9f51baf6..d4e26d0f 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue @@ -546,6 +546,11 @@ expandOrder = []; // 当医嘱类型改变时,清空当前选择的项目名称,因为不同类型项目的数据结构可能不兼容 prescriptionList[scope.$index].adviceName = undefined; + prescriptionList[scope.$index].adviceType_dictText = ''; + // 🔧 Bug Fix: 医嘱类型改变时,重置minUnitQuantity和minUnitCode,避免null值 + prescriptionList[scope.$index].minUnitQuantity = prescriptionList[scope.$index].quantity || 1; + prescriptionList[scope.$index].minUnitCode = prescriptionList[scope.$index].unitCode; + prescriptionList[scope.$index].minUnitCode_dictText = prescriptionList[scope.$index].unitCode_dictText; adviceQueryParams.adviceTypes = value; // 🎯 修复:改为 adviceTypes(复数) // 根据选择的类型设置categoryCode,用于药品分类筛选 @@ -956,25 +961,38 @@ const { method_code, unit_code, rate_code, distribution_category_code, drord_doc // drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=手术 const adviceTypeList = computed(() => { // 如果字典已加载,使用字典数据;否则使用默认值 + let list = []; if (drord_doctor_type.value && drord_doctor_type.value.length > 0) { - return drord_doctor_type.value.map(item => ({ - label: item.label, - value: parseInt(item.value) || item.value - })); + // 过滤掉字典中已有的"全部"选项,避免重复 + list = drord_doctor_type.value + .filter(item => item.label !== '全部') + .map(item => ({ + label: item.label, + value: parseInt(item.value) || item.value + })); + } else { + // 默认返回值,确保页面正常显示 + list = [ + { label: '西药', value: 1 }, + { label: '中成药', value: 2 }, + { label: '诊疗', value: 3 }, + { label: '耗材', value: 4 }, + { label: '会诊', value: 5 }, + { label: '手术', value: 6 }, + ]; } - // 默认返回值,确保页面正常显示 - return [ - { label: '西药', value: 1 }, - { label: '中成药', value: 2 }, - { label: '诊疗', value: 3 }, - { label: '耗材', value: 4 }, - { label: '会诊', value: 5 }, - { label: '手术', value: 6 }, - ]; + // 在最后添加"全部"选项 + list.push({ label: '全部', value: 0 }); + return list; }); // 根据类型值获取显示标签,避免非编辑态出现空标签 -const mapAdviceTypeLabel = (type) => { +const mapAdviceTypeLabel = (type, adviceTableName) => { + // 🔧 Bug Fix: 根据adviceTableName判断耗材类型 + // 后端adviceType=2既表示中成药又表示耗材,需要通过表名区分 + if (type === 2 && adviceTableName === 'adm_device_definition') { + return '耗材'; + } const found = adviceTypeList.value.find((item) => item.value === type); return found ? found.label : ''; }; @@ -1602,11 +1620,8 @@ function getListInfo(addNewRow) { // 🔧 Bug Fix: 后端保存时将耗材(4)转换为中成药(2),显示时需要转换回来 // 检查 adviceTableName,如果是耗材表则应该是耗材类型 const adviceTableName = contentJson?.adviceTableName || item.adviceTableName; - if (adviceType === 2 && adviceTableName === 'adm_device_definition') { - adviceType = 4; // 后端2(中成药) -> 前端4(耗材) - } - let adviceType_dictText = item.adviceType_dictText || mapAdviceTypeLabel(adviceType); + let adviceType_dictText = item.adviceType_dictText || mapAdviceTypeLabel(adviceType, adviceTableName); // 如果是会诊类型,设置为会诊类型 if (isConsultation) { @@ -2343,6 +2358,13 @@ function handleSave(prescriptionId) { item.accountId = finalAccountId; } item.dbOpType = '1'; + + // 🔧 Bug Fix: 确保耗材的minUnitQuantity被正确设置 + if (item.adviceType == 4) { + item.minUnitQuantity = item.quantity; + item.minUnitCode = item.unitCode; + item.minUnitCode_dictText = item.unitCode_dictText; + } }); loading.value = true; @@ -2366,20 +2388,25 @@ function handleSave(prescriptionId) { finalUnitCode = item.minUnitCode; } item.minUnitQuantity = finalQuantity; + } else if (item.adviceType == 4) { + // 🔧 Bug Fix: 耗材类型只有一个单位,minUnitQuantity等于quantity + item.minUnitQuantity = item.quantity; + // 🔧 Bug Fix: 确保minUnitCode等于unitCode + item.minUnitCode = item.unitCode; + item.minUnitCode_dictText = item.unitCode_dictText; + finalUnitCode = item.unitCode; } else { item.minUnitQuantity = item.quantity; } // --- 【修改点3:Bug 1 修复 (类型转换)】 --- let saveAdviceType = item.adviceType; - if (item.adviceType == 4) { - saveAdviceType = 2; // 耗材:前端4 -> 后端2 - } else if (item.adviceType == 2) { - saveAdviceType = 1; // 中成药:前端2 -> 后端1 - } else if (item.adviceType == 5) { + // 🔧 Bug Fix: 保持原类型,不进行转换 + // 前端类型: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=手术 + // 后端类型: 1=药品, 2=耗材, 3=医疗活动, 6=手术 + // 后端会通过 ItemType.DEVICE.getValue() || adviceType == 4 来识别耗材 + if (item.adviceType == 5) { saveAdviceType = 3; // 会诊:前端5 -> 后端3(诊疗类) - } else if (item.adviceType == 6) { - saveAdviceType = 6; // 🔧 BugFix#318: 手术类型保持为6 } // 🔧 Bug Fix: Validate and fix NaN values before sending to backend @@ -2439,6 +2466,8 @@ function handleSave(prescriptionId) { quantity: finalQuantity, unitCode: finalUnitCode, totalPrice: item.totalPrice, // Ensure totalPrice is valid + // 🔧 Bug Fix: 确保 categoryEnum 被传递(耗材必填字段) + categoryEnum: item.categoryEnum || parsedContent.categoryEnum, // 🔧 Bug Fix: 确保库存匹配成功的关键字段 adviceTableName: adviceTableNameVal, locationId: locationIdVal, @@ -2446,6 +2475,13 @@ function handleSave(prescriptionId) { methodCode: item.methodCode || parsedContent.methodCode, // 🔧 确保 accountId 被传递(用于预结算) accountId: item.accountId || parsedContent.accountId, + // 🔧 Bug Fix: 确保minUnitQuantity被传递(耗材必填字段) + minUnitQuantity: item.minUnitQuantity, + minUnitCode: item.minUnitCode, + minUnitCode_dictText: item.minUnitCode_dictText, + // 🔧 Bug Fix: 确保 definitionId 和 definitionDetailId 被传递(费用项必填字段) + definitionId: item.definitionId || parsedContent.definitionId, + definitionDetailId: item.definitionDetailId || parsedContent.definitionDetailId, // 🔧 更新 contentJson 中的 adviceType,确保后端分类正确 contentJson: JSON.stringify({ ...parsedContent, @@ -2635,12 +2671,17 @@ function handleOrderBindInfo(bindIdInfo, currentMethodCode) { encounterDiagnosisId: encounterDiagnosisId.value, // 🔧 确保 adviceType 和显示文本正确设置 adviceType: item.adviceType, - adviceType_dictText: mapAdviceTypeLabel(item.adviceType), + adviceType_dictText: mapAdviceTypeLabel(item.adviceType, item.adviceTableName), }; // 计算价格和总量 const unitInfo = unitCodeList.value.find((k) => k.value == item.unitCode); - if (unitInfo && unitInfo.type == 'minUnit') { + // 🔧 Bug Fix: 耗材类型只有一个单位,minUnitQuantity等于quantity + if (item.adviceType == 4) { + newRow.price = newRow.unitPrice; + newRow.totalPrice = (item.quantity * newRow.unitPrice).toFixed(6); + newRow.minUnitQuantity = item.quantity; + } else if (unitInfo && unitInfo.type == 'minUnit') { newRow.price = newRow.minUnitPrice; newRow.totalPrice = (item.quantity * newRow.minUnitPrice).toFixed(6); newRow.minUnitQuantity = item.quantity; @@ -2705,6 +2746,12 @@ function handleSaveSign(row, index, prescriptionId) { return; } + // 🔧 Bug Fix: 验证医嘱类型不能为"全部" + if (row.adviceType === 0) { + proxy.$modal.msgWarning('请选择医嘱类型'); + return; + } + // 重新查找索引,确保使用当前处方列表中的正确索引 const actualIndex = prescriptionList.value.findIndex(item => item.uniqueKey === row.uniqueKey); if (actualIndex === -1) { @@ -2774,14 +2821,18 @@ function handleSaveSign(row, index, prescriptionId) { row.encounterId = props.patientInfo.encounterId; row.accountId = accountId.value; // 确保非编辑态显示正确的医嘱类型标签 - row.adviceType_dictText = mapAdviceTypeLabel(row.adviceType); + row.adviceType_dictText = mapAdviceTypeLabel(row.adviceType, row.adviceTableName); // 更新本地显示的最小单位数量(仅用于前端逻辑,不影响显示单位) if (row.adviceType == 1 || row.adviceType == 2) { row.minUnitQuantity = row.minUnitCode == row.unitCode ? row.quantity : row.quantity * row.partPercent; } else { + // 🔧 Bug Fix: 耗材和其他类型,minUnitQuantity等于quantity row.minUnitQuantity = row.quantity; + // 🔧 Bug Fix: 确保minUnitCode等于unitCode(耗材只有一个单位) + row.minUnitCode = row.unitCode; + row.minUnitCode_dictText = row.unitCode_dictText; } row.conditionId = conditionId.value; @@ -2976,14 +3027,12 @@ function handleSaveBatch(prescriptionId) { // --- Bug 1 修复:类型转换 --- let saveAdviceType = item.adviceType; - if (item.adviceType == 4) { - saveAdviceType = 2; // 耗材前端4 -> 后端2 - } else if (item.adviceType == 2) { - saveAdviceType = 1; // 中成药前端2 -> 后端1 - } else if (item.adviceType == 5) { + // 🔧 Bug Fix: 保持原类型,不进行转换 + // 前端类型: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=手术 + // 后端类型: 1=药品, 2=耗材, 3=医疗活动, 6=手术 + // 后端会通过 ItemType.DEVICE.getValue() || adviceType == 4 来识别耗材 + if (item.adviceType == 5) { saveAdviceType = 3; // 会诊前端5 -> 后端3(诊疗类) - } else if (item.adviceType == 6) { - saveAdviceType = 6; // 🔧 BugFix#318: 手术类型保持为6 } // 🔧 BugFix#318: 过滤掉手术特有字段,只保留标准医嘱字段 @@ -2994,7 +3043,11 @@ function handleSaveBatch(prescriptionId) { 'encounterId', 'groupId', 'injectFlag', 'lotNumber', 'methodCode', 'partPercent', 'patientId', 'positionId', 'positionName', 'prescriptionNo', 'quantity', 'rateCode', 'requestId', 'skinTestFlag', 'sortNumber', 'statusEnum', 'totalPrice', - 'unitCode', 'unitPrice', 'volume', 'ybClassEnum' + 'unitCode', 'unitPrice', 'volume', 'ybClassEnum', + // 🔧 Bug Fix: 添加 definitionId 和 definitionDetailId 字段 + 'definitionId', 'definitionDetailId', + // 🔧 Bug Fix: 添加 categoryEnum 字段(耗材必填) + 'categoryEnum' ]; let filteredItem = {}; standardItemFields.forEach(field => { @@ -3193,17 +3246,26 @@ function syncGroupFields(row) { function setValue(row) { unitCodeList.value = []; unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' }); - unitCodeList.value.push({ - value: row.doseUnitCode, - label: row.doseUnitCode_dictText, - type: 'dose', - }); + + // 🔧 Bug Fix: 耗材类型只有一个单位,不需要dose和minUnit选项 + if (row.adviceType == 4) { + // 耗材只添加一个单位选项 + row.minUnitCode = row.unitCode; + row.minUnitCode_dictText = row.unitCode_dictText; + } else { + // 药品类型添加dose和minUnit选项 + unitCodeList.value.push({ + value: row.doseUnitCode, + label: row.doseUnitCode_dictText, + type: 'dose', + }); - unitCodeList.value.push({ - value: row.minUnitCode, - label: row.minUnitCode_dictText, - type: 'minUnit', - }); + unitCodeList.value.push({ + value: row.minUnitCode, + label: row.minUnitCode_dictText, + type: 'minUnit', + }); + } if (row.adviceType == 2 && row.minUnitCode != row.unitCode) { unitCodeList.value.push({ value: row.minUnitCode, @@ -3237,8 +3299,15 @@ function setValue(row) { prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value; prescriptionList.value[rowIndex.value].doseUnitCode = row.doseUnitCode; prescriptionList.value[rowIndex.value].minUnitCode = row.minUnitCode; - prescriptionList.value[rowIndex.value].unitCode = - row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode; + // 🔧 Bug Fix: 耗材类型只有一个单位,minUnitCode应该等于unitCode + if (Number(row.adviceType) == 4) { + prescriptionList.value[rowIndex.value].minUnitCode = row.unitCode; + prescriptionList.value[rowIndex.value].minUnitCode_dictText = row.unitCode_dictText; + prescriptionList.value[rowIndex.value].unitCode = row.unitCode; + } else { + prescriptionList.value[rowIndex.value].unitCode = + row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode; + } prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode; prescriptionList.value[rowIndex.value].skinTestFlag = row.skinTestFlag; prescriptionList.value[rowIndex.value].definitionId = row.chargeItemDefinitionId; @@ -3307,6 +3376,10 @@ function setValue(row) { // 🔧 Bug #218 修复:保留组套中的quantity,如果没有则默认1 prescriptionList.value[rowIndex.value].quantity = row.quantity || 1; prescriptionList.value[rowIndex.value].totalPrice = validPrice * (row.quantity || 1); + // 🔧 Bug Fix: 设置耗材的minUnitQuantity,避免保存时null错误 + prescriptionList.value[rowIndex.value].minUnitQuantity = row.quantity || 1; + // 🔧 Bug Fix: 设置耗材的categoryEnum,避免数据库约束错误 + prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode || 3; // 默认为3 prescriptionList.value[rowIndex.value].positionName = row.positionName || ''; // 🔧 Bug Fix: 使用 positionId,如果为空则使用患者信息中的 orgId console.log('设置耗材locationId:', { @@ -3325,6 +3398,10 @@ function setValue(row) { prescriptionList.value[rowIndex.value].minUnitPrice = 0; prescriptionList.value[rowIndex.value].quantity = row.quantity || 1; prescriptionList.value[rowIndex.value].totalPrice = 0; + // 🔧 Bug Fix: 设置耗材的minUnitQuantity,避免保存时null错误 + prescriptionList.value[rowIndex.value].minUnitQuantity = row.quantity || 1; + // 🔧 Bug Fix: 设置耗材的categoryEnum,避免数据库约束错误 + prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode || 3; // 默认为3 prescriptionList.value[rowIndex.value].positionName = row.positionName || ''; const finalLocationId = row.positionId || props.patientInfo.orgId; prescriptionList.value[rowIndex.value].locationId = finalLocationId; @@ -3967,6 +4044,16 @@ function convertDoseValues(row, index) { // 总量计算,仅适用只有两种单位的情况 function calculateTotalAmount(row, index) { nextTick(() => { + // 🔧 Bug Fix: 处理耗材类型的总金额计算 + if (row.adviceType == 4) { + row.totalPrice = (row.quantity * row.unitPrice).toFixed(6); + // 🔧 Bug Fix: 确保耗材的minUnitQuantity和minUnitCode正确设置 + row.minUnitQuantity = row.quantity; + row.minUnitCode = row.unitCode; + row.minUnitCode_dictText = row.unitCode_dictText; + return; + } + if (row.adviceType == 2) { calculateTotalPrice(row, index); return;