src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -20,6 +20,7 @@ import java.text.ParseException; import java.util.List; import java.util.Map; /** * @author :yys @@ -58,6 +59,22 @@ return AjaxResult.success(qualityStatisticsDto); } @ApiOperation("生产分析") @GetMapping("/productionStatistics") @Log(title = "生产分析", businessType = BusinessType.OTHER) public AjaxResult productionStatistics(QualityStatisticsDto req) { Map<String, List<String>> qualityStatisticsDto = homeService.productionStatistics(); return AjaxResult.success(qualityStatisticsDto); } @GetMapping("/inventoryStatistics") @Log(title = "仓储分析", businessType = BusinessType.OTHER) @ApiOperation("仓储分析") public AjaxResult inventoryStatistics(QualityStatisticsDto req) { QualityStatisticsDto qualityStatisticsDto = homeService.inventoryStatistics(); return AjaxResult.success(qualityStatisticsDto); } @GetMapping("/todos") @Log(title = "待办事项", businessType = BusinessType.OTHER) @ApiOperation("待办事项") src/main/java/com/ruoyi/home/dto/QualityStatisticsDto.java
@@ -15,10 +15,10 @@ @ApiModel public class QualityStatisticsDto { @ApiModelProperty(value = "原材料已检测数") @ApiModelProperty(value = "原材料已检测数,入库数量") private BigDecimal supplierNum; @ApiModelProperty(value = "出厂已检测数") @ApiModelProperty(value = "出厂已检测数,出库数量") private BigDecimal factoryNum; @ApiModelProperty(value = "过程已检测数") src/main/java/com/ruoyi/home/dto/QualityStatisticsItem.java
@@ -14,13 +14,13 @@ @ApiModel public class QualityStatisticsItem { @ApiModelProperty(value = "原材料不合格数") @ApiModelProperty(value = "原材料合格数,入库") private BigDecimal supplierNum; @ApiModelProperty(value = "出厂不合格数") @ApiModelProperty(value = "出厂合格数,出库") private BigDecimal factoryNum; @ApiModelProperty(value = "过程不合格数") @ApiModelProperty(value = "过程合格数") private BigDecimal processNum; private String date; src/main/java/com/ruoyi/home/service/HomeService.java
@@ -5,6 +5,7 @@ import java.text.ParseException; import java.util.List; import java.util.Map; /** * @author :yys @@ -25,4 +26,8 @@ StatisticsReceivablePayableDto statisticsReceivablePayable(Integer type); QualityProductQualifiedRateDto qualityProductQualifiedRate(); QualityStatisticsDto inventoryStatistics(); Map<String, List<String>> productionStatistics(); } src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -1,6 +1,9 @@ package com.ruoyi.home.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.account.pojo.AccountExpense; import com.ruoyi.account.pojo.AccountIncome; import com.ruoyi.approve.mapper.ApproveProcessMapper; import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.common.utils.SecurityUtils; @@ -14,6 +17,8 @@ import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper; import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut; import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; import com.ruoyi.production.mapper.SalesLedgerWorkMapper; import com.ruoyi.production.pojo.SalesLedgerWork; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.purchase.mapper.PaymentRegistrationMapper; @@ -84,6 +89,9 @@ @Autowired private SysDeptMapper sysDeptMapper; @Autowired private SalesLedgerWorkMapper salesLedgerWorkMapper;; @Override public HomeBusinessDto business() { @@ -495,4 +503,161 @@ return qualityProductQualifiedRateDto; } @Override public QualityStatisticsDto inventoryStatistics() { // 获取近四个月数据(往前推三个月,共4个完整月份) LocalDate today = LocalDate.now(); // 定义日期格式化器(用于显示“年月”格式) DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM"); QualityStatisticsDto qualityStatisticsDto = new QualityStatisticsDto(); List<QualityStatisticsItem> qualityStatisticsItems = new ArrayList<>(); BigDecimal supplierNum = new BigDecimal(0); //入库数量 BigDecimal factoryNum = new BigDecimal(0); //出库数量 // 循环4次,分别统计近4个月的数据(当前月、前1个月、前2个月、前3个月) for (int i = 3; i >= 0; i--) { // 计算当前循环对应的月份(i=0:当前月,i=1:前1个月,以此类推) LocalDate currentMonth = today.minusMonths(i); // 当月的开始日期(每月1号) LocalDate monthStart = currentMonth.withDayOfMonth(1); // 当月的结束日期(每月最后一天) LocalDate monthEnd = currentMonth.withDayOfMonth(currentMonth.lengthOfMonth()); // 入库数量 // 构建当月的查询条件(如果想一次性查全4个月数据再内存筛选,可优化为先查全再循环筛选) LambdaQueryWrapper<ProcurementRecordStorage> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.ge(ProcurementRecordStorage::getCreateTime, monthStart) .le(ProcurementRecordStorage::getCreateTime, monthEnd); // 筛选当月数据 List<ProcurementRecordStorage> monthInspects = procurementRecordStorageMapper.selectList(queryWrapper); // 出库数量 LambdaQueryWrapper<ProcurementRecordOut> queryWrapper1 = new LambdaQueryWrapper<>(); queryWrapper1.ge(ProcurementRecordOut::getCreateTime, monthStart) .le(ProcurementRecordOut::getCreateTime, monthEnd); List<ProcurementRecordOut> monthInspects1 = procurementRecordOutMapper.selectList(queryWrapper1); BigDecimal reduce = monthInspects.stream() .map(ProcurementRecordStorage::getInboundNum) .reduce(BigDecimal.ZERO, BigDecimal::add); supplierNum = supplierNum.add(reduce); BigDecimal reduce1 = monthInspects1.stream() .map(ProcurementRecordOut::getInboundNum) .reduce(BigDecimal.ZERO, BigDecimal::add); factoryNum= factoryNum.add(reduce1); // 构建当月统计项 QualityStatisticsItem item = new QualityStatisticsItem(); item.setDate(monthStart.format(monthFormatter)); // 日期显示为“年月”(如 2025-10) // 1. 供应商检验(类型0)- 合格数量 BigDecimal supplierQualified = monthInspects.stream() .map(ProcurementRecordStorage::getInboundNum) .reduce(BigDecimal.ZERO, BigDecimal::add); item.setSupplierNum(supplierQualified); // 3. 工厂检验(类型2)- 合格数量 BigDecimal factoryQualified = monthInspects1.stream() .map(ProcurementRecordOut::getInboundNum) .reduce(BigDecimal.ZERO, BigDecimal::add); item.setFactoryNum(factoryQualified); qualityStatisticsItems.add(item); } // 统计近4个月总数据(所有月份汇总) qualityStatisticsDto.setSupplierNum(supplierNum); qualityStatisticsDto.setFactoryNum(factoryNum); qualityStatisticsDto.setItem(qualityStatisticsItems); return qualityStatisticsDto; } @Override public Map<String, List<String>> productionStatistics() { // 获取最近四个月(当前月 + 前3个月)的时间范围 LocalDate today = LocalDate.now(); DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM"); // 年月格式化器 Map<String, List<String>> result = new HashMap<>(); List<String> months = new ArrayList<>(); // 存储年月(如 2025-12、2025-11) List<String> totalIncomeList = new ArrayList<>(); // 每月总收入 List<String> totalExpenseList = new ArrayList<>(); // 每月总支出 List<String> netIncomeList = new ArrayList<>(); // 每月净收入(收入-支出) // 步骤1:计算近4个月的年月列表(当前月、前1月、前2月、前3月) List<String> targetMonths = new ArrayList<>(); for (int i = 0; i < 4; i++) { LocalDate currentMonth = today.minusMonths(i); String monthStr = currentMonth.format(monthFormatter); targetMonths.add(monthStr); } // 反转列表,确保顺序为「前3月 → 当前月」(可选,按需求调整顺序) Collections.reverse(targetMonths); // 步骤2:一次性查询近4个月所有收入数据,按“年月”分组汇总 LocalDate fourMonthsAgo = today.minusMonths(3).withDayOfMonth(1); // 近4个月起始日(前3月1号) LocalDate currentMonthEnd = today.withDayOfMonth(today.lengthOfMonth()); // 当前月结束日 ZoneId zoneId = ZoneId.of("Asia/Shanghai"); // 查询近4个月所有收入 List<SalesLedgerWork> allIncomes = salesLedgerWorkMapper.selectList( Wrappers.<SalesLedgerWork>lambdaQuery() .ge(SalesLedgerWork::getSchedulingDate, fourMonthsAgo.toString()) // 大于等于起始日 .le(SalesLedgerWork::getSchedulingDate, currentMonthEnd.toString()) // 小于等于结束日 ); // 待生产 Map<String, BigDecimal> monthlyIncomeMap = allIncomes.stream() .filter(income -> income.getSchedulingNum() != null && income.getStatus().equals(1)) // 过滤空金额 .collect(Collectors.groupingBy( income -> { // 将输入时间(字符串)转换为LocalDate,再格式化为年月 return income.getSchedulingDate().format(monthFormatter); }, Collectors.reducing(BigDecimal.ZERO, SalesLedgerWork::getSchedulingNum, BigDecimal::add) )); // 生产中 Map<String, BigDecimal> monthlyExpenseMap = allIncomes.stream() .filter(income -> income.getSchedulingNum() != null && income.getStatus().equals(2)) // 过滤空金额 .collect(Collectors.groupingBy( income -> { // 将输入时间(字符串)转换为LocalDate,再格式化为年月 return income.getSchedulingDate().format(monthFormatter); }, Collectors.reducing(BigDecimal.ZERO, SalesLedgerWork::getSchedulingNum, BigDecimal::add) )); // 已报工 Map<String, BigDecimal> successIncomeMap = allIncomes.stream() .filter(income -> income.getSchedulingNum() != null && income.getStatus().equals(3)) // 过滤空金额 .collect(Collectors.groupingBy( income -> { // 将输入时间(字符串)转换为LocalDate,再格式化为年月 return income.getSchedulingDate().format(monthFormatter); }, Collectors.reducing(BigDecimal.ZERO, SalesLedgerWork::getSchedulingNum, BigDecimal::add) )); // 步骤4:循环4个目标月份,填充统计数据(无数据时默认为0) for (String month : targetMonths) { // 待生产 BigDecimal totalIncome = monthlyIncomeMap.getOrDefault(month, BigDecimal.ZERO); // 生产中 BigDecimal totalExpense = monthlyExpenseMap.getOrDefault(month, BigDecimal.ZERO); // 已报工 BigDecimal netIncome = successIncomeMap.getOrDefault(month, BigDecimal.ZERO); // 填充列表 months.add(month); totalIncomeList.add(totalIncome.toString()); totalExpenseList.add(totalExpense.toString()); netIncomeList.add(netIncome.toString()); } // 组装结果 result.put("days", months); // 年月(如 ["2025-09", "2025-10", "2025-11", "2025-12"]) result.put("totalIncome", totalIncomeList); // 待生产 result.put("totalExpense", totalExpenseList); // 生产中 result.put("netIncome", netIncomeList); // 已报工 return result; } } src/main/java/com/ruoyi/purchase/service/impl/PaymentRegistrationServiceImpl.java
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.pojo.AccountExpense; import com.ruoyi.account.service.AccountExpenseService; import com.ruoyi.basic.mapper.SupplierManageMapper; import com.ruoyi.basic.pojo.SupplierManage; import com.ruoyi.common.utils.DateUtils; @@ -26,6 +28,7 @@ import com.ruoyi.sales.pojo.SalesLedgerProduct; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.math.BigDecimal; @@ -57,6 +60,8 @@ private TicketRegistrationMapper ticketRegistrationMapper; private ProductRecordMapper productRecordMapper; private AccountExpenseService accountExpenseService; /** * 查询付款登记 @@ -95,6 +100,7 @@ * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int insertPaymentRegistration(PaymentRegistration paymentRegistration) { PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(paymentRegistration.getPurchaseLedgerId()); SalesLedger salesLedger = salesLedgerMapper.selectOne(new QueryWrapper<SalesLedger>(). @@ -127,6 +133,19 @@ paymentRegistration.setRegistrantId(loginUser.getUserId()); paymentRegistration.setCreateTime(DateUtils.getNowDate()); paymentRegistration.setUpdateTime(DateUtils.getNowDate()); // 2. 处理账户收入 AccountExpense accountExpense = new AccountExpense(); accountExpense.setExpenseDate(purchaseLedger.getEntryDate()); accountExpense.setExpenseType("0"); accountExpense.setSupplierName(purchaseLedger.getSupplierName()); accountExpense.setExpenseMoney(paymentRegistration.getCurrentPaymentAmount()); accountExpense.setExpenseDescribed("付款登记:" + tr.getInvoiceNumber()); accountExpense.setExpenseMethod("0"); accountExpense.setInvoiceNumber(tr.getInvoiceNumber()); accountExpense.setInputTime(new Date()); accountExpense.setInputUser(loginUser.getNickName()); accountExpenseService.save(accountExpense); return paymentRegistrationMapper.insert(paymentRegistration); } @@ -149,7 +168,13 @@ if (total.add(paymentRegistration.getCurrentPaymentAmount()).compareTo(ticketRegistration.getInvoiceAmount()) > 0) { throw new RuntimeException("付款金额超出发票金额"); } List<AccountExpense> accountExpenseDBs = accountExpenseService.getByInvoiceNumberList(ticketRegistration.getInvoiceNumber()); if (!CollectionUtils.isEmpty(accountExpenseDBs)) { accountExpenseDBs.forEach(accountExpenseDB -> { accountExpenseDB.setExpenseMoney(paymentRegistration.getCurrentPaymentAmount()); accountExpenseService.updateById(accountExpenseDB); }); } paymentRegistration.setUpdateTime(DateUtils.getNowDate()); return paymentRegistrationMapper.updateById(paymentRegistration); } src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -132,41 +132,13 @@ purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId()); purchaseLedger.setRecorderName(sysUser.getNickName()); purchaseLedger.setPhoneNumber(sysUser.getPhonenumber()); // 2. 处理账户收入 AccountExpense accountExpense = new AccountExpense(); accountExpense.setExpenseDate(purchaseLedger.getEntryDate()); accountExpense.setExpenseType("0"); accountExpense.setSupplierName(purchaseLedger.getSupplierName()); accountExpense.setExpenseMoney(purchaseLedger.getContractAmount()); accountExpense.setExpenseDescribed("采购合同:" + purchaseLedger.getPurchaseContractNumber()); accountExpense.setExpenseMethod("0"); accountExpense.setInvoiceNumber(purchaseLedger.getPurchaseContractNumber()); accountExpense.setInputTime(new Date()); accountExpense.setInputUser(loginUser.getNickName()); // 3. 新增或更新主表 if (purchaseLedger.getId() == null) { purchaseLedgerMapper.insert(purchaseLedger); // accountIncomeService.save(accountIncome); accountExpenseService.save(accountExpense); } else { purchaseLedgerMapper.updateById(purchaseLedger); PurchaseLedger purchaseLedgerDB = purchaseLedgerMapper.selectById(purchaseLedger.getId()); List<AccountExpense> accountExpenseDBs = accountExpenseService.getByInvoiceNumberList(purchaseLedger.getPurchaseContractNumber()); if (!CollectionUtils.isEmpty(accountExpenseDBs)) { accountExpenseDBs.forEach(accountExpenseDB -> { accountExpenseDB.setExpenseDate(purchaseLedgerDB.getEntryDate()); accountExpenseDB.setExpenseType("0"); accountExpenseDB.setSupplierName(purchaseLedgerDB.getSupplierName()); accountExpenseDB.setExpenseMoney(purchaseLedgerDB.getContractAmount()); accountExpenseDB.setExpenseDescribed("采购合同:" + purchaseLedgerDB.getPurchaseContractNumber()); accountExpenseDB.setExpenseMethod("0"); accountExpenseDB.setInvoiceNumber(purchaseLedgerDB.getPurchaseContractNumber()); accountExpenseService.updateById(accountExpenseDB); }); } } // 4. 处理子表数据 src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -122,7 +122,7 @@ /** * 新增修改销售台账 */ @Log(title = "销售台账", businessType = BusinessType.INSERT) @Log(title = "新增修改销售台账", businessType = BusinessType.INSERT) @PostMapping("/addOrUpdateSalesLedger") public AjaxResult add(@RequestBody SalesLedgerDto salesLedgerDto) { return toAjax(salesLedgerService.addOrUpdateSalesLedger(salesLedgerDto)); src/main/java/com/ruoyi/sales/service/impl/ReceiptPaymentServiceImpl.java
@@ -4,6 +4,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.pojo.AccountIncome; import com.ruoyi.account.service.AccountIncomeService; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.sales.dto.CustomerInteractionDto; @@ -19,6 +22,7 @@ import com.ruoyi.sales.service.ReceiptPaymentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; @@ -39,6 +43,8 @@ private SalesLedgerMapper salesLedgerMapper; @Autowired private InvoiceLedgerMapper invoiceLedgerMapper; @Autowired private AccountIncomeService accountIncomeService; /** * 回款登记新增 @@ -46,6 +52,7 @@ * @return */ @Override @Transactional(rollbackFor = Exception.class) public int receiptPaymentSaveOrUpdate(ReceiptPayment receiptPayment) { ReceiptPayment byId = receiptPayment; if (!ObjectUtils.isEmpty(receiptPayment.getId())){ @@ -64,10 +71,28 @@ throw new RuntimeException("本次回款金额不能大于待回款金额"); } if(null==receiptPayment.getId()){ AccountIncome accountIncome = new AccountIncome(); accountIncome.setIncomeDate(DateUtils.toDate(receiptPayment.getReceiptPaymentDate())); accountIncome.setIncomeType("0"); accountIncome.setCustomerName(receiptPaymentDto1.getCustomerName()); accountIncome.setIncomeMoney(receiptPayment.getReceiptPaymentAmount()); accountIncome.setIncomeMethod("0"); accountIncome.setInputTime(new Date()); accountIncome.setInputUser(receiptPayment.getRegistrant()); accountIncome.setIncomeDescribed("回款登记:" + invoiceLedger.getInvoiceNo()); accountIncome.setInvoiceNumber(invoiceLedger.getInvoiceNo()); accountIncomeService.save(accountIncome); return receiptPaymentMapper.insert(receiptPayment); }else { AccountIncome salesLedgerDB = accountIncomeService.getByInvoiceNumber(invoiceLedger.getInvoiceNo()); List<AccountIncome> accountIncomeDBs = accountIncomeService.getByInvoiceNumberList(salesLedgerDB.getInvoiceNumber()); if (!org.springframework.util.CollectionUtils.isEmpty(accountIncomeDBs)) { accountIncomeDBs.forEach(accountIncomeDB -> { accountIncomeDB.setIncomeMoney(receiptPayment.getReceiptPaymentAmount()); accountIncomeDB.setInputUser(receiptPayment.getRegistrant()); accountIncomeService.updateById(accountIncomeDB); }); } return receiptPaymentMapper.updateById(receiptPayment); } } src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -379,41 +379,15 @@ SalesLedger salesLedger = convertToEntity(salesLedgerDto); salesLedger.setCustomerName(customer.getCustomerName()); salesLedger.setTenantId(customer.getTenantId()); AccountIncome accountIncome = new AccountIncome(); accountIncome.setIncomeDate(salesLedger.getEntryDate()); accountIncome.setIncomeType("0"); accountIncome.setCustomerName(customer.getCustomerName()); accountIncome.setIncomeMoney(salesLedger.getContractAmount()); accountIncome.setIncomeMethod("0"); accountIncome.setInputTime(new Date()); accountIncome.setInputUser(salesLedger.getEntryPerson()); // 3. 新增或更新主表 if (salesLedger.getId() == null) { String contractNo = generateSalesContractNo(); salesLedger.setSalesContractNo(contractNo); salesLedgerMapper.insert(salesLedger); accountIncome.setIncomeDescribed("销售合同:" + salesLedger.getSalesContractNo()); accountIncome.setInvoiceNumber(salesLedger.getSalesContractNo()); accountIncomeService.save(accountIncome); } else { salesLedgerMapper.updateById(salesLedger); SalesLedger salesLedgerDB = salesLedgerMapper.selectById(salesLedger.getId()); List<AccountIncome> accountIncomeDBs = accountIncomeService.getByInvoiceNumberList(salesLedger.getSalesContractNo()); if (!org.springframework.util.CollectionUtils.isEmpty(accountIncomeDBs)) { accountIncomeDBs.forEach(accountIncomeDB -> { accountIncomeDB.setCustomerName(salesLedgerDB.getCustomerName()); accountIncomeDB.setIncomeMoney(salesLedgerDB.getContractAmount()); accountIncomeDB.setIncomeDescribed("销售合同:" + salesLedgerDB.getSalesContractNo()); accountIncomeDB.setInvoiceNumber(salesLedgerDB.getSalesContractNo()); accountIncomeDB.setInputTime(new Date()); accountIncomeDB.setInputUser(salesLedgerDB.getEntryPerson()); accountIncomeService.updateById(accountIncomeDB); }); } } // 4. 处理子表数据 List<SalesLedgerProduct> productList = salesLedgerDto.getProductData(); if (productList != null && !productList.isEmpty()) { src/main/resources/mapper/account/AccountExpenseMapper.xml
@@ -19,6 +19,7 @@ <if test="accountExpense.expenseMethod != null and accountExpense.expenseMethod != '' "> AND expense_method = #{accountExpense.expenseMethod} </if> order by expense_date desc </select> <select id="accountExpenseExport" resultType="com.ruoyi.account.pojo.AccountExpense"> SELECT src/main/resources/mapper/account/AccountIncomeMapper.xml
@@ -20,6 +20,7 @@ <if test="accountIncome.incomeMethod != null and accountIncome.incomeMethod != '' "> AND income_method = #{accountIncome.incomeMethod} </if> order by income_date desc </select> <select id="accountIncomeExport" resultType="com.ruoyi.account.pojo.AccountIncome"> SELECT