chenhj
2026-04-24 ffe4413db421aabb7446007ff76d4e9943c6fc84
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -3,30 +3,48 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.extension.service.impl.ServiceImpl;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.pojo.ProductionPlan;
import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.sales.dto.InvoiceRegistrationProductDto;
import com.ruoyi.sales.dto.SalesLedgerProductDto;
import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.InvoiceRegistrationProduct;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo;
import com.ruoyi.technology.mapper.TechnologyBomStructureMapper;
import com.ruoyi.technology.mapper.TechnologyRoutingMapper;
import com.ruoyi.technology.pojo.TechnologyRouting;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -37,26 +55,27 @@
 * @date 2025-05-08
 */
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class SalesLedgerProductServiceImpl extends ServiceImpl<SalesLedgerProductMapper, SalesLedgerProduct> implements ISalesLedgerProductService {
    private SalesLedgerProductMapper salesLedgerProductMapper;
    private SalesLedgerMapper salesLedgerMapper;
    private PurchaseLedgerMapper purchaseLedgerMapper;
    private ProductOrderMapper productOrderMapper;
    private ProcessRouteItemMapper processRouteItemMapper;
    private ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private ProcessRouteMapper processRouteMapper;
    private ProductWorkOrderMapper productWorkOrderMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final ProductionAccountMapper productionAccountMapper;
    private final SalesLedgerMapper salesLedgerMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    private final ProductionPlanMapper productionPlanMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final ProductionOrderService productionOrderService;
    private final TechnologyRoutingMapper technologyRoutingMapper;
    private final TechnologyBomStructureMapper technologyBomStructureMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionProductOutputMapper productionProductOutputMapper;
    private final ProductionProductInputMapper productionProductInputMapper;
    private final QualityInspectMapper qualityInspectMapper;
    private final ShippingInfoMapper shippingInfoMapper;
    private final ShippingInfoServiceImpl shippingInfoService;
    private final StockUtils stockUtils;
    private final StockInventoryMapper stockInventoryMapper;
    @Override
    public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
@@ -70,6 +89,20 @@
//                .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType());
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct);
        if(!CollectionUtils.isEmpty(salesLedgerProducts)){
            salesLedgerProducts.forEach(item -> {
                // 发货信息
                ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
                        .orderByDesc(ShippingInfo::getCreateTime)
                        .last("limit 1"));
                if(shippingInfo != null){
                    item.setShippingDate(shippingInfo.getShippingDate());
                    item.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                    item.setShippingStatus(shippingInfo.getStatus());
                    item.setExpressCompany(shippingInfo.getExpressCompany());
                    item.setExpressNumber(shippingInfo.getExpressNumber());
                }
            });
            // 开票
            InvoiceRegistrationProductDto invoiceRegistrationProductDto = new InvoiceRegistrationProductDto();
            invoiceRegistrationProductDto.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId().intValue());
@@ -85,14 +118,16 @@
                        if(ledgerProduct.getId().intValue() == registrationProductDto.getSalesLedgerProductId()){
                            invoiceNum =  invoiceNum.add(registrationProductDto.getInvoiceNum());
                            invoiceAmount = invoiceAmount.add(registrationProductDto.getInvoiceAmount());
                            noInvoiceNum = ledgerProduct.getQuantity().subtract(invoiceNum);
                            noInvoiceAmount = ledgerProduct.getTaxInclusiveTotalPrice().subtract(invoiceAmount);
                        }
                    }
                    noInvoiceNum = ledgerProduct.getQuantity().subtract(invoiceNum);
                    noInvoiceAmount = ledgerProduct.getTaxInclusiveTotalPrice().subtract(invoiceAmount);
                    ledgerProduct.setInvoiceNum(invoiceNum);
                    ledgerProduct.setInvoiceAmount(invoiceAmount);
                    ledgerProduct.setNoInvoiceNum(noInvoiceNum);
                    ledgerProduct.setNoInvoiceAmount(noInvoiceAmount);
                }
            }
@@ -112,6 +147,12 @@
        if (deletedProducts.isEmpty()) {
            return 0; // 没有可删除的数据
        }
        //删除发货信息
        List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                .in(ShippingInfo::getSalesLedgerProductId, Arrays.asList(ids)));
        if(!CollectionUtils.isEmpty(shippingInfos)){
            shippingInfoService.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList()));
        }
        // 可能属于多个主表
        Set<Long> mainIds = deletedProducts.stream()
@@ -122,41 +163,7 @@
        // 2. 执行删除操作
        int result = salesLedgerProductMapper.deleteBatchIds(Arrays.asList(ids));
        //删除对应的生产订单
        //批量查询productOrder
        List<ProductOrder> productOrders = productOrderMapper.selectList(
                new LambdaQueryWrapper<ProductOrder>()
                        .in(ProductOrder::getProductModelId, ids)
        );
        if (!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::getRouteId, orderIds)
            );
            if (!CollectionUtils.isEmpty(allRouteItems)) {
                List<Long> routeItemIds = allRouteItems.stream()
                        .map(ProductProcessRouteItem::getId)
                        .collect(Collectors.toList());
                // 批量删除workOrder
                productWorkOrderMapper.delete(new LambdaQueryWrapper<ProductWorkOrder>()
                                .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds));
            }
            // 批量删除processRouteItem
            productProcessRouteItemMapper.delete(new LambdaQueryWrapper<ProductProcessRouteItem>()
                            .in(ProductProcessRouteItem::getRouteId, orderIds));
            // 批量删除productOrder
            productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>()
                    .in(ProductOrder::getProductModelId, ids));
        }
        deleteProductionData(Arrays.asList(ids));
        // 3. 对每个主表ID进行金额更新
        for (Long salesLedgerId : mainIds) {
@@ -179,72 +186,34 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addOrUpdateSalesLedgerProduct(SalesLedgerProduct salesLedgerProduct) {
        //未开票数量+金额
        salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity());
        salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
        // 待回款,付款
        if(salesLedgerProduct.getType().equals(1)){
            salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProduct.getTaxInclusiveTotalPrice().subtract(salesLedgerProduct.getInvoiceTotal()));
            //未开票数量+金额
            salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity());
            salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
        }else{
            salesLedgerProduct.setPendingTicketsTotal(salesLedgerProduct.getTaxInclusiveTotalPrice().subtract(salesLedgerProduct.getTicketsTotal()));
            // 未来票数量+金额
            salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
            salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
        }
        int result;
        Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
        if (salesLedgerProduct.getId() == null) {
            salesLedgerProduct.setRegisterDate(LocalDateTime.now());
            result = salesLedgerProductMapper.insert(salesLedgerProduct);
            ProductOrder productOrder = new ProductOrder();
            productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
            productOrder.setProductModelId(salesLedgerProduct.getId());
            productOrder.setNpsNo("SC" + String.format("%08d", salesLedgerProduct.getId()));
            productOrderMapper.insert(productOrder);
            ProcessRoute processRoute = processRouteMapper.selectOne(new QueryWrapper<ProcessRoute>().lambda().eq(ProcessRoute::getProductModelId, salesLedgerProduct.getProductModelId()));
            if (processRoute != null) {
                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.setRouteId(productOrder.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()));
            // 删除生产核算数据
            addProductionData(salesLedgerProduct);
        }
        // 如果插入或更新成功,并且有 salesLedgerId,才继续更新主表金额
@@ -275,6 +244,72 @@
            }
        }
        return result;
    }
    /**
     * 新增生产数据
     */
    public void addProductionData(SalesLedgerProduct salesLedgerProduct) {
        //先判断该产品是否需要生产
        if (!salesLedgerProduct.getIsProduction()) {
            return;
        }
        ProductionPlan productionPlan = new ProductionPlan();
        productionPlan.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        productionPlan.setSalesLedgerProductId(salesLedgerProduct.getId());
        productionPlan.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        productionPlan.setProductModelId(salesLedgerProduct.getProductModelId());
        productionPlan.setQtyRequired(salesLedgerProduct.getQuantity());
        productionPlan.setSource("销售");
        productionPlan.setStatus(0);
        productionPlanMapper.insert(productionPlan);
    }
    /**
     * 删除生产计划
     */
    public void deleteProductionData(List<Long> productIds) {
        List<ProductionPlan> productionPlans = productionPlanMapper.selectList(
                new LambdaQueryWrapper<ProductionPlan>()
                        .in(ProductionPlan::getSalesLedgerProductId, productIds.stream().map(Long::intValue).collect(Collectors.toList())));
        if (org.springframework.util.CollectionUtils.isEmpty(productionPlans)) {
            return;
        }
        //如果生产计划已下发则不能删除
        if (productionPlans.stream().anyMatch(productionPlan -> productionPlan.getStatus() != 0)) {
            throw new RuntimeException("生产计划已下发,不能删除该销售产品");
        }
        List<Long> ids = productionPlans.stream().map(ProductionPlan::getId).collect(Collectors.toList());
        productionPlanMapper.deleteByIds(ids);
    }
    @Override
    public IPage<SalesLedgerProductDto> listPage(Page page, SalesLedgerProductDto salesLedgerProduct) {
        IPage<SalesLedgerProductDto> salesLedgerProductDtoIPage = salesLedgerProductMapper.listPage(page, salesLedgerProduct);
        salesLedgerProductDtoIPage.getRecords().forEach(item -> {
            // 判断状态
            if(item.getTaxInclusiveTotalPrice().compareTo(item.getInvoiceTotal()) == 0){
                item.setStatusName("已完成付款");
            }else{
                item.setStatusName("未完成付款");
            }
        });
        return salesLedgerProductDtoIPage;
    }
    @Override
    public IPage<SalesLedgerProductDto> listPagePurchaseLedger(Page page, SalesLedgerProductDto salesLedgerProduct) {
        IPage<SalesLedgerProductDto> salesLedgerProductDtoIPage = salesLedgerProductMapper.listPagePurchaseLedger(page, salesLedgerProduct);
        salesLedgerProductDtoIPage.getRecords().forEach(item -> {
            // 判断状态
            if(item.getTaxInclusiveTotalPrice().compareTo(item.getTicketsTotal()) == 0){
                item.setStatusName("已完成付款");
            }else{
                item.setStatusName("未完成付款");
            }
        });
        return salesLedgerProductDtoIPage;
    }
    /**
@@ -311,4 +346,69 @@
            throw new RuntimeException("动态更新主表金额失败", e);
        }
    }
    @Override
    public R judgmentInventory(SalesLedgerProduct salesLedgerProduct) {
        TechnologyRouting routing = technologyRoutingMapper.selectOne(
                new QueryWrapper<TechnologyRouting>().lambda()
                        .eq(TechnologyRouting::getProductModelId, salesLedgerProduct.getProductModelId())
                        .orderByDesc(TechnologyRouting::getCreateTime)
                        .last("LIMIT 1"));
        if (routing == null) {
            return R.fail("请先设置工艺路线");
        }
        List<TechnologyBomStructureVo> structureList = technologyBomStructureMapper.listByBomId(routing.getBomId().longValue());
        if (structureList == null || structureList.isEmpty()) {
            return R.fail("请先设置产品结构");
        }
        int count = 0;
        StringBuilder stringBuffer = new StringBuilder();
        for (TechnologyBomStructureVo structure : structureList) {
            if (structure.getParentId() == null) {
                continue;
            }
            StockInventory stockInventory = stockInventoryMapper.selectOne(
                    new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, structure.getProductModelId()));
            if (stockInventory == null) {
                count++;
                stringBuffer.append(structure.getProductName()).append("-").append(structure.getModel()).append("库存不足").append(System.lineSeparator());
                continue;
            }
            BigDecimal required = salesLedgerProduct.getQuantity().multiply(structure.getUnitQuantity() == null ? BigDecimal.ZERO : structure.getUnitQuantity());
            BigDecimal remain = stockInventory.getQualitity()
                    .subtract(stockInventory.getLockedQuantity())
                    .subtract(required)
                    .divide(BigDecimal.ONE, 2, RoundingMode.CEILING);
            if (remain.compareTo(BigDecimal.ZERO) < 0) {
                count++;
                stringBuffer.append(structure.getProductName())
                        .append("-")
                        .append(structure.getModel())
                        .append("库存不足,少")
                        .append(remain.abs())
                        .append(System.lineSeparator());
            }
        }
        if (count > 0) {
            return R.fail(stringBuffer.toString());
        }
        return R.ok();
    }
    private String generateNextPlanNo(String datePrefix) {
        QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("mps_no", "JH" + datePrefix);
        queryWrapper.orderByDesc("mps_no");
        queryWrapper.last("LIMIT 1");
        ProductionPlan latestPlan = productionPlanMapper.selectOne(queryWrapper);
        int sequence = 1;
        if (latestPlan != null && latestPlan.getMpsNo() != null && !latestPlan.getMpsNo().isEmpty()) {
            String sequenceStr = latestPlan.getMpsNo().substring(("JH" + datePrefix).length());
            try {
                sequence = Integer.parseInt(sequenceStr) + 1;
            } catch (NumberFormatException e) {
                sequence = 1;
            }
        }
        return "JH" + datePrefix + String.format("%04d", sequence);
    }
}