| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.purchase.PurchaseInboundDto; |
| | | import com.ruoyi.account.bean.dto.purchase.PurchaseReturnDto; |
| | | import com.ruoyi.account.bean.dto.sales.SalesOutboundDto; |
| | | import com.ruoyi.account.bean.dto.sales.SalesReturnDto; |
| | | import com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo; |
| | | import com.ruoyi.account.bean.vo.purchase.PurchaseReturnVo; |
| | | import com.ruoyi.account.bean.vo.sales.SalesOutboundVo; |
| | | import com.ruoyi.account.bean.vo.sales.SalesReturnVo; |
| | | import com.ruoyi.account.mapper.AccountStatementMapper; |
| | | import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper; |
| | | import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper; |
| | | import com.ruoyi.account.pojo.AccountStatement; |
| | | import com.ruoyi.account.pojo.purchase.AccountPurchasePayment; |
| | | import com.ruoyi.account.pojo.sales.AccountSalesCollection; |
| | | import com.ruoyi.approve.mapper.ApproveProcessMapper; |
| | |
| | | import com.ruoyi.home.dto.*; |
| | | import com.ruoyi.home.mapper.HomeMapper; |
| | | import com.ruoyi.home.service.HomeService; |
| | | import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper; |
| | | import com.ruoyi.production.bean.dto.ProductionProductOutputDto; |
| | | import com.ruoyi.production.mapper.*; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; |
| | | import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.mapper.QualityUnqualifiedMapper; |
| | |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.staff.mapper.StaffOnJobMapper; |
| | | import com.ruoyi.staff.pojo.StaffOnJob; |
| | | import com.ruoyi.stock.mapper.StockInRecordMapper; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.mapper.StockOutRecordMapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | public class HomeServiceImpl implements HomeService { |
| | | |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | | private final StockOutRecordMapper stockOutRecordMapper; |
| | | private final ReturnManagementMapper returnManagementMapper; |
| | | private final StockInRecordMapper stockInRecordMapper; |
| | | private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper; |
| | | |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | |
| | |
| | | LocalDate startDate = range[0]; |
| | | LocalDate endDate = range[1]; |
| | | |
| | | //销售出库 |
| | | BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate); |
| | | //销售退货 |
| | | BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate); |
| | | //采购入库 |
| | | BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate); |
| | | //采购退货 |
| | | BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate); |
| | | //收款 |
| | | BigDecimal advanceMoney = sumCollectionAmount(startDate, endDate); |
| | | //付款 |
| | | BigDecimal prepayMoney = sumPaymentAmount(startDate, endDate); |
| | | |
| | | dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(advanceMoney)))); |
| | | dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(prepayMoney)))); |
| | | //应收金额=销售出库-销售退货 |
| | | dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(salesReturnAmount)))); |
| | | //应付金额=采购入库-采购退货 |
| | | dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(purchaseReturnAmount)))); |
| | | //收款金额=收款单 |
| | | dto.setAdvanceMoney(scaleMoney(advanceMoney)); |
| | | //付款金额=付款单 |
| | | dto.setPrepayMoney(scaleMoney(prepayMoney)); |
| | | return dto; |
| | | } |
| | |
| | | public List<Map<String, Object>> productInOutAnalysis(Integer type) { |
| | | String targetName; |
| | | if (type == 1) { |
| | | targetName = "原材料"; |
| | | targetName = "原料"; |
| | | } else if (type == 2) { |
| | | targetName = "成品"; |
| | | } else if (type == 3) { |
| | |
| | | YearMonth currentMonth = YearMonth.from(today); |
| | | LocalDate startDate = currentMonth.atDay(1); |
| | | LocalDate endDate = currentMonth.atEndOfMonth(); |
| | | |
| | | //收款 |
| | | BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate); |
| | | //销售出库 |
| | | BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate); |
| | | String collectionRate = toRateString(monthlyIncome, receivableBase); |
| | | |
| | | String currentMonthText = currentMonth.toString(); |
| | | List<AccountStatement> receivableStatements = defaultList(accountStatementMapper.selectList( |
| | | new LambdaQueryWrapper<AccountStatement>() |
| | | .eq(AccountStatement::getAccountType, 1) |
| | | .le(AccountStatement::getStatementMonth, currentMonthText))); |
| | | |
| | | long overdueNum = receivableStatements.stream() |
| | | .filter(item -> item.getStatementMonth() != null && item.getStatementMonth().compareTo(currentMonthText) < 0) |
| | | .filter(item -> defaultDecimal(item.getClosingBalance()).compareTo(BigDecimal.ZERO) > 0) |
| | | .count(); |
| | | |
| | | long totalReceivableCount = receivableStatements.size(); |
| | | String overdueRate = totalReceivableCount == 0 |
| | | ? "0.00" |
| | | : BigDecimal.valueOf(overdueNum) |
| | | .multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(totalReceivableCount), 2, RoundingMode.HALF_UP) |
| | | .toString(); |
| | | |
| | | //销售退货 |
| | | BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate); |
| | | //回款率=收款/(销售出库-销售退货)应收 |
| | | String collectionRate = toRateString(monthlyIncome, receivableBase.subtract(salesReturnAmount)); |
| | | //逾期数=(销售出库金额-销售退货金额)应收金额-收款金额 |
| | | BigDecimal overdueAmount = receivableBase.subtract(salesReturnAmount).subtract(monthlyIncome); |
| | | //逾期率=逾期数/应收金额 |
| | | String overdueRate = toRateString(overdueAmount, receivableBase); |
| | | dto.setMonthlyIncome(scaleMoney(monthlyIncome)); |
| | | dto.setCollectionRate(collectionRate); |
| | | dto.setOverdueNum(BigDecimal.valueOf(overdueNum)); |
| | | dto.setOverdueNum(overdueAmount); |
| | | dto.setOverdueRate(overdueRate); |
| | | return dto; |
| | | } |
| | |
| | | YearMonth currentMonth = YearMonth.from(today); |
| | | LocalDate startDate = currentMonth.atDay(1); |
| | | LocalDate endDate = currentMonth.atEndOfMonth(); |
| | | |
| | | //支出 |
| | | BigDecimal monthlyExpenditure = sumPaymentAmount(startDate, endDate); |
| | | //采购入库 |
| | | BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate); |
| | | //采购退货 |
| | | BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate); |
| | | //付款率=付款/(采购入库-采购退货)应付 |
| | | String paymentRate = toRateString(monthlyExpenditure, payableBase.subtract(purchaseReturnAmount)); |
| | | //收款 |
| | | BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate); |
| | | //毛利润= 收款-支出 |
| | | BigDecimal grossProfit = monthlyIncome.subtract(monthlyExpenditure); |
| | | //利润率=毛利润/收款 |
| | | String profitMarginRate = toRateString(grossProfit, monthlyIncome); |
| | | |
| | | dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure)); |
| | | dto.setPaymentRate(toRateString(monthlyExpenditure, payableBase)); |
| | | dto.setPaymentRate(paymentRate); |
| | | dto.setGrossProfit(scaleMoney(grossProfit)); |
| | | dto.setProfitMarginRate(toRateString(grossProfit, monthlyIncome)); |
| | | dto.setProfitMarginRate(profitMarginRate); |
| | | return dto; |
| | | } |
| | | |
| | |
| | | String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | |
| | | List<ProductionTaskStatisticsDto> startList = productionOperationTaskMapper.selectTaskStartStats(startStr, endStr); |
| | | |
| | | List<ProductionProductOutputDto> outputList = productionProductOutputMapper |
| | | .selectOutputStats(startStr, endStr); |
| | | List<ProductionProductOutputDto> outputList = productionProductOutputMapper.selectOutputStats(startStr, endStr); |
| | | |
| | | Map<String, WorkOrderEfficiencyDto> dateMap = new HashMap<>(); |
| | | |
| | | // 1. 处理开工数量(统一将时间转为 LocalDate 字符串,去除时分秒) |
| | | if (!CollectionUtils.isEmpty(startList)) { |
| | | for (ProductionTaskStatisticsDto item : startList) { |
| | | if (item.getActualStartTime() != null) { |
| | | String date = item.getActualStartTime().toString(); |
| | | WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto()); |
| | | dto.setDate(date); |
| | | // 💡 关键修改:如果是 LocalDateTime,必须先 toLocalDate() 再 toString() |
| | | String dateStr = item.getActualStartTime().toString(); |
| | | WorkOrderEfficiencyDto dto = dateMap.computeIfAbsent(dateStr, k -> { |
| | | WorkOrderEfficiencyDto newDto = new WorkOrderEfficiencyDto(); |
| | | newDto.setDate(k); |
| | | newDto.setStartQuantity(BigDecimal.ZERO); |
| | | newDto.setFinishQuantity(BigDecimal.ZERO); |
| | | newDto.setYieldRate("0.00"); |
| | | return newDto; |
| | | }); |
| | | |
| | | BigDecimal qty = item.getPlanQuantity() != null ? item.getPlanQuantity() : BigDecimal.ZERO; |
| | | dto.setStartQuantity(dto.getStartQuantity() != null ? dto.getStartQuantity().add(qty) : qty); |
| | | dateMap.put(date, dto); |
| | | dto.setStartQuantity(dto.getStartQuantity().add(qty)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 完工数量和良品率 |
| | | // 2. 处理完工数量和报废数量(仅通过 1 次循环搞定,大幅提升性能) |
| | | if (!CollectionUtils.isEmpty(outputList)) { |
| | | // 定义一个临时的报废数计数器,方便后面算良品率 |
| | | Map<String, BigDecimal> scrapMap = new HashMap<>(); |
| | | |
| | | for (ProductionProductOutputDto item : outputList) { |
| | | if (item.getCreateTime() != null) { |
| | | String date = item.getCreateTime().toLocalDate().toString(); |
| | | WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto()); |
| | | dto.setDate(date); |
| | | String dateStr = item.getCreateTime().toLocalDate().toString(); |
| | | |
| | | BigDecimal finishQty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO; |
| | | BigDecimal scrapQty = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO; |
| | | WorkOrderEfficiencyDto dto = dateMap.computeIfAbsent(dateStr, k -> { |
| | | WorkOrderEfficiencyDto newDto = new WorkOrderEfficiencyDto(); |
| | | newDto.setDate(k); |
| | | newDto.setStartQuantity(BigDecimal.ZERO); |
| | | newDto.setFinishQuantity(BigDecimal.ZERO); |
| | | newDto.setYieldRate("0.00"); |
| | | return newDto; |
| | | }); |
| | | |
| | | dto.setFinishQuantity( |
| | | dto.getFinishQuantity() != null ? dto.getFinishQuantity().add(finishQty) : finishQty); |
| | | BigDecimal qty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO; |
| | | BigDecimal scrap = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO; |
| | | |
| | | // 良品数 = 总投入数 - 报废数 (对应你原代码中 finishMap 的逻辑) |
| | | BigDecimal goodQty = qty.subtract(scrap); |
| | | dto.setFinishQuantity(dto.getFinishQuantity().add(goodQty)); |
| | | |
| | | // 累加报废数 |
| | | scrapMap.put(dateStr, scrapMap.getOrDefault(dateStr, BigDecimal.ZERO).add(scrap)); |
| | | } |
| | | } |
| | | |
| | | Map<String, BigDecimal> scrapMap = outputList.stream() |
| | | .filter(i -> i.getCreateTime() != null) |
| | | .collect(Collectors.groupingBy( |
| | | i -> i.getCreateTime().toLocalDate().toString(), |
| | | Collectors.reducing(BigDecimal.ZERO, |
| | | i -> i.getScrapQty() != null ? i.getScrapQty() : BigDecimal.ZERO, |
| | | BigDecimal::add))); |
| | | |
| | | Map<String, BigDecimal> finishMap = outputList.stream() |
| | | .filter(i -> i.getCreateTime() != null) |
| | | .collect(Collectors.groupingBy( |
| | | i -> i.getCreateTime().toLocalDate().toString(), |
| | | Collectors.reducing(BigDecimal.ZERO, i -> { |
| | | BigDecimal qty = (i.getQuantity() != null) ? i.getQuantity() : BigDecimal.ZERO; |
| | | BigDecimal scrap = (i.getScrapQty() != null) ? i.getScrapQty() : BigDecimal.ZERO; |
| | | return qty.subtract(scrap); |
| | | }, BigDecimal::add))); |
| | | |
| | | finishMap.forEach((date, qty) -> { |
| | | WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto()); |
| | | dto.setDate(date); |
| | | dto.setFinishQuantity(qty); |
| | | dateMap.put(date, dto); |
| | | }); |
| | | |
| | | dateMap.forEach((date, dto) -> { |
| | | BigDecimal finish = dto.getFinishQuantity() != null ? dto.getFinishQuantity() : BigDecimal.ZERO; |
| | | BigDecimal scrap = scrapMap.getOrDefault(date, BigDecimal.ZERO); |
| | | // 3. 计算良品率 |
| | | dateMap.forEach((dateStr, dto) -> { |
| | | BigDecimal finish = dto.getFinishQuantity(); |
| | | BigDecimal scrap = scrapMap.getOrDefault(dateStr, BigDecimal.ZERO); |
| | | BigDecimal total = finish.add(scrap); |
| | | |
| | | if (total.compareTo(BigDecimal.ZERO) > 0) { |
| | | BigDecimal rate = finish.divide(total, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100")) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | // 💡 延续你之前的逻辑:使用 RoundingMode.DOWN 截断,防止 99.999% 四舍五入变成 100.00 |
| | | BigDecimal rate = finish.divide(total, 4, RoundingMode.DOWN) |
| | | .multiply(new BigDecimal("100")) |
| | | .setScale(2, RoundingMode.DOWN); |
| | | dto.setYieldRate(rate.toString()); |
| | | } else { |
| | | dto.setYieldRate("0.00"); |
| | | } |
| | | |
| | | if (dto.getStartQuantity() == null) |
| | | dto.setStartQuantity(BigDecimal.ZERO); |
| | | if (dto.getFinishQuantity() == null) |
| | | dto.setFinishQuantity(BigDecimal.ZERO); |
| | | }); |
| | | } |
| | | |
| | | dateMap.values().forEach(dto -> { |
| | | if (dto.getStartQuantity() == null) |
| | | dto.setStartQuantity(BigDecimal.ZERO); |
| | | if (dto.getFinishQuantity() == null) |
| | | dto.setFinishQuantity(BigDecimal.ZERO); |
| | | if (dto.getYieldRate() == null) |
| | | dto.setYieldRate("0.00"); |
| | | }); |
| | | |
| | | // 4. 排序并输出 |
| | | return dateMap.values().stream() |
| | | .sorted(Comparator.comparing(WorkOrderEfficiencyDto::getDate)) |
| | | .collect(Collectors.toList()); |
| | |
| | | } |
| | | |
| | | switch (parent.getProductName()) { |
| | | case "原材料": |
| | | case "原料": |
| | | rawMaterialCount = rawMaterialCount.add(quantity); |
| | | break; |
| | | case "半成品": |
| | |
| | | return new LocalDate[]{startDate, endDate}; |
| | | } |
| | | |
| | | //计算日期内的销售出库金额 |
| | | private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) { |
| | | List<SalesLedger> salesLedgers = defaultList(salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>() |
| | | .ge(SalesLedger::getEntryDate, toDate(startDate)) |
| | | .lt(SalesLedger::getEntryDate, toExclusiveEndDate(endDate)))); |
| | | return sumAmount(salesLedgers, SalesLedger::getContractAmount); |
| | | SalesOutboundDto salesOutboundDto = new SalesOutboundDto(); |
| | | salesOutboundDto.setStartDate(startDate); |
| | | salesOutboundDto.setEndDate(endDate); |
| | | List<SalesOutboundVo> salesOutboundVos = stockOutRecordMapper.listPageAccountSales(new Page(1, -1), salesOutboundDto).getRecords(); |
| | | return sumAmount(salesOutboundVos, SalesOutboundVo::getOutboundAmount); |
| | | } |
| | | |
| | | //计算日期内的销售退货金额 |
| | | private BigDecimal sumSalesReturnAmount(LocalDate startDate, LocalDate endDate) { |
| | | SalesReturnDto salesReturnDto = new SalesReturnDto(); |
| | | salesReturnDto.setStartDate(startDate); |
| | | salesReturnDto.setEndDate(endDate); |
| | | List<SalesReturnVo> salesReturnVos = returnManagementMapper.listPageAccountSalesReturn(new Page(1, -1), salesReturnDto).getRecords(); |
| | | return sumAmount(salesReturnVos, SalesReturnVo::getRefundAmount); |
| | | } |
| | | |
| | | //计算日期内的采购入库金额 |
| | | private BigDecimal sumPurchaseContractAmount(LocalDate startDate, LocalDate endDate) { |
| | | List<PurchaseLedger> purchaseLedgers = defaultList(purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>() |
| | | .ge(PurchaseLedger::getEntryDate, toDate(startDate)) |
| | | .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(endDate)))); |
| | | return sumAmount(purchaseLedgers, PurchaseLedger::getContractAmount); |
| | | PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto(); |
| | | purchaseInboundDto.setStartDate(startDate); |
| | | purchaseInboundDto.setEndDate(endDate); |
| | | List<PurchaseInboundVo> purchaseInboundVos = stockInRecordMapper.listPageAccountPurchase(new Page(1, -1), purchaseInboundDto).getRecords(); |
| | | return sumAmount(purchaseInboundVos, PurchaseInboundVo::getInboundAmount); |
| | | } |
| | | |
| | | //计算日期内的采购退货金额 |
| | | private BigDecimal sumPurchaseReturnAmount(LocalDate startDate, LocalDate endDate) { |
| | | PurchaseReturnDto purchaseReturnDto = new PurchaseReturnDto(); |
| | | purchaseReturnDto.setStartDate(startDate); |
| | | purchaseReturnDto.setEndDate(endDate); |
| | | List<PurchaseReturnVo> purchaseReturnVos = purchaseReturnOrdersMapper.listPageAccountPurchaseReturn(new Page(1, -1), purchaseReturnDto).getRecords(); |
| | | return sumAmount(purchaseReturnVos, PurchaseReturnVo::getTotalAmount); |
| | | } |
| | | |
| | | //计算日期内的总收款金额 |
| | | private BigDecimal sumCollectionAmount(LocalDate startDate, LocalDate endDate) { |
| | | List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList( |
| | | new LambdaQueryWrapper<AccountSalesCollection>() |
| | |
| | | return sumAmount(collections, AccountSalesCollection::getCollectionAmount); |
| | | } |
| | | |
| | | //计算日期内的总付款金额 |
| | | private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) { |
| | | List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList( |
| | | new LambdaQueryWrapper<AccountPurchasePayment>() |