diff --git a/backend/src/main/java/com/kronecker/backend/controller/StockController.java b/backend/src/main/java/com/kronecker/backend/controller/StockController.java index 18adb16..c55be2d 100644 --- a/backend/src/main/java/com/kronecker/backend/controller/StockController.java +++ b/backend/src/main/java/com/kronecker/backend/controller/StockController.java @@ -29,6 +29,7 @@ public class StockController { @RequestParam(required = false) String medicineName, @RequestParam(required = false) String batchNo) { var wrapper = new LambdaQueryWrapper() + .gt(Stock::getQuantity, 0) .like(StringUtils.hasText(batchNo), Stock::getBatchNo, batchNo) .orderByAsc(Stock::getExpiryDate); // 按药品名搜索需先查 medicine 表 @@ -48,6 +49,7 @@ public class StockController { public Result> warning(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer size) { var wrapper = new LambdaQueryWrapper() + .gt(Stock::getQuantity, 0) .gt(Stock::getExpiryDate, java.time.LocalDate.now()) .orderByAsc(Stock::getExpiryDate); Page result = stockService.page(new Page<>(page, size), wrapper); diff --git a/backend/src/main/java/com/kronecker/backend/controller/StockInController.java b/backend/src/main/java/com/kronecker/backend/controller/StockInController.java index 04a4347..5db3651 100644 --- a/backend/src/main/java/com/kronecker/backend/controller/StockInController.java +++ b/backend/src/main/java/com/kronecker/backend/controller/StockInController.java @@ -7,8 +7,10 @@ import com.kronecker.backend.common.Result; import com.kronecker.backend.dto.StockInCreateDTO; import com.kronecker.backend.entity.*; import com.kronecker.backend.service.StockInService; +import com.baomidou.mybatisplus.extension.service.IService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import java.util.stream.Collectors; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @@ -24,6 +26,7 @@ public class StockInController { private final IService recordService; private final IService detailService; private final IService supplierService; + private final IService medicineService; private final StockInService stockInService; @GetMapping @@ -39,8 +42,15 @@ public class StockInController { public Result getById(@PathVariable Long id) { StockInRecord record = recordService.getById(id); if (record != null) { - record.setDetails(detailService.list( - new LambdaQueryWrapper().eq(StockInDetail::getRecordId, id))); + var details = detailService.list( + new LambdaQueryWrapper().eq(StockInDetail::getRecordId, id)); + if (!details.isEmpty()) { + var medIds = details.stream().map(StockInDetail::getMedicineId).collect(Collectors.toSet()); + var medMap = medicineService.listByIds(medIds).stream() + .collect(Collectors.toMap(Medicine::getId, Medicine::getName)); + details.forEach(d -> d.setMedicineName(medMap.getOrDefault(d.getMedicineId(), ""))); + } + record.setDetails(details); var supp = supplierService.getById(record.getSupplierId()); if (supp != null) record.setSupplierName(supp.getName()); } diff --git a/backend/src/main/java/com/kronecker/backend/controller/StockOutController.java b/backend/src/main/java/com/kronecker/backend/controller/StockOutController.java index 7d92edb..ceea84e 100644 --- a/backend/src/main/java/com/kronecker/backend/controller/StockOutController.java +++ b/backend/src/main/java/com/kronecker/backend/controller/StockOutController.java @@ -7,8 +7,10 @@ import com.kronecker.backend.common.Result; import com.kronecker.backend.dto.StockOutCreateDTO; import com.kronecker.backend.entity.*; import com.kronecker.backend.service.StockOutService; +import com.baomidou.mybatisplus.extension.service.IService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import java.util.stream.Collectors; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @@ -24,6 +26,7 @@ public class StockOutController { private final IService recordService; private final IService detailService; private final IService customerService; + private final IService medicineService; private final StockOutService stockOutService; @GetMapping @@ -39,8 +42,16 @@ public class StockOutController { public Result getById(@PathVariable Long id) { StockOutRecord record = recordService.getById(id); if (record != null) { - record.setDetails(detailService.list( - new LambdaQueryWrapper().eq(StockOutDetail::getRecordId, id))); + var details = detailService.list( + new LambdaQueryWrapper().eq(StockOutDetail::getRecordId, id)); + // 填充药品名 + if (!details.isEmpty()) { + var medIds = details.stream().map(StockOutDetail::getMedicineId).collect(Collectors.toSet()); + var medMap = medicineService.listByIds(medIds).stream() + .collect(Collectors.toMap(Medicine::getId, Medicine::getName)); + details.forEach(d -> d.setMedicineName(medMap.getOrDefault(d.getMedicineId(), ""))); + } + record.setDetails(details); if (record.getCustomerId() != null) { var cust = customerService.getById(record.getCustomerId()); if (cust != null) record.setCustomerName(cust.getName()); diff --git a/backend/src/main/java/com/kronecker/backend/service/impl/DashboardServiceImpl.java b/backend/src/main/java/com/kronecker/backend/service/impl/DashboardServiceImpl.java index 4ccf57a..7ab61f1 100644 --- a/backend/src/main/java/com/kronecker/backend/service/impl/DashboardServiceImpl.java +++ b/backend/src/main/java/com/kronecker/backend/service/impl/DashboardServiceImpl.java @@ -38,6 +38,7 @@ public class DashboardServiceImpl implements DashboardService { public Page getExpiryWarning(Page page) { Page result = stockMapper.selectPage(page, new LambdaQueryWrapper() + .gt(Stock::getQuantity, 0) .le(Stock::getExpiryDate, LocalDate.now().plusDays(90)) .orderByAsc(Stock::getExpiryDate)); populateStockNames(result.getRecords()); diff --git a/backend/src/main/java/com/kronecker/backend/service/impl/StockOutServiceImpl.java b/backend/src/main/java/com/kronecker/backend/service/impl/StockOutServiceImpl.java index 7399b41..b94591d 100644 --- a/backend/src/main/java/com/kronecker/backend/service/impl/StockOutServiceImpl.java +++ b/backend/src/main/java/com/kronecker/backend/service/impl/StockOutServiceImpl.java @@ -64,26 +64,23 @@ public class StockOutServiceImpl implements StockOutService { var details = detailMapper.selectList( new LambdaQueryWrapper().eq(StockOutDetail::getRecordId, id)); - // 检查库存是否充足 + // 检查库存是否充足 + 扣减库存(FIFO: 先过期先出) for (var d : details) { - int totalStock = stockMapper.selectList(new LambdaQueryWrapper() - .eq(Stock::getMedicineId, d.getMedicineId()) - .eq(Stock::getWarehouseId, record.getWarehouseId()) - .eq(Stock::getBatchNo, d.getBatchNo())) - .stream().mapToInt(Stock::getQuantity).sum(); - if (totalStock < d.getQuantity()) { - throw new BusinessException("药品批次 " + d.getBatchNo() + " 库存不足"); - } - } - - // 扣减库存(FIFO: 先过期先出) - for (var d : details) { - int remaining = d.getQuantity(); - var stocks = stockMapper.selectList(new LambdaQueryWrapper() + var qw = new LambdaQueryWrapper() .eq(Stock::getMedicineId, d.getMedicineId()) .eq(Stock::getWarehouseId, record.getWarehouseId()) - .eq(Stock::getBatchNo, d.getBatchNo()) - .orderByAsc(Stock::getExpiryDate)); + .gt(Stock::getQuantity, 0); + if (d.getBatchNo() != null && !d.getBatchNo().isBlank()) { + qw.eq(Stock::getBatchNo, d.getBatchNo()); + } + qw.orderByAsc(Stock::getExpiryDate); + var stocks = stockMapper.selectList(qw); + + int remaining = d.getQuantity(); + int available = stocks.stream().mapToInt(Stock::getQuantity).sum(); + if (available < remaining) { + throw new BusinessException("药品库存不足,需要" + remaining + ",可用" + available); + } for (Stock s : stocks) { if (remaining <= 0) break; int deduct = Math.min(s.getQuantity(), remaining); diff --git a/frontend/src/views/stockIn/StockInForm.vue b/frontend/src/views/stockIn/StockInForm.vue index d882036..696d639 100644 --- a/frontend/src/views/stockIn/StockInForm.vue +++ b/frontend/src/views/stockIn/StockInForm.vue @@ -32,7 +32,8 @@ async function handleSubmit() { if (!form.supplierId) { ElMessage.warning('请选择供应商'); return } if (!details.value.length) { ElMessage.warning('请添加药品明细'); return } updateTotal() - await createStockIn({ ...form, details: details.value, totalAmount: totalAmount.value }) + const submitDetails = details.value.map(d => ({ ...d, totalPrice: d.costPrice * d.quantity })) + await createStockIn({ ...form, details: submitDetails, totalAmount: totalAmount.value }) ElMessage.success('入库单已创建,等待审核') router.push('/stock-in/list') } diff --git a/frontend/src/views/stockOut/StockOutForm.vue b/frontend/src/views/stockOut/StockOutForm.vue index b6e1669..3016b59 100644 --- a/frontend/src/views/stockOut/StockOutForm.vue +++ b/frontend/src/views/stockOut/StockOutForm.vue @@ -42,7 +42,9 @@ async function handleSubmit() { if (!form.customerId) { ElMessage.warning('请选择客户'); return } if (!details.value.length) { ElMessage.warning('请添加药品明细'); return } updateTotal() - await createStockOut({ ...form, details: details.value, totalAmount: totalAmount.value }) + // 补齐每条明细的 totalPrice + const submitDetails = details.value.map(d => ({ ...d, totalPrice: detailTotal(d) })) + await createStockOut({ ...form, details: submitDetails, totalAmount: totalAmount.value }) ElMessage.success('出库单已创建,等待审核') router.push('/stock-out/list') }