已添加4个文件
已修改21个文件
1045 ■■■■ 文件已修改
src/main/java/com/ruoyi/account/controller/AccountingController.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProcessRouteController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductBomController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductProcessController.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/SalesLedgerProductionAccountingController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/SalesLedgerProductionAccountingDto.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductOrder.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProcessRouteService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductProcessService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProcessRouteServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductProcessServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 242 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceLedgerMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/InvoicePurchaseMapper.xml 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/controller/AccountingController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package com.ruoyi.account.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.service.impl.AccountingServiceImpl;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author :yys
 * @date : 2026/1/17 10:40
 */
@Api(tags = "会计核算")
@RestController
@RequestMapping("/accounting")
public class AccountingController extends BaseController {
    @Autowired
    private AccountingServiceImpl accountingService;
    @ApiOperation("总计")
    @GetMapping("/total")
    public AjaxResult total(@RequestParam Integer year) {
        return accountingService.total(year);
    }
    @ApiOperation("设备类型分布")
    @GetMapping("/deviceTypeDistribution")
    public AjaxResult deviceTypeDistribution(@RequestParam Integer year) {
        return accountingService.deviceTypeDistribution(year);
    }
    @ApiOperation("设备分页查询计算折旧")
    @GetMapping("/calculateDepreciation")
    public AjaxResult calculateDepreciation(Page page, @RequestParam Integer year) {
        return accountingService.calculateDepreciation(page,year);
    }
}
src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.account.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author :yys
 * @date : 2026/1/17 13:42
 */
@Data
public class DeviceTypeDetail {
    private String type;       // è®¾å¤‡ç±»åž‹
    private BigDecimal count;  // æ•°é‡
    private BigDecimal amount; // ä»·æ ¼
}
src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.account.dto;
import com.mchange.v1.util.ListUtils;
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
 * @author :yys
 * @date : 2026/1/17 13:19
 */
@Data
public class DeviceTypeDistributionVO {
    private Integer totalCount = 0; // è®¾å¤‡ç±»åž‹æ€»æ•°
    // é¥¼å›¾å’ŒæŠ˜çº¿å›¾çš„分类(设备类型)
    private List<String> categories = new ArrayList<>();
    // å„类型设备的数量
    private List<BigDecimal> countData = new ArrayList<>();
    // å„类型设备的总金额
    private List<BigDecimal> amountData = new ArrayList<>();
    // æ˜Žç»†åˆ—表,包含类型、数量、金额
    private List<DeviceTypeDetail> details = new ArrayList<>();
}
src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,292 @@
package com.ruoyi.account.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.dto.DeviceTypeDetail;
import com.ruoyi.account.dto.DeviceTypeDistributionVO;
import com.ruoyi.account.mapper.BorrowInfoMapper;
import com.ruoyi.account.pojo.BorrowInfo;
import com.ruoyi.device.mapper.DeviceLedgerMapper;
import com.ruoyi.device.pojo.DeviceLedger;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.CustomStorage;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.service.impl.ProcurementRecordOutServiceImpl;
import com.ruoyi.procurementrecord.service.impl.ProcurementRecordServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author :yys
 * @date : 2026/1/17 10:41
 */
@Service
@Slf4j
public class AccountingServiceImpl {
    @Autowired
    private DeviceLedgerMapper deviceLedgerMapper;
    @Autowired
    private BorrowInfoMapper borrowInfoMapper;
    @Autowired
    private CustomStorageMapper customStorageMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordMapper;
    @Autowired
    private ProcurementRecordOutMapper procurementRecordOutMapper;
    public AjaxResult total(Integer year) {
        Map<String,Object> map = new HashMap<>();
        map.put("deprAmount",0); // æŠ˜æ—§é‡‘额
        map.put("deviceTotal",0); // è®¾å¤‡æ€»æ•°
        map.put("deviceAmount",0); // è®¾å¤‡èµ„产原值
        map.put("netValue",0); // å‡€å€¼
        map.put("debt",0); // è´Ÿå€º
        map.put("inventoryValue",0); // åº“存资产
        LambdaQueryWrapper<DeviceLedger> deviceLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLedgerLambdaQueryWrapper.like(DeviceLedger::getCreateTime,year);
        List<DeviceLedger> deviceLedgers = deviceLedgerMapper.selectList(deviceLedgerLambdaQueryWrapper);
        if(CollectionUtils.isNotEmpty(deviceLedgers)){
            map.put("deviceTotal",deviceLedgers.size());
            BigDecimal reduce = deviceLedgers.stream()
                    .map(DeviceLedger::getTaxIncludingPriceTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put("deviceAmount",reduce);
            List<DeviceLedger> collect = deviceLedgers.stream().filter(deviceLedger -> deviceLedger.getIsDepr() != null && deviceLedger.getIsDepr() == 1).collect(Collectors.toList());
            // æ ¹æ®å½“前年份和设备录入时间年份比较 * æ¯å¹´æŠ˜æ—§é‡‘额 = è®¾å¤‡æŠ˜æ—§é‡‘额
            BigDecimal total = new BigDecimal(0);
            if(CollectionUtils.isNotEmpty(collect)){
                for (DeviceLedger deviceLedger : collect) {
                    BigDecimal totalDepreciation = calculatePreciseDepreciation(deviceLedger);
                    total = total.add(totalDepreciation);
                }
                map.put("deprAmount",total);
            }
            // å‡€å€¼ = è®¾å¤‡èµ„产原值 - è®¾å¤‡ç´¯è®¡æŠ˜æ—§é‡‘额
            map.put("netValue",reduce.subtract(total));
        }
        // è´Ÿå€º
        LambdaQueryWrapper<BorrowInfo> borrowInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        borrowInfoLambdaQueryWrapper.like(BorrowInfo::getCreateTime,year)
                .eq(BorrowInfo::getStatus,1);
        List<BorrowInfo> borrowInfos = borrowInfoMapper.selectList(borrowInfoLambdaQueryWrapper);
        if(CollectionUtils.isNotEmpty(borrowInfos)){
            BigDecimal reduce = borrowInfos.stream()
                    .map(BorrowInfo::getBorrowAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put("debt",reduce);
        }
        // åº“存资产
        LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordStorageLambdaQueryWrapper.like(ProcurementRecordStorage::getCreateTime,year);
        List<ProcurementRecordStorage> procurementRecordStorages = procurementRecordMapper.selectList(procurementRecordStorageLambdaQueryWrapper);
        BigDecimal procurementRecordTotal = new BigDecimal(0);
        if(CollectionUtils.isNotEmpty(procurementRecordStorages)){
            // èŽ·å–å‡ºåº“æ•°æ®
            LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
            procurementRecordOutLambdaQueryWrapper.like(ProcurementRecordOut::getCreateTime,year)
                    .in(ProcurementRecordOut::getProcurementRecordStorageId, procurementRecordStorages
                            .stream()
                            .map(ProcurementRecordStorage::getId)
                            .collect(Collectors.toList()))
                    .in(ProcurementRecordOut::getType,Arrays.asList(1,2));
            List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(procurementRecordOutLambdaQueryWrapper);
            for (ProcurementRecordStorage procurementRecordStorage : procurementRecordStorages) {
                // é‡‡è´­ï¼Œç”Ÿäº§å…¥åº“总价值
                procurementRecordTotal.add(procurementRecordStorage.getUnitPrice().multiply(procurementRecordStorage.getInboundNum()));
                // é€šè¿‡å…¥åº“id,类型获取出库数据
                List<ProcurementRecordOut> procurementRecordOutsByStorageId = procurementRecordOuts.stream()
                        .filter(procurementRecordOut -> procurementRecordOut.getProcurementRecordStorageId().equals(procurementRecordStorage.getId())
                        && procurementRecordOut.getType().equals(procurementRecordStorage.getType()))
                        .collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(procurementRecordOutsByStorageId)){
                    for (ProcurementRecordOut procurementRecordOut : procurementRecordOutsByStorageId) {
                        // é‡‡è´­ï¼Œç”Ÿäº§å‡ºåº“总价值
                        procurementRecordTotal.subtract(procurementRecordStorage.getUnitPrice().multiply(procurementRecordOut.getInboundNum()));
                    }
                }
            }
        }
        LambdaQueryWrapper<CustomStorage> customStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        customStorageLambdaQueryWrapper.like(CustomStorage::getInboundDate,year);
        List<CustomStorage> customStorages = customStorageMapper.selectList(customStorageLambdaQueryWrapper);
        BigDecimal customStorageTotal = new BigDecimal(0);
        if(CollectionUtils.isNotEmpty(customStorages)){
            // èŽ·å–å‡ºåº“æ•°æ®
            LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
            procurementRecordOutLambdaQueryWrapper.like(ProcurementRecordOut::getCreateTime,year)
                    .in(ProcurementRecordOut::getProcurementRecordStorageId, customStorages
                            .stream()
                            .map(CustomStorage::getId)
                            .collect(Collectors.toList()))
                    .eq(ProcurementRecordOut::getType,3);
            List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(procurementRecordOutLambdaQueryWrapper);
            customStorages.forEach(customStorage -> {
                // è‡ªå®šä¹‰å…¥åº“总价值
                customStorageTotal.add(customStorage.getTaxInclusiveUnitPrice().multiply(customStorage.getInboundNum()));
                // é€šè¿‡å…¥åº“id,类型获取出库数据
                List<ProcurementRecordOut> procurementRecordOutsByStorageId = procurementRecordOuts.stream()
                        .filter(procurementRecordOut -> procurementRecordOut.getProcurementRecordStorageId().equals(customStorage.getId()))
                        .collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(procurementRecordOutsByStorageId)){
                    for (ProcurementRecordOut procurementRecordOut : procurementRecordOutsByStorageId) {
                        // è‡ªå®šä¹‰å‡ºåº“总价值
                        customStorageTotal.subtract(procurementRecordOut.getInboundNum().multiply(customStorage.getTaxInclusiveUnitPrice()));
                    }
                }
            });
        }
        map.put("inventoryValue",procurementRecordTotal.add(customStorageTotal));
        return AjaxResult.success( map);
    }
    /**
     * è®¡ç®—设备累计折旧金额
     * @param deviceLedger è®¾å¤‡å°è´¦å®žä½“
     * @return ç´¯è®¡æŠ˜æ—§é‡‘额(BigDecimal,保留2位小数)
     */
    public static BigDecimal calculateTotalDepreciation(DeviceLedger deviceLedger) {
        // 1. ç©ºå€¼æ ¡éªŒ
        if (deviceLedger == null) {
            return BigDecimal.ZERO;
        }
        // 2. åˆ¤æ–­æ˜¯å¦å¼€å¯æŠ˜æ—§ï¼Œæœªå¼€å¯åˆ™æŠ˜æ—§é‡‘额为0
        Integer isDepr = deviceLedger.getIsDepr();
        if (isDepr == null || isDepr != 1) { // 1-是 2-否
            return BigDecimal.ZERO;
        }
        // 3. èŽ·å–æ¯å¹´æŠ˜æ—§é‡‘é¢ï¼Œä¸ºç©ºåˆ™è¿”å›ž0
        BigDecimal annualDepreciation = deviceLedger.getAnnualDepreciationAmount();
        if (annualDepreciation == null || annualDepreciation.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // 4. èŽ·å–è®¾å¤‡å½•å…¥æ—¶é—´ï¼Œä¸ºç©ºåˆ™è¿”å›ž0
        LocalDateTime createTime = deviceLedger.getCreateTime();
        if (createTime == null) {
            return BigDecimal.ZERO;
        }
        // 5. è®¡ç®—年份差值
        int currentYear = Year.now().getValue(); // å½“前年份
        int createYear = createTime.getYear();    // è®¾å¤‡å½•入年份
        int yearDiff = currentYear - createYear;
        // 6. å¤„理年份差值为负数的情况(录入时间在未来)
        if (yearDiff < 0) {
            return BigDecimal.ZERO;
        }
        // 7. è®¡ç®—总折旧金额 = å¹´ä»½å·®å€¼ * æ¯å¹´æŠ˜æ—§é‡‘额
        BigDecimal totalDepreciation = annualDepreciation.multiply(BigDecimal.valueOf(yearDiff));
        // 8. è¿”回保留2位小数的结果(金额规范)
        return totalDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
    /**
     * ã€è¿›é˜¶ç‰ˆã€‘按实际天数计算折旧(更精准)
     * @param deviceLedger è®¾å¤‡å°è´¦å®žä½“
     * @return ç´¯è®¡æŠ˜æ—§é‡‘额
     */
    public static BigDecimal calculatePreciseDepreciation(DeviceLedger deviceLedger) {
        if (deviceLedger == null) {
            return BigDecimal.ZERO;
        }
        // åˆ¤æ–­æ˜¯å¦å¼€å¯æŠ˜æ—§
        Integer isDepr = deviceLedger.getIsDepr();
        if (isDepr == null || isDepr != 1) {
            return BigDecimal.ZERO;
        }
        // èŽ·å–æ¯å¹´æŠ˜æ—§é‡‘é¢
        BigDecimal annualDepreciation = deviceLedger.getAnnualDepreciationAmount();
        if (annualDepreciation == null || annualDepreciation.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // èŽ·å–è®¾å¤‡å½•å…¥æ—¶é—´
        LocalDateTime createTime = deviceLedger.getCreateTime();
        if (createTime == null) {
            return BigDecimal.ZERO;
        }
        // å½“前时间
        LocalDateTime now = LocalDateTime.now();
        // å¦‚果录入时间在未来,返回0
        if (createTime.isAfter(now)) {
            return BigDecimal.ZERO;
        }
        // è®¡ç®—总天数
        long days = ChronoUnit.DAYS.between(createTime, now);
        // æŒ‰æ¯å¹´365天计算折旧金额
        BigDecimal dailyDepreciation = annualDepreciation.divide(BigDecimal.valueOf(365), 6, BigDecimal.ROUND_HALF_UP);
        BigDecimal totalDepreciation = dailyDepreciation.multiply(BigDecimal.valueOf(days));
        return totalDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
    public AjaxResult deviceTypeDistribution(Integer year) {
        // 2. ç»„装返回VO
       DeviceTypeDistributionVO vo = new DeviceTypeDistributionVO();
       List<DeviceTypeDetail> details = deviceLedgerMapper.getDeviceTypeDistributionByYear( year);
        vo.setDetails(details);
       if(CollectionUtils.isNotEmpty(details)){
           // 3. æå–图表所需的分类和数据
           vo.setCategories(details.stream()
                   .map(DeviceTypeDetail::getType)
                   .collect(Collectors.toList()));
           vo.setCountData(details.stream()
                   .map(DeviceTypeDetail::getCount)
                   .collect(Collectors.toList()));
           vo.setAmountData(details.stream()
                   .map(DeviceTypeDetail::getAmount)
                   .collect(Collectors.toList()));
           vo.setTotalCount(vo.getCategories().size());
       }
        return AjaxResult.success(vo);
    }
    public AjaxResult calculateDepreciation(Page page, Integer year) {
        LambdaQueryWrapper<DeviceLedger> deviceLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLedgerLambdaQueryWrapper.like(DeviceLedger::getCreateTime,year)
                .eq(DeviceLedger::getIsDepr,1);
        IPage<DeviceLedger> deviceLedgerIPage = deviceLedgerMapper.selectPage(page, deviceLedgerLambdaQueryWrapper);
        for (DeviceLedger record : deviceLedgerIPage.getRecords()) {
            record.setDeprAmount(calculatePreciseDepreciation(record));
            record.setNetValue(record.getTaxIncludingPriceTotal().subtract(record.getDeprAmount()));
        }
        return AjaxResult.success(deviceLedgerIPage);
    }
}
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
@@ -135,4 +135,14 @@
    @ApiModelProperty("运行时长")
    private String runtimeDuration;
    @ApiModelProperty("是否折旧 1-是 2-否")
    private Integer isDepr;
    @ApiModelProperty("每年折旧金额")
    private BigDecimal annualDepreciationAmount;
    @ApiModelProperty("设备类型")
    private String type;
}
src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.dto.DeviceTypeDetail;
import com.ruoyi.account.dto.DeviceTypeDistributionVO;
import com.ruoyi.device.dto.DeviceLedgerDto;
import com.ruoyi.device.execl.DeviceLedgerExeclDto;
import com.ruoyi.device.pojo.DeviceLedger;
@@ -21,4 +23,11 @@
    @InterceptorIgnore(tenantLine = "true")
    DeviceLedger selectById1(Long id);
    /**
     * æŒ‰å¹´ä»½ç»Ÿè®¡è®¾å¤‡ç±»åž‹åˆ†å¸ƒ
     * @param year ä¼ å…¥çš„年份(如2026)
     * @return å„类型的数量和金额
     */
    List<DeviceTypeDetail> getDeviceTypeDistributionByYear(@Param("year") Integer year);
}
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
@@ -142,4 +142,21 @@
    @ApiModelProperty("运行时长")
    private String runtimeDuration;
    @ApiModelProperty("是否折旧 1-是 2-否")
    private Integer isDepr;
    @ApiModelProperty("每年折旧金额")
    private BigDecimal annualDepreciationAmount;
    @TableField(exist = false)
    @ApiModelProperty("累计折旧")
    private BigDecimal deprAmount;
    @TableField(exist = false)
    @ApiModelProperty("净值")
    private BigDecimal netValue;
    @ApiModelProperty("设备类型")
    private String type;
}
src/main/java/com/ruoyi/production/controller/ProcessRouteController.java
@@ -23,9 +23,6 @@
    @Autowired
    private ProcessRouteService processRouteService;
    @Autowired
    private ProcessRouteItemService processRouteItemService;
    @GetMapping("page")
    @ApiOperation("分页查询")
    public R page(Page<ProcessRouteDto>  page, ProcessRouteDto processRouteDto) {
@@ -45,8 +42,6 @@
    @ApiOperation("删除工艺路线")
    @DeleteMapping("/{ids}")
    public R delete(@PathVariable("ids") Long[] ids) {
        //删除工艺路线详情
        processRouteItemService.remove(Wrappers.<ProcessRouteItem>lambdaQuery().in(ProcessRouteItem::getRouteId,Arrays.asList(ids)));
        return R.ok(processRouteService.removeBatchByIds(Arrays.asList(ids)));
        return R.ok(processRouteService.batchDelete(Arrays.asList(ids)));
    }
}
src/main/java/com/ruoyi/production/controller/ProductBomController.java
@@ -11,8 +11,10 @@
import com.ruoyi.production.pojo.ProcessRoute;
import com.ruoyi.production.pojo.ProductBom;
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductProcessRoute;
import com.ruoyi.production.service.ProcessRouteService;
import com.ruoyi.production.service.ProductBomService;
import com.ruoyi.production.service.ProductProcessRouteService;
import com.ruoyi.production.service.ProductProcessService;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
@@ -39,6 +41,9 @@
    @Autowired
    private ProcessRouteService processRouteService;
    @Autowired
    private ProductProcessRouteService productProcessRouteService;
    @GetMapping("/listPage")
    @Log(title = "BOM-分页查询", businessType = BusinessType.OTHER)
@@ -67,7 +72,8 @@
    @Log(title = "删除", businessType = BusinessType.DELETE)
    public AjaxResult batchDelete(@RequestBody List<Integer> ids) {
        List<ProcessRoute> list = processRouteService.list(Wrappers.<ProcessRoute>lambdaQuery().in(ProcessRoute::getBomId, ids));
        if (list.size()>0){
        List<ProductProcessRoute> list2 = productProcessRouteService.list(Wrappers.<ProductProcessRoute>lambdaQuery().in(ProductProcessRoute::getBomId, ids));
        if (list.size()>0 || list2.size()>0){
            return AjaxResult.error("该BOM已经存在对应的工艺路线,无法进行删除");
        }
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/production/controller/ProductProcessController.java
@@ -59,10 +59,7 @@
    @DeleteMapping("/batchDelete")
    @Log(title = "删除", businessType = BusinessType.DELETE)
    public AjaxResult batchDelete(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return AjaxResult.error("请选择至少一条数据");
        }
        return AjaxResult.success(productProcessService.removeBatchByIds(ids));
        return AjaxResult.success(productProcessService.batchDelete(ids));
    }
    @ApiOperation("查询所有工序")
src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
@@ -1,9 +1,11 @@
package com.ruoyi.production.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.dto.ProductProcessRouteItemDto;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.service.ProductionProductMainService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -11,6 +13,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@@ -49,4 +52,16 @@
    public R delete(@RequestBody ProductionProductMainDto productionProductMainDto) {
        return R.ok(productionProductMainService.removeProductMain(productionProductMainDto));
    }
    /**
     * å¯¼å‡º
     */
    @PostMapping("/export")
    public void export(HttpServletResponse response, ProductionProductMainDto productionProductMainDto) {
        List<ProductionProductMainDto> list;
        list = productionProductMainService.listPageProductionProductMainDto(new Page<>(1, -1), productionProductMainDto).getRecords();
        ExcelUtil<ProductionProductMainDto> util = new ExcelUtil<ProductionProductMainDto>(ProductionProductMainDto.class);
        util.exportExcel(response, list, "生产报工数据");
    }
}
src/main/java/com/ruoyi/production/controller/SalesLedgerProductionAccountingController.java
@@ -2,10 +2,13 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.pojo.AccountExpense;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.ProductOrderDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.pojo.SalesLedgerProductionAccounting;
import com.ruoyi.production.service.impl.SalesLedgerProductionAccountingServiceImpl;
@@ -13,8 +16,12 @@
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * @author :yys
@@ -36,4 +43,15 @@
        return AjaxResult.success(list);
    }
    /**
     * å¯¼å‡º
     */
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto) {
        List<SalesLedgerProductionAccountingDto> list;
        list = salesLedgerProductionAccountingService.listPage(new Page<>(1, -1), salesLedgerProductionAccountingDto).getRecords();
        ExcelUtil<SalesLedgerProductionAccountingDto> util = new ExcelUtil<SalesLedgerProductionAccountingDto>(SalesLedgerProductionAccountingDto.class);
        util.exportExcel(response, list, "生产核算数据");
    }
}
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.dto;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.production.pojo.ProductionProductMain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -8,28 +10,36 @@
import java.time.LocalDateTime;
@Data
@ExcelIgnoreUnannotated
public class ProductionProductMainDto extends ProductionProductMain {
    @ApiModelProperty(value = "工单编号")
    @Excel(name = "工单编号")
    private String workOrderNo;
    @ApiModelProperty(value = "工单状态")
    private String workOrderStatus;
    @ApiModelProperty(value = "报工人员昵称")
    @Excel(name = "报工人")
    private String nickName;
    @ApiModelProperty(value = "报工数量")
    @Excel(name = "报工数量")
    private BigDecimal quantity;
    //产品名称
    @Excel(name = "产品名称")
    private String productName;
    //产品规格型号
    @Excel(name = "产品规格型号")
    private String productModelName;
    //单位
    @Excel(name = "单位")
    private String unit;
    //销售合同号
    @Excel(name = "销售合同号")
    private String salesContractNo;
}
src/main/java/com/ruoyi/production/dto/SalesLedgerProductionAccountingDto.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.dto;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -12,6 +14,7 @@
 */
@Data
@ApiModel
@ExcelIgnoreUnannotated
public class SalesLedgerProductionAccountingDto extends SalesLedgerProductDto{
    /**
@@ -24,6 +27,7 @@
     * æŽ’产人名称
     */
    @ApiModelProperty(value = "生产人名称")
    @Excel(name = "生产人")
    private String schedulingUserName;
@@ -31,29 +35,34 @@
     * å·¥èµ„
     */
    @ApiModelProperty(value = "工资")
    @Excel(name = "工资")
    private BigDecimal wages;
    /**
     * ç”Ÿäº§æ•°é‡
     */
    @ApiModelProperty(value = "生产数量")
    @Excel(name = "生产数量")
    private BigDecimal finishedNum;
    /**
     * å·¥æ—¶å®šé¢
     */
    @ApiModelProperty(value = "工时定额")
    @Excel(name = "工时定额")
    private BigDecimal workHours;
    /**
     * å·¥åº
     */
    @ApiModelProperty(value = "工序")
    @Excel(name = "工序")
    private String process;
    /**
     * æŽ’产日期
     */
    @ApiModelProperty(value = "排产日期")
    @Excel(name = "生产日期")
    private String schedulingDate;
    @ApiModelProperty(value = "开始时间")
src/main/java/com/ruoyi/production/pojo/ProductOrder.java
@@ -34,7 +34,7 @@
    private Long productModelId;
    /**
     * å·¥è‰ºè·¯çº¿id
     * æ¨¡ç‰ˆçš„工艺路线id
     */
    @ApiModelProperty(value = "工艺路线id")
    private Long routeId;
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
@@ -16,6 +17,7 @@
    private Long id;
    @ApiModelProperty(value = "报工单号")
    @Excel(name = "报工单号")
    private String productNo;
    @ApiModelProperty(value = "报工人员id")
@@ -37,6 +39,7 @@
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "创建时间")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "更新时间")
src/main/java/com/ruoyi/production/service/ProcessRouteService.java
@@ -7,9 +7,13 @@
import com.ruoyi.production.pojo.ProcessRoute;
import io.swagger.models.auth.In;
import java.util.List;
public interface ProcessRouteService extends IService<ProcessRoute> {
    IPage<ProcessRouteDto> pageProcessRouteDto(Page<ProcessRouteDto> page, ProcessRouteDto processRouteDto);
    Integer saveProcessRoute(ProcessRoute processRoute);
    int batchDelete(List<Long> ids);
}
src/main/java/com/ruoyi/production/service/ProductProcessService.java
@@ -23,4 +23,6 @@
    AjaxResult add(ProductProcessDto productProcessDto);
    AjaxResult importData(MultipartFile file);
    String batchDelete(List<Integer> ids);
}
src/main/java/com/ruoyi/production/service/impl/ProcessRouteServiceImpl.java
@@ -1,11 +1,18 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.dto.ProcessRouteDto;
import com.ruoyi.production.mapper.ProcessRouteItemMapper;
import com.ruoyi.production.mapper.ProcessRouteMapper;
import com.ruoyi.production.mapper.ProductOrderMapper;
import com.ruoyi.production.mapper.ProductProcessRouteMapper;
import com.ruoyi.production.pojo.ProcessRoute;
import com.ruoyi.production.pojo.ProcessRouteItem;
import com.ruoyi.production.pojo.ProductOrder;
import com.ruoyi.production.pojo.ProductProcessRoute;
import com.ruoyi.production.service.ProcessRouteService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +22,8 @@
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
@Service
@AllArgsConstructor
@@ -23,6 +32,12 @@
    @Autowired
    private ProcessRouteMapper processRouteMapper;
    @Autowired
    private ProcessRouteItemMapper processRouteItemMapper;
    @Autowired
    private ProductOrderMapper productOrderMapper;
    @Override
    public IPage<ProcessRouteDto> pageProcessRouteDto(Page<ProcessRouteDto> page, ProcessRouteDto processRouteDto) {
@@ -40,4 +55,16 @@
        processRoute.setProcessRouteCode(newProductCode);
        return processRouteMapper.updateById(processRoute);
    }
    @Override
    public int batchDelete(List<Long> ids) {
        //先判断是否已经引用了
        List<ProductOrder> productOrders = productOrderMapper.selectList(Wrappers.<ProductOrder>lambdaQuery().in(ProductOrder::getRouteId, ids));
        if (productOrders.size()>0){
            throw new RuntimeException("该工艺路线生产已引用,不能删除");
        }
        //删除工艺路线详情
        processRouteItemMapper.delete(Wrappers.<ProcessRouteItem>lambdaQuery().in(ProcessRouteItem::getRouteId, ids));
        return processRouteMapper.deleteBatchIds(ids);
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductProcessServiceImpl.java
@@ -2,14 +2,19 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.ProductProcessDto;
import com.ruoyi.production.mapper.ProcessRouteItemMapper;
import com.ruoyi.production.mapper.ProductProcessMapper;
import com.ruoyi.production.mapper.ProductProcessRouteItemMapper;
import com.ruoyi.production.pojo.ProcessRouteItem;
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductProcessRouteItem;
import com.ruoyi.production.service.ProductProcessService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +28,10 @@
public class ProductProcessServiceImpl extends ServiceImpl<ProductProcessMapper, ProductProcess> implements ProductProcessService {
    @Autowired
    private ProductProcessMapper productProcessMapper;
    @Autowired
    private ProcessRouteItemMapper processRouteItemMapper;
    @Autowired
    private ProductProcessRouteItemMapper productProcessRouteItemMapper;
    @Override
    public IPage<ProductProcessDto> listPage(Page page, ProductProcessDto productProcessDto) {
@@ -34,7 +43,7 @@
        ProductProcess productProcess = new ProductProcess();
        BeanUtils.copyProperties(productProcessDto,productProcess);
        boolean save = productProcessMapper.insert(productProcess) > 0;
        if (save && ObjectUtils.isNotNull(productProcessDto.getNo())) {
        if (save && ObjectUtils.isNull(productProcessDto.getNo())) {
            // æ ¹æ®id生成no字段:GX + 8位数字(不足8位前面补0)
            String no = "GX" + String.format("%08d", productProcess.getId());
            productProcess.setNo(no);
@@ -42,7 +51,7 @@
            productProcessMapper.updateById(productProcess);
            return AjaxResult.success();
        }
        return AjaxResult.error();
        return AjaxResult.success();
    }
    @Override
@@ -60,4 +69,16 @@
            return AjaxResult.error("导入失败");
        }
    }
    @Override
    public String batchDelete(List<Integer> ids) {
        //查询是否生产中已经引用了这些工序
        List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(Wrappers.<ProcessRouteItem>lambdaQuery().in(ProcessRouteItem::getProcessId, ids));
        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().in(ProductProcessRouteItem::getProcessId, ids));
        if (!CollectionUtils.isEmpty(processRouteItems) || !CollectionUtils.isEmpty(productProcessRouteItems)){
            throw new RuntimeException("该工序已经被使用,无法删除");
        }
        productProcessMapper.deleteBatchIds(ids);
        return null;
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -157,10 +157,10 @@
            Product product = productMapper.selectById(productModel1.getProductId());
            BigDecimal stockQuantity = stockUtils.getStockQuantity(productModel1.getId()).get("stockQuantity");
            if (!(stockQuantity.compareTo(BigDecimal.ZERO) > 0)) {
                throw new RuntimeException(product.getProductName() + "库存为0");
                throw new RuntimeException(product.getProductName()+"产品的"+productModel1.getModel() + "的规格库存为0");
            }
            if (stockQuantity.compareTo(productStructureDto.getUnitQuantity().multiply(dto.getQuantity())) < 0) {
                throw new RuntimeException(product.getProductName() + "库存不足");
                throw new RuntimeException(product.getProductName()+"产品的"+productModel1.getModel() + "的规格库存不足");
            }
            ProductionProductInput productionProductInput = new ProductionProductInput();
            productionProductInput.setProductModelId(productStructureDto.getProductModelId());
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -5,12 +5,15 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.*;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.sales.dto.InvoiceRegistrationProductDto;
import com.ruoyi.sales.dto.SalesLedgerProductDto;
import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper;
@@ -45,6 +48,7 @@
public class SalesLedgerProductServiceImpl extends ServiceImpl<SalesLedgerProductMapper, SalesLedgerProduct> implements ISalesLedgerProductService {
    private SalesLedgerProductMapper salesLedgerProductMapper;
    private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private SalesLedgerMapper salesLedgerMapper;
@@ -62,6 +66,10 @@
    private ProductProcessRouteMapper productProcessRouteMapper;
    private ProductWorkOrderMapper productWorkOrderMapper;
    private ProductionProductMainMapper productionProductMainMapper;
    private ProductionProductOutputMapper productionProductOutputMapper;
    private ProductionProductInputMapper productionProductInputMapper;
    private QualityInspectMapper qualityInspectMapper;
    @Override
    public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
@@ -205,76 +213,18 @@
        Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
        if (salesLedgerProduct.getId() == null) {
            result = salesLedgerProductMapper.insert(salesLedgerProduct);
            ProductOrder productOrder = new ProductOrder();
            productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
            productOrder.setProductModelId(salesLedgerProduct.getId());
            productOrder.setNpsNo("SC" + String.format("%08d", salesLedgerProduct.getId()));
            productOrder.setQuantity(salesLedgerProduct.getQuantity());//需求数量
            productOrder.setCompleteQuantity(BigDecimal.ZERO);//完成数量
            productOrderMapper.insert(productOrder);
            ProcessRoute processRoute = processRouteMapper.selectOne(new QueryWrapper<ProcessRoute>().lambda().eq(ProcessRoute::getProductModelId, salesLedgerProduct.getProductModelId()));
            if (processRoute != null) {
                //新增生产订单工艺路线主表
                ProductProcessRoute productProcessRoute = new ProductProcessRoute();
                productProcessRoute.setProductModelId(processRoute.getProductModelId());
                productProcessRoute.setProcessRouteCode(processRoute.getProcessRouteCode());
                productProcessRoute.setProductOrderId(productOrder.getId());
                productProcessRoute.setBomId(processRoute.getBomId());
                productProcessRouteMapper.insert(productProcessRoute);
                //新增生产订单工艺路线子表
                List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(new QueryWrapper<ProcessRouteItem>().lambda().eq(ProcessRouteItem::getRouteId, processRoute.getId()));
                // ç”Ÿæˆå½“前日期的前缀:年月日
                String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                for (ProcessRouteItem processRouteItem : processRouteItems) {
                    ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                    productProcessRouteItem.setProductModelId(processRouteItem.getProductModelId());
                    productProcessRouteItem.setProcessId(processRouteItem.getProcessId());
                    productProcessRouteItem.setProductOrderId(productOrder.getId());
                    productProcessRouteItem.setProductRouteId(productProcessRoute.getId());
                    int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                    if (insert > 0) {
                        // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§å·¥å•号
                        QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                        queryWrapper.likeRight("work_order_no", datePrefix)
                                .orderByDesc("work_order_no")
                                .last("LIMIT 1");
                        ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                        int sequenceNumber = 1; // é»˜è®¤åºå·
                        if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                            String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                            if (lastNo.startsWith(datePrefix)) {
                                String seqStr = lastNo.substring(datePrefix.length());
                                try {
                                    sequenceNumber = Integer.parseInt(seqStr) + 1;
                                } catch (NumberFormatException e) {
                                    sequenceNumber = 1;
                                }
                            }
                        }
                        // ç”Ÿæˆå®Œæ•´çš„工单号
                        String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                        ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                        productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                        productWorkOrder.setProductOrderId(productOrder.getId());
                        productWorkOrder.setPlanQuantity(salesLedgerProduct.getQuantity());
                        productWorkOrder.setWorkOrderNo(workOrderNoStr);
                        productWorkOrder.setStatus(1);
                        productWorkOrderMapper.insert(productWorkOrder);
                    }
                }
                productOrder.setRouteId(processRoute.getId());
                productOrderMapper.updateById(productOrder);
            }
            addProductionData(salesLedgerProduct);
        } else {
            //查询原本的产品型号id
            salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
            result = salesLedgerProductMapper.updateById(salesLedgerProduct);
            /*删除对应的生产数据并重新新增*/
            deleteProductionData(Arrays.asList(salesLedgerProduct.getId()));
            // åˆ é™¤ç”Ÿäº§æ ¸ç®—数据
            LambdaQueryWrapper<SalesLedgerProductionAccounting> reportWrapper = new LambdaQueryWrapper<>();
            reportWrapper.in(SalesLedgerProductionAccounting::getSalesLedgerId, salesLedgerId);
            salesLedgerProductionAccountingMapper.delete(reportWrapper);
            addProductionData(salesLedgerProduct);
        }
        // å¦‚果插入或更新成功,并且有 salesLedgerId,才继续更新主表金额
@@ -307,6 +257,164 @@
        return result;
    }
    /**
     * æ–°å¢žç”Ÿäº§æ•°æ®
     */
    public void addProductionData(SalesLedgerProduct salesLedgerProduct) {
        ProductOrder productOrder = new ProductOrder();
        productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        productOrder.setProductModelId(salesLedgerProduct.getId());
        productOrder.setNpsNo("SC" + String.format("%08d", salesLedgerProduct.getId()));
        productOrder.setQuantity(salesLedgerProduct.getQuantity());//需求数量
        productOrder.setCompleteQuantity(BigDecimal.ZERO);//完成数量
        productOrderMapper.insert(productOrder);
        List<ProcessRoute> processRoutes = processRouteMapper.selectList(new QueryWrapper<ProcessRoute>().lambda()
                .eq(ProcessRoute::getProductModelId, salesLedgerProduct.getProductModelId())
                .orderByDesc(ProcessRoute::getCreateTime));
        if (processRoutes.size()>0){
            ProcessRoute processRoute = processRoutes.get(0);
            //新增生产订单工艺路线主表
            ProductProcessRoute productProcessRoute = new ProductProcessRoute();
            productProcessRoute.setProductModelId(processRoute.getProductModelId());
            productProcessRoute.setProcessRouteCode(processRoute.getProcessRouteCode());
            productProcessRoute.setProductOrderId(productOrder.getId());
            productProcessRoute.setBomId(processRoute.getBomId());
            productProcessRouteMapper.insert(productProcessRoute);
            //新增生产订单工艺路线子表
            List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(new QueryWrapper<ProcessRouteItem>().lambda().eq(ProcessRouteItem::getRouteId, processRoute.getId()));
            // ç”Ÿæˆå½“前日期的前缀:年月日
            String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            for (ProcessRouteItem processRouteItem : processRouteItems) {
                ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                productProcessRouteItem.setProductModelId(processRouteItem.getProductModelId());
                productProcessRouteItem.setProcessId(processRouteItem.getProcessId());
                productProcessRouteItem.setProductOrderId(productOrder.getId());
                productProcessRouteItem.setProductRouteId(productProcessRoute.getId());
                int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                if (insert > 0) {
                    // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§å·¥å•号
                    QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                    queryWrapper.likeRight("work_order_no", datePrefix)
                            .orderByDesc("work_order_no")
                            .last("LIMIT 1");
                    ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                    int sequenceNumber = 1; // é»˜è®¤åºå·
                    if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                        String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                        if (lastNo.startsWith(datePrefix)) {
                            String seqStr = lastNo.substring(datePrefix.length());
                            try {
                                sequenceNumber = Integer.parseInt(seqStr) + 1;
                            } catch (NumberFormatException e) {
                                sequenceNumber = 1;
                            }
                        }
                    }
                    // ç”Ÿæˆå®Œæ•´çš„工单号
                    String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                    ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                    productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                    productWorkOrder.setProductOrderId(productOrder.getId());
                    productWorkOrder.setPlanQuantity(salesLedgerProduct.getQuantity());
                    productWorkOrder.setWorkOrderNo(workOrderNoStr);
                    productWorkOrder.setStatus(1);
                    productWorkOrderMapper.insert(productWorkOrder);
                }
            }
            productOrder.setRouteId(processRoute.getId());
            productOrderMapper.updateById(productOrder);
        }
    }
    /**
     * åˆ é™¤ç”Ÿäº§æ•°æ®
     */
    public void deleteProductionData(List<Long> productIds) {
        //批量查询productOrder
        List<ProductOrder> productOrders = productOrderMapper.selectList(
                new LambdaQueryWrapper<ProductOrder>()
                        .in(ProductOrder::getProductModelId, productIds)
        );
        if (!org.springframework.util.CollectionUtils.isEmpty(productOrders)) {
            List<Long> orderIds = productOrders.stream()
                    .map(ProductOrder::getId)
                    .collect(Collectors.toList());
            // æ‰¹é‡æŸ¥è¯¢processRouteItems
            List<ProductProcessRouteItem> allRouteItems = productProcessRouteItemMapper.selectList(
                    new LambdaQueryWrapper<ProductProcessRouteItem>()
                            .in(ProductProcessRouteItem::getProductOrderId, orderIds)
            );
            if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(allRouteItems)) {
                // èŽ·å–è¦åˆ é™¤çš„å·¥åºé¡¹ID
                List<Long> routeItemIds = allRouteItems.stream()
                        .map(ProductProcessRouteItem::getId)
                        .collect(Collectors.toList());
                // æŸ¥è¯¢å…³è”的工单ID
                List<ProductWorkOrder> workOrders = productWorkOrderMapper.selectList(
                        new LambdaQueryWrapper<ProductWorkOrder>()
                                .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds)
                );
                if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(workOrders)) {
                    List<Long> workOrderIds = workOrders.stream()
                            .map(ProductWorkOrder::getId)
                            .collect(Collectors.toList());
                    // æŸ¥è¯¢å…³è”的生产主表ID
                    List<ProductionProductMain> productMains = productionProductMainMapper.selectList(
                            new LambdaQueryWrapper<ProductionProductMain>()
                                    .in(ProductionProductMain::getWorkOrderId, workOrderIds)
                    );
                    List<Long> productMainIds = productMains.stream()
                            .map(ProductionProductMain::getId)
                            .collect(Collectors.toList());
                    // åˆ é™¤äº§å‡ºè¡¨ã€æŠ•入表数据
                    if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(productMainIds)) {
                        productionProductOutputMapper.deleteByProductMainIds(productMainIds);
                        productionProductInputMapper.deleteByProductMainIds(productMainIds);
                        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                                new LambdaQueryWrapper<QualityInspect>()
                                        .in(QualityInspect::getProductMainId, productMainIds)
                        );
                        qualityInspects.forEach(qualityInspect -> {
                            //inspectState=1 å·²æäº¤ ä¸èƒ½åˆ é™¤
                            if (qualityInspect.getInspectState() == 1) {
                                throw new RuntimeException("已提交的检验单不能删除");
                            }
                        });
                        qualityInspectMapper.deleteByProductMainIds(productMainIds);
                    }
                    // åˆ é™¤ç”Ÿäº§ä¸»è¡¨æ•°æ®
                    productionProductMainMapper.deleteByWorkOrderIds(workOrderIds);
                    // åˆ é™¤å·¥å•数据
                    productWorkOrderMapper.delete(new LambdaQueryWrapper<ProductWorkOrder>()
                            .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds));
                }
            }
            // æ‰¹é‡åˆ é™¤processRouteItem
            productProcessRouteItemMapper.delete(new LambdaQueryWrapper<ProductProcessRouteItem>()
                    .in(ProductProcessRouteItem::getProductOrderId, orderIds));
            // æ‰¹é‡åˆ é™¤productProcessRoute
            productProcessRouteMapper.delete(new LambdaQueryWrapper<ProductProcessRoute>()
                    .in(ProductProcessRoute::getProductOrderId, orderIds));
            // æ‰¹é‡åˆ é™¤productOrder
            productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>()
                    .in(ProductOrder::getProductModelId, productIds));
        }
    }
    @Override
    public IPage<SalesLedgerProductDto> listPage(Page page, SalesLedgerProductDto salesLedgerProduct) {
        IPage<SalesLedgerProductDto> salesLedgerProductDtoIPage = salesLedgerProductMapper.listPage(page, salesLedgerProduct);
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -32,6 +32,7 @@
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.*;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import com.ruoyi.sales.service.ISalesLedgerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -77,6 +78,7 @@
    private final CustomerMapper customerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl;
    private final CommonFileMapper commonFileMapper;
@@ -90,7 +92,7 @@
    private final SalesLedgerWorkMapper salesLedgerWorkMapper;
    private final SalesLedgerProductionAccountingMapper  salesLedgerProductionAccountingMapper;
    private final SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
@@ -217,8 +219,8 @@
        // æŸ¥è¯¢åŽŸå§‹æ•°æ®
        LambdaQueryWrapper<SalesLedger> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(SalesLedger::getCustomerId,
                        SalesLedger::getCustomerName,
                        SalesLedger::getContractAmount)
                SalesLedger::getCustomerName,
                SalesLedger::getContractAmount)
                .orderByDesc(SalesLedger::getContractAmount);
        List<SalesLedger> records = salesLedgerMapper.selectList(queryWrapper);
@@ -226,7 +228,7 @@
        Map<Long, GroupedCustomer> groupedMap = new LinkedHashMap<>(); // ä½¿ç”¨LinkedHashMap保持排序
        for (SalesLedger record : records) {
            groupedMap.computeIfAbsent(record.getCustomerId(),
                            k -> new GroupedCustomer(record.getCustomerId(), record.getCustomerName()))
                    k -> new GroupedCustomer(record.getCustomerId(), record.getCustomerName()))
                    .addAmount(record.getContractAmount());
        }
@@ -393,86 +395,8 @@
        List<Long> productIds = products.stream()
                .map(SalesLedgerProduct::getId)
                .collect(Collectors.toList());
        //批量查询productOrder
        List<ProductOrder> productOrders = productOrderMapper.selectList(
                new LambdaQueryWrapper<ProductOrder>()
                        .in(ProductOrder::getProductModelId, productIds)
        );
        if (!org.springframework.util.CollectionUtils.isEmpty(productOrders)) {
            List<Long> orderIds = productOrders.stream()
                    .map(ProductOrder::getId)
                    .collect(Collectors.toList());
            // æ‰¹é‡æŸ¥è¯¢processRouteItems
            List<ProductProcessRouteItem> allRouteItems = productProcessRouteItemMapper.selectList(
                    new LambdaQueryWrapper<ProductProcessRouteItem>()
                            .in(ProductProcessRouteItem::getProductOrderId, orderIds)
            );
            if (!CollectionUtils.isEmpty(allRouteItems)) {
                // èŽ·å–è¦åˆ é™¤çš„å·¥åºé¡¹ID
                List<Long> routeItemIds = allRouteItems.stream()
                        .map(ProductProcessRouteItem::getId)
                        .collect(Collectors.toList());
                // æŸ¥è¯¢å…³è”的工单ID
                List<ProductWorkOrder> workOrders = productWorkOrderMapper.selectList(
                        new LambdaQueryWrapper<ProductWorkOrder>()
                                .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds)
                );
                if (!CollectionUtils.isEmpty(workOrders)) {
                    List<Long> workOrderIds = workOrders.stream()
                            .map(ProductWorkOrder::getId)
                            .collect(Collectors.toList());
                    // æŸ¥è¯¢å…³è”的生产主表ID
                    List<ProductionProductMain> productMains = productionProductMainMapper.selectList(
                            new LambdaQueryWrapper<ProductionProductMain>()
                                    .in(ProductionProductMain::getWorkOrderId, workOrderIds)
                    );
                    List<Long> productMainIds = productMains.stream()
                            .map(ProductionProductMain::getId)
                            .collect(Collectors.toList());
                    // åˆ é™¤äº§å‡ºè¡¨ã€æŠ•入表数据
                    if (!CollectionUtils.isEmpty(productMainIds)) {
                        productionProductOutputMapper.deleteByProductMainIds(productMainIds);
                        productionProductInputMapper.deleteByProductMainIds(productMainIds);
                        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                                new LambdaQueryWrapper<QualityInspect>()
                                        .in(QualityInspect::getProductMainId, productMainIds)
                        );
                        qualityInspects.forEach(qualityInspect -> {
                            //inspectState=1 å·²æäº¤ ä¸èƒ½åˆ é™¤
                            if(qualityInspect.getInspectState() == 1){
                                throw new RuntimeException("已提交的检验单不能删除");
                            }
                        });
                        qualityInspectMapper.deleteByProductMainIds(productMainIds);
                    }
                    // åˆ é™¤ç”Ÿäº§ä¸»è¡¨æ•°æ®
                    productionProductMainMapper.deleteByWorkOrderIds(workOrderIds);
                    // åˆ é™¤å·¥å•数据
                    productWorkOrderMapper.delete(new LambdaQueryWrapper<ProductWorkOrder>()
                            .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds));
                }
            }
            // æ‰¹é‡åˆ é™¤processRouteItem
            productProcessRouteItemMapper.delete(new LambdaQueryWrapper<ProductProcessRouteItem>()
                    .in(ProductProcessRouteItem::getProductOrderId, orderIds));
            // æ‰¹é‡åˆ é™¤productProcessRoute
            productProcessRouteMapper.delete(new LambdaQueryWrapper<ProductProcessRoute>()
                    .in(ProductProcessRoute::getProductOrderId, orderIds));
            // æ‰¹é‡åˆ é™¤productOrder
            productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>()
                    .in(ProductOrder::getProductModelId, productIds));
        }
        //删除生产数据
        salesLedgerProductServiceImpl.deleteProductionData(productIds);
        // æ‰¹é‡åˆ é™¤äº§å“å­è¡¨
        if (!productIds.isEmpty()) {
@@ -483,11 +407,11 @@
        wrapper.in(InvoiceRegistrationProduct::getSalesLedgerId, idList);
        List<InvoiceRegistrationProduct> invoiceRegistrationProducts = invoiceRegistrationProductMapper.selectList(wrapper);
        List<Integer> invoiceLedgerIds = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(invoiceRegistrationProducts)){
        if (CollectionUtils.isNotEmpty(invoiceRegistrationProducts)) {
            LambdaQueryWrapper<InvoiceLedger> wrapperOne = new LambdaQueryWrapper<>();
            wrapperOne.in(InvoiceLedger::getInvoiceRegistrationProductId, invoiceRegistrationProducts.stream().map(InvoiceRegistrationProduct::getId).collect(Collectors.toList()));
            List<InvoiceLedger> invoiceLedgers = invoiceLedgerMapper.selectList(wrapperOne);
            if(CollectionUtils.isNotEmpty(invoiceLedgers)){
            if (CollectionUtils.isNotEmpty(invoiceLedgers)) {
                invoiceLedgerIds = invoiceLedgers.stream().map(InvoiceLedger::getId).collect(Collectors.toList());
            }
            invoiceLedgerMapper.delete(wrapperOne);
@@ -497,7 +421,7 @@
        wrapperTwo.in(InvoiceRegistration::getSalesLedgerId, idList);
        invoiceRegistrationMapper.delete(wrapperTwo);
        if(CollectionUtils.isNotEmpty(invoiceLedgerIds)){
        if (CollectionUtils.isNotEmpty(invoiceLedgerIds)) {
            LambdaQueryWrapper<ReceiptPayment> wrapperTree = new LambdaQueryWrapper<>();
            wrapperTree.in(ReceiptPayment::getInvoiceLedgerId, invoiceLedgerIds);
            receiptPaymentMapper.delete(wrapperTree);
@@ -512,7 +436,7 @@
        LambdaQueryWrapper<SalesLedgerWork> workOrderWrapper = new LambdaQueryWrapper<>();
        workOrderWrapper.in(SalesLedgerWork::getSalesLedgerId, idList);
        salesLedgerWorkMapper.delete(workOrderWrapper);
        // åˆ é™¤ç”Ÿäº§æŠ¥å·¥æ•°æ®
        // åˆ é™¤ç”Ÿäº§æ ¸ç®—数据
        LambdaQueryWrapper<SalesLedgerProductionAccounting> reportWrapper = new LambdaQueryWrapper<>();
        reportWrapper.in(SalesLedgerProductionAccounting::getSalesLedgerId, idList);
        salesLedgerProductionAccountingMapper.delete(reportWrapper);
@@ -668,79 +592,8 @@
                salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProductMapper.insert(salesLedgerProduct);
                ProductOrder productOrder = new ProductOrder();
                productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
                productOrder.setProductModelId(salesLedgerProduct.getId());
                productOrder.setNpsNo("SC" + String.format("%08d", salesLedgerProduct.getId()));
                productOrder.setQuantity(salesLedgerProduct.getQuantity());//需求数量
                productOrder.setCompleteQuantity(BigDecimal.ZERO);//完成数量
                productOrderMapper.insert(productOrder);
                ProcessRoute processRoute = processRouteMapper.selectOne(new QueryWrapper<ProcessRoute>().lambda().eq(ProcessRoute::getProductModelId, salesLedgerProduct.getProductModelId()));
                List<ProductStructureDto> productStructureDtos = productStructureMapper.listBybomId(processRoute.getBomId());
                if (processRoute != null) {
                    //新增生产订单工艺路线主表
                    ProductProcessRoute productProcessRoute = new ProductProcessRoute();
                    productProcessRoute.setProductModelId(processRoute.getProductModelId());
                    productProcessRoute.setProcessRouteCode(processRoute.getProcessRouteCode());
                    productProcessRoute.setProductOrderId(productOrder.getId());
                    productProcessRoute.setBomId(processRoute.getBomId());
                    productProcessRouteMapper.insert(productProcessRoute);
                    //新增生产订单工艺路线子表
                    List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(new QueryWrapper<ProcessRouteItem>().lambda().eq(ProcessRouteItem::getRouteId, processRoute.getId()));
                    // ç”Ÿæˆå½“前日期的前缀:年月日
                    String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                    for (ProcessRouteItem processRouteItem : processRouteItems) {
                        ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                        productProcessRouteItem.setProductModelId(processRouteItem.getProductModelId());
                        productProcessRouteItem.setProcessId(processRouteItem.getProcessId());
                        productProcessRouteItem.setProductOrderId(productOrder.getId());
                        productProcessRouteItem.setProductRouteId(productProcessRoute.getId());
                        productProcessRouteItem.setDragSort(processRouteItem.getDragSort());
                        int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                        if (insert > 0) {
                            // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§å·¥å•号
                            QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                            queryWrapper.likeRight("work_order_no", datePrefix)
                                    .orderByDesc("work_order_no")
                                    .last("LIMIT 1");
                            ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                            int sequenceNumber = 1; // é»˜è®¤åºå·
                            if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                                String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                                if (lastNo.startsWith(datePrefix)) {
                                    String seqStr = lastNo.substring(datePrefix.length());
                                    try {
                                        sequenceNumber = Integer.parseInt(seqStr) + 1;
                                    } catch (NumberFormatException e) {
                                        sequenceNumber = 1;
                                    }
                                }
                            }
                            // ç”Ÿæˆå®Œæ•´çš„工单号
                            String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                            ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                            productStructureDtos.stream().forEach(productStructureDto -> {
                                if (productStructureDto.getProductModelId().equals(productProcessRouteItem.getProductModelId())){
                                    productWorkOrder.setPlanQuantity(productWorkOrder.getPlanQuantity());
                                }
                            });
                            if (Objects.equals(productProcessRouteItem.getProductModelId(), salesLedgerProduct.getProductModelId())) {
                                productWorkOrder.setPlanQuantity(salesLedgerProduct.getQuantity());
                            }
                            productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                            productWorkOrder.setProductOrderId(productOrder.getId());
                            productWorkOrder.setWorkOrderNo(workOrderNoStr);
                            productWorkOrder.setStatus(1);
                            productWorkOrderMapper.insert(productWorkOrder);
                        }
                    }
                    productOrder.setRouteId(processRoute.getId());
                    productOrderMapper.updateById(productOrder);
                }
                // æ·»åŠ ç”Ÿäº§æ•°æ®
                salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct);
            }
        }
    }
src/main/resources/mapper/device/DeviceLedgerMapper.xml
@@ -28,7 +28,10 @@
        dl.update_time ,
        su.nick_name AS createUser,
        dl.update_user,
        dl.tenant_id
        dl.tenant_id,
        dl.is_depr,
        dl.annual_depreciation_amount,
        dl.type
        FROM device_ledger dl
        left join sys_user su on dl.create_user = su.user_id
        <where>
@@ -84,5 +87,18 @@
        from device_ledger
        where id = #{id}
    </select>
    <select id="getDeviceTypeDistributionByYear"
            resultType="com.ruoyi.account.dto.DeviceTypeDetail"
            parameterType="java.lang.Integer">
        SELECT
            `type`,
            SUM(`number`) AS `count`,
            SUM(tax_including_price_unit) AS amount
        FROM device_ledger
        WHERE YEAR(create_time) = #{year}
          AND type IS NOT NULL
        GROUP BY type
    </select>
</mapper>
src/main/resources/mapper/purchase/InvoicePurchaseMapper.xml
@@ -7,49 +7,27 @@
    <select id="selectPurchaseReport" resultType="com.ruoyi.purchase.dto.InvoicePurchaseReportDto">
        SELECT
        A.*,
        FORMAT(A.sales_tax_amount - A.purchase_tax_amount, 2) AS balance_amount
        FROM (
        SELECT
        sl.customer_contract_no,
        sl.id,
        pl.id AS pl_id,
        sl.sales_contract_no AS customerContractNo,
        sl.customer_name,
        sl.project_name,
        slp.tax_inclusive_total_price - slp.tax_exclusive_total_price AS contract_amount,
        SUM(slp1.tax_inclusive_total_price - slp1.tax_exclusive_total_price) AS purchase_amount,
        (slp.tax_exclusive_total_price) / COUNT(slp.id) AS sale_tax_exclusive_total_price,
        SUM(slp1.tax_exclusive_total_price) AS tax_exclusive_total_price,
        (slp.tax_inclusive_total_price - slp.tax_exclusive_total_price) -
        SUM(slp1.tax_inclusive_total_price - slp1.tax_exclusive_total_price) AS balance,
        CONCAT(
        FORMAT(
        ((slp.tax_inclusive_total_price - slp.tax_exclusive_total_price) -
        SUM(slp1.tax_inclusive_total_price - slp1.tax_exclusive_total_price)) /
        (slp.tax_inclusive_total_price - slp.tax_exclusive_total_price) * 100,
        2),
        '%'
        ) AS balance_ratio,
        (slp.tax_inclusive_total_price - slp.tax_exclusive_total_price) AS sales_tax_amount,
        SUM(slp1.tax_inclusive_total_price - slp1.tax_exclusive_total_price) AS purchase_tax_amount
        sl.contract_amount AS contract_amount,
        SUM( pl.contract_amount ) AS purchase_amount,
        (
        sl.contract_amount - SUM( pl.contract_amount )) AS balance,
        CONCAT( ROUND( ( sl.contract_amount - SUM( pl.contract_amount )) / sl.contract_amount * 100, 1 ), '%' ) AS balance_ratio
        FROM
        purchase_ledger pl
        LEFT JOIN
        sales_ledger sl ON pl.sales_ledger_id = sl.id
        LEFT JOIN
        sales_ledger_product slp ON slp.sales_ledger_id = sl.id AND slp.type = 1
        LEFT JOIN
        sales_ledger_product slp1 ON slp1.sales_ledger_id = pl.id AND slp1.type = 2
        GROUP BY
        sl.customer_contract_no, sl.customer_name, sl.project_name, sl.id, pl.id
        HAVING
        sl.customer_contract_no IS NOT NULL
        ) A
        sales_ledger sl
        INNER JOIN purchase_ledger pl ON sl.sales_contract_no = pl.sales_contract_no
        <where>
            <if test="c.customerName != null and c.customerName != ''">
                AND A.customer_name LIKE CONCAT('%', #{c.customerName}, '%')
                AND sl.customer_name LIKE CONCAT('%', #{c.customerName}, '%')
            </if>
        </where>
        GROUP BY
        sl.sales_contract_no,
        sl.customer_name,
        sl.contract_amount
        ORDER BY
        sl.sales_contract_no;
    </select>
    <select id="listVat" resultType="com.ruoyi.purchase.dto.VatDto">
        select *