| | |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.net.URLEncoder; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | |
| | | private ProcurementRecordService procurementRecordService; |
| | | private IApproveProcessService approveProcessService; |
| | | |
| | | private StockInventoryService stockInventoryService; |
| | | private SalesLedgerMapper salesLedgerMapper; |
| | | |
| | | @Override |
| | | public int add(QualityInspectDto qualityInspectDto) { |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | | BeanUtils.copyProperties(qualityInspectDto, qualityInspect); |
| | | qualityInspect.setInspectState(0);//默认未提交 |
| | | ensureQualifiedSplitDefaults(qualityInspect); |
| | | // 前端若已传合格/不合格数量,defaults 不会写 pass_rate,这里补算列表展示用合格率 |
| | | refreshPassRateFromQuantities(qualityInspect); |
| | | qualityInspectMapper.insert(qualityInspect); |
| | | for (QualityInspectParam qualityInspectParam : qualityInspectDto.getQualityInspectParams()) { |
| | | qualityInspectParam.setInspectId(qualityInspect.getId()); |
| | |
| | | @Override |
| | | public int submit(QualityInspect inspect) { |
| | | QualityInspect qualityInspect = qualityInspectMapper.selectById(inspect.getId()); |
| | | //提交前必须判断是否合格 |
| | | if (ObjectUtils.isNull(qualityInspect.getCheckResult())) { |
| | | throw new RuntimeException("请先判断是否合格"); |
| | | if (qualityInspect == null) { |
| | | throw new RuntimeException("质检单不存在"); |
| | | } |
| | | /*判断不合格*/ |
| | | if (qualityInspect.getCheckResult().equals("不合格")) { |
| | | if (Objects.equals(qualityInspect.getInspectState(), 1)) { |
| | | throw new RuntimeException("该质检单已提交,不能重复提交"); |
| | | } |
| | | if (inspect != null) { |
| | | if (inspect.getQualifiedQuantity() != null) { |
| | | qualityInspect.setQualifiedQuantity(inspect.getQualifiedQuantity()); |
| | | } |
| | | if (inspect.getUnqualifiedQuantity() != null) { |
| | | qualityInspect.setUnqualifiedQuantity(inspect.getUnqualifiedQuantity()); |
| | | } |
| | | } |
| | | validateAndCalculateQuantities(qualityInspect); |
| | | |
| | | BigDecimal qualifiedQty = qualityInspect.getQualifiedQuantity(); |
| | | BigDecimal unqualifiedQty = qualityInspect.getUnqualifiedQuantity(); |
| | | |
| | | if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | QualityUnqualified qualityUnqualified = new QualityUnqualified(); |
| | | BeanUtils.copyProperties(qualityInspect, qualityUnqualified); |
| | | qualityUnqualified.setInspectState(0);//待处理 |
| | | List<QualityInspectParam> inspectParams = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId())); |
| | | String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(",")); |
| | | qualityUnqualified.setDefectivePhenomena(text + "这些指标中存在不合格");//不合格现象 |
| | | qualityUnqualified.setId(null); |
| | | qualityUnqualified.setQuantity(unqualifiedQty); |
| | | qualityUnqualified.setInspectState(0); |
| | | qualityUnqualified.setDefectivePhenomena(buildDefectivePhenomena(qualityInspect)); |
| | | qualityUnqualified.setInspectId(qualityInspect.getId()); |
| | | qualityUnqualifiedMapper.insert(qualityUnqualified); |
| | | } else { |
| | | // 原材料检验合格后,先发起“入库审批”,审批通过后再入库 |
| | | } |
| | | |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | if (Objects.equals(qualityInspect.getInspectType(), 0)) { |
| | | submitQualifiedInboundApprove(qualityInspect); |
| | | } else { |
| | | // 直接入库 |
| | | stockUtils.addStock( |
| | | qualityInspect.getPurchaseLedgerId() == null ? null : qualityInspect.getPurchaseLedgerId().longValue(), |
| | | qualityInspect.getPurchaseLedgerId(), |
| | | null, |
| | | qualityInspect.getProductModelId(), |
| | | qualityInspect.getQuantity(), |
| | | qualifiedQty, |
| | | StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), |
| | | qualityInspect.getId() |
| | | ); |
| | | syncQualifiedInboundToPurchaseProducts(qualityInspect); |
| | | syncQualifiedInboundToPurchaseProducts(qualityInspect, qualifiedQty); |
| | | qualityInspect.setApprovalStatus(3); |
| | | } else if (Objects.equals(qualityInspect.getInspectType(), 2) && qualityInspect.getSalesLedgerId() != null) { |
| | | // 销售订单的成品出厂检验,提交后直接入库 |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(qualityInspect.getSalesLedgerProductId()); |
| | | stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.SALE_STOCK_IN.getCode()); |
| | | stockInventoryDto.setQualitity(qualifiedQty); |
| | | stockInventoryDto.setProductModelId(qualityInspect.getProductModelId()); |
| | | stockInventoryDto.setSalesLedgerId(qualityInspect.getSalesLedgerId()); |
| | | stockInventoryDto.setSalesLedgerProductId(qualityInspect.getSalesLedgerProductId()); |
| | | stockInventoryService.addstockInventory(stockInventoryDto); |
| | | |
| | | syncQualifiedInboundToSalesProducts(qualityInspect, qualifiedQty); |
| | | } else { |
| | | stockUtils.addStock( |
| | | qualityInspect.getPurchaseLedgerId() == null ? null : qualityInspect.getPurchaseLedgerId(), |
| | | null, |
| | | qualityInspect.getProductModelId(), |
| | | qualifiedQty, |
| | | StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), |
| | | qualityInspect.getId() |
| | | ); |
| | | syncQualifiedInboundToPurchaseProducts(qualityInspect, qualifiedQty); |
| | | } |
| | | } |
| | | qualityInspect.setInspectState(1);//已提交 |
| | | |
| | | qualityInspect.setCheckResult(resolveCheckResult(qualifiedQty, unqualifiedQty)); |
| | | qualityInspect.setInspectState(1); |
| | | int updated = qualityInspectMapper.updateById(qualityInspect); |
| | | refreshPurchaseLedgerStockStatusByInspect(qualityInspect.getPurchaseLedgerId()); |
| | | if (qualityInspect.getSalesLedgerId() != null) { |
| | | refreshSalesLedgerStockStatusByInspect(qualityInspect.getSalesLedgerId()); |
| | | } |
| | | return updated; |
| | | } |
| | | |
| | | private void validateAndCalculateQuantities(QualityInspect qualityInspect) { |
| | | if (qualityInspect.getQualifiedQuantity() == null || qualityInspect.getUnqualifiedQuantity() == null) { |
| | | throw new RuntimeException("请填写合格数量和不合格数量"); |
| | | } |
| | | if (qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) < 0 |
| | | || qualityInspect.getUnqualifiedQuantity().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new RuntimeException("合格数量和不合格数量不能为负数"); |
| | | } |
| | | if (qualityInspect.getQuantity() == null) { |
| | | throw new RuntimeException("质检单总数量异常"); |
| | | } |
| | | BigDecimal total = qualityInspect.getQualifiedQuantity().add(qualityInspect.getUnqualifiedQuantity()); |
| | | BigDecimal qtyScaled = qualityInspect.getQuantity().setScale(4, RoundingMode.HALF_UP); |
| | | BigDecimal sumScaled = total.setScale(4, RoundingMode.HALF_UP); |
| | | if (sumScaled.compareTo(qtyScaled) > 0) { |
| | | throw new RuntimeException("合格数量与不合格数量之和不能超过总数量"); |
| | | } |
| | | qualityInspect.setPassRate(calculatePassRate(qualityInspect.getQualifiedQuantity(), qualityInspect.getQuantity())); |
| | | } |
| | | |
| | | /** |
| | | * 新增时若未拆分合格/不合格,默认全部为待检合格数 |
| | | */ |
| | | private void ensureQualifiedSplitDefaults(QualityInspect q) { |
| | | if (q.getQuantity() == null) { |
| | | return; |
| | | } |
| | | if (q.getQualifiedQuantity() == null && q.getUnqualifiedQuantity() == null) { |
| | | q.setQualifiedQuantity(q.getQuantity()); |
| | | q.setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | q.setPassRate(calculatePassRate(q.getQualifiedQuantity(), q.getQuantity())); |
| | | if (q.getCheckResult() == null || q.getCheckResult().isEmpty()) { |
| | | q.setCheckResult("合格"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private BigDecimal calculatePassRate(BigDecimal qualifiedQty, BigDecimal totalQty) { |
| | | if (totalQty == null || totalQty.compareTo(BigDecimal.ZERO) <= 0 || qualifiedQty == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return qualifiedQty.multiply(BigDecimal.valueOf(100)) |
| | | .divide(totalQty, 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private void refreshPassRateFromQuantities(QualityInspect q) { |
| | | if (q.getQuantity() == null || q.getQualifiedQuantity() == null) { |
| | | return; |
| | | } |
| | | q.setPassRate(calculatePassRate(q.getQualifiedQuantity(), q.getQuantity())); |
| | | } |
| | | |
| | | private String resolveCheckResult(BigDecimal qualifiedQty, BigDecimal unqualifiedQty) { |
| | | if (unqualifiedQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return "合格"; |
| | | } |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return "不合格"; |
| | | } |
| | | return "部分合格"; |
| | | } |
| | | |
| | | private String buildDefectivePhenomena(QualityInspect qualityInspect) { |
| | | if (ObjectUtils.isNotEmpty(qualityInspect.getDefectivePhenomena())) { |
| | | return qualityInspect.getDefectivePhenomena(); |
| | | } |
| | | List<QualityInspectParam> inspectParams = qualityInspectParamService.list( |
| | | Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, qualityInspect.getId())); |
| | | if (inspectParams.isEmpty()) { |
| | | return "质检不合格数量:" + qualityInspect.getUnqualifiedQuantity(); |
| | | } |
| | | String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(",")); |
| | | return text + "等指标检验不合格,不合格数量:" + qualityInspect.getUnqualifiedQuantity(); |
| | | } |
| | | |
| | | private void submitQualifiedInboundApprove(QualityInspect qualityInspect) { |
| | |
| | | if (!Objects.equals(qualityInspect.getInspectType(), 0)) { |
| | | throw new RuntimeException("审批失败,仅原材料检验支持入库审批"); |
| | | } |
| | | if (!Objects.equals(qualityInspect.getInspectState(), 1) || !"合格".equals(qualityInspect.getCheckResult())) { |
| | | if (!Objects.equals(qualityInspect.getInspectState(), 1)) { |
| | | throw new RuntimeException("审批失败,当前质检单状态不允许入库"); |
| | | } |
| | | BigDecimal qualifiedQty = qualityInspect.getQualifiedQuantity(); |
| | | if (qualifiedQty == null || qualifiedQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new RuntimeException("审批失败,无合格数量可入库"); |
| | | } |
| | | stockUtils.addStock( |
| | | qualityInspect.getPurchaseLedgerId() == null ? null : qualityInspect.getPurchaseLedgerId().longValue(), |
| | | null, |
| | | qualityInspect.getProductModelId(), |
| | | qualityInspect.getQuantity(), |
| | | qualifiedQty, |
| | | StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), |
| | | qualityInspect.getId() |
| | | ); |
| | | syncQualifiedInboundToPurchaseProducts(qualityInspect); |
| | | syncQualifiedInboundToPurchaseProducts(qualityInspect, qualifiedQty); |
| | | refreshPurchaseLedgerStockStatusByInspect(qualityInspect.getPurchaseLedgerId()); |
| | | qualityInspect.setApprovalStatus(3); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | |
| | | |
| | | @Override |
| | | public int updateQualityInspect(QualityInspectDto qualityInspectDto) { |
| | | QualityInspect existing = qualityInspectMapper.selectById(qualityInspectDto.getId()); |
| | | if (existing == null) { |
| | | throw new RuntimeException("质检单不存在"); |
| | | } |
| | | if (Objects.equals(existing.getInspectState(), 1)) { |
| | | throw new RuntimeException("已提交的数据不允许修改"); |
| | | } |
| | | if (ObjectUtils.isNotNull(qualityInspectDto.getQualityInspectParams())) { |
| | | qualityInspectParamService.remove(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, qualityInspectDto.getId())); |
| | | for (QualityInspectParam qualityInspectParam : qualityInspectDto.getQualityInspectParams()) { |
| | | qualityInspectParam.setInspectId(qualityInspectDto.getId()); |
| | | qualityInspectParam.setId(null); |
| | | } |
| | | qualityInspectParamService.saveBatch(qualityInspectDto.getQualityInspectParams()); |
| | | } |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | | BeanUtils.copyProperties(qualityInspectDto, qualityInspect); |
| | | qualityInspect.setQuantity(existing.getQuantity()); |
| | | |
| | | BigDecimal qf = qualityInspect.getQualifiedQuantity() != null ? qualityInspect.getQualifiedQuantity() : existing.getQualifiedQuantity(); |
| | | BigDecimal uqf = qualityInspect.getUnqualifiedQuantity() != null ? qualityInspect.getUnqualifiedQuantity() : existing.getUnqualifiedQuantity(); |
| | | if (qf == null || uqf == null) { |
| | | BigDecimal qty = existing.getQuantity() != null ? existing.getQuantity() : BigDecimal.ZERO; |
| | | if ("不合格".equals(existing.getCheckResult())) { |
| | | qf = BigDecimal.ZERO; |
| | | uqf = qty; |
| | | } else { |
| | | qf = qty; |
| | | uqf = BigDecimal.ZERO; |
| | | } |
| | | } |
| | | qualityInspect.setQualifiedQuantity(qf); |
| | | qualityInspect.setUnqualifiedQuantity(uqf); |
| | | validateAndCalculateQuantities(qualityInspect); |
| | | qualityInspect.setCheckResult(resolveCheckResult(qf, uqf)); |
| | | return qualityInspectMapper.updateById(qualityInspect); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private void syncQualifiedInboundToPurchaseProducts(QualityInspect qualityInspect) { |
| | | private void syncQualifiedInboundToPurchaseProducts(QualityInspect qualityInspect, BigDecimal inboundQty) { |
| | | if (qualityInspect == null) { |
| | | return; |
| | | } |
| | | if (!Objects.equals(qualityInspect.getInspectType(), 0) || qualityInspect.getPurchaseLedgerId() == null) { |
| | | return; |
| | | } |
| | | if (qualityInspect.getProductModelId() == null || qualityInspect.getQuantity() == null) { |
| | | if (qualityInspect.getProductModelId() == null || inboundQty == null) { |
| | | return; |
| | | } |
| | | BigDecimal inboundQty = qualityInspect.getQuantity(); |
| | | if (inboundQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private void syncQualifiedInboundToSalesProducts(QualityInspect qualityInspect, BigDecimal inboundQty) { |
| | | if (qualityInspect == null || qualityInspect.getSalesLedgerProductId() == null || inboundQty == null) { |
| | | return; |
| | | } |
| | | if (inboundQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | |
| | | SalesLedgerProduct line = salesLedgerProductMapper.selectById(qualityInspect.getSalesLedgerProductId()); |
| | | if (line == null) { |
| | | return; |
| | | } |
| | | |
| | | BigDecimal orderQty = line.getQuantity() == null ? BigDecimal.ZERO : line.getQuantity(); |
| | | BigDecimal stocked = line.getStockedQuantity() == null ? BigDecimal.ZERO : line.getStockedQuantity(); |
| | | BigDecimal newStocked = stocked.add(inboundQty); |
| | | |
| | | int status; |
| | | if (newStocked.compareTo(BigDecimal.ZERO) <= 0) { |
| | | status = 0; |
| | | } else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) { |
| | | status = 1; |
| | | } else { |
| | | status = 2; |
| | | } |
| | | |
| | | line.setStockedQuantity(newStocked); |
| | | line.setProductStockStatus(status); |
| | | line.fillRemainingQuantity(); |
| | | salesLedgerProductMapper.updateById(line); |
| | | } |
| | | |
| | | private void refreshSalesLedgerStockStatusByInspect(Long salesLedgerId) { |
| | | if (salesLedgerId == null) { |
| | | return; |
| | | } |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)); |
| | | if (products == null || products.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | boolean hasStocked = false; |
| | | boolean allStocked = true; |
| | | for (SalesLedgerProduct product : products) { |
| | | BigDecimal orderQty = product.getQuantity() == null ? BigDecimal.ZERO : product.getQuantity(); |
| | | BigDecimal sq = product.getStockedQuantity() == null ? BigDecimal.ZERO : product.getStockedQuantity(); |
| | | if (sq.compareTo(BigDecimal.ZERO) > 0) { |
| | | hasStocked = true; |
| | | } |
| | | if (orderQty.compareTo(BigDecimal.ZERO) <= 0 || sq.compareTo(orderQty) < 0) { |
| | | allStocked = false; |
| | | } |
| | | } |
| | | |
| | | long pendingInspectCount = qualityInspectMapper.selectCount(Wrappers.<QualityInspect>lambdaQuery() |
| | | .eq(QualityInspect::getSalesLedgerId, salesLedgerId) |
| | | .eq(QualityInspect::getInspectState, 0)); |
| | | boolean allInspectFinished = pendingInspectCount == 0; |
| | | |
| | | SalesLedger ledger = salesLedgerMapper.selectById(salesLedgerId); |
| | | if (ledger != null) { |
| | | int newStatus = ledger.getStockStatus() != null ? ledger.getStockStatus() : 0; |
| | | if (allInspectFinished) { |
| | | newStatus = allStocked ? 2 : 5; // 2: 已入库(质检完成), 5: 部分质检完成 |
| | | } else { |
| | | newStatus = 4; // 4: 质检中 |
| | | } |
| | | ledger.setStockStatus(newStatus); |
| | | salesLedgerMapper.updateById(ledger); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |