zss
2 天以前 9468068e746f25fc2d4bd618b9a07166f269e32a
feat(stock): 不合格库存批次号自动生成及使用
已修改7个文件
152 ■■■■■ 文件已修改
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java
@@ -1,6 +1,7 @@
package com.ruoyi.procurementrecord.bean.dto;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@@ -17,6 +18,7 @@
    //未退货数量
    private BigDecimal unQuantity;
    //总退货数量
    private BigDecimal totalReturnNum;
    // 退货总价
@@ -24,4 +26,13 @@
    // 退货总价
    private BigDecimal taxInclusiveUnitPrice;
    @Schema(description = "出库单号")
    private String outboundBatches;
    @Schema(description = "批次号")
    private String batchNo;
    @Schema(description = "发货出库数量")
    private BigDecimal stockOutNum;
}
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java
@@ -11,6 +11,9 @@
    @Schema(description = "出库单id")
    private Long id;
    @Schema(description = "产品规格id")
    private Long productModelId;
    @Schema(description = "产品大类")
    private String productCategory;
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -21,7 +21,6 @@
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.ShippingInfoService;
import lombok.RequiredArgsConstructor;
@@ -91,8 +90,6 @@
    @Override
    public ShippingInfoVo getReturnManagementDtoByShippingIdId(Long shippingId) {
        ShippingInfo byId = shippingInfoService.getById(shippingId);
        SalesLedger salesLedger = salesLedgerMapper.selectById(byId.getSalesLedgerId());
//        BeanUtils.copyProperties(salesLedger, salesLedgerDto);
        ShippingInfoVo shippingInfoVo = new ShippingInfoVo();
        shippingInfoVo.setShippingInfo(byId);
        List<ShippingProductVo> shippingProductVos = shippingInfoService.getReturnManagementDtoById(byId.getId());
@@ -105,6 +102,7 @@
        ReturnManagement byId = this.getById(returnManagementId);
        List<ReturnSaleProductDto> list = returnSaleProductService.listReturnSaleProduct(returnManagementId);
        byId.setStatus(1);
        byId.setSettler(SecurityUtils.getLoginUser().getNickName());
        updateById(byId);
        SalesRefundAmountOrderDto salesRefundAmountOrder = new SalesRefundAmountOrderDto();
        salesRefundAmountOrder.setReturnManagementId(returnManagementId);
@@ -117,11 +115,11 @@
            salesRefundAmountOrder.setRefundedAmount(new BigDecimal(0));
            // 是否有质量问题
            if (returnSaleProduct.getIsQuality() == 1) {
                // 有质量问题,入不合格库
                stockUtils.addUnStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId());
                // 有质量问题,入不合格库(带批次)
                stockUtils.addUnStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
            }else{
                // 无质量问题,入合格库
                stockUtils.addStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId());
                // 无质量问题,入合格库(带批次)
                stockUtils.addStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
            }
        }
        salesRefundAmountOrder.setRefundAmount(bigDecimal);
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -49,6 +49,24 @@
    }
    /**
     * 不合格入库带批次号
     *
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void addUnStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo) {
        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
        stockUninventoryDto.setRecordId(recordId);
        stockUninventoryDto.setRecordType(String.valueOf(recordType));
        stockUninventoryDto.setQualitity(quantity);
        stockUninventoryDto.setProductModelId(productModelId);
        stockUninventoryDto.setBatchNo(batchNo);
        stockUninventoryService.addStockInRecordOnly(stockUninventoryDto);
    }
    /**
     * 不合格出库
     *
     * @param productModelId
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -4,8 +4,12 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -14,21 +18,22 @@
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockUnInventoryExportData;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.pojo.StockUninventory;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.service.StockOutRecordService;
import com.ruoyi.stock.service.StockUninventoryService;
import lombok.AllArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
@@ -47,6 +52,8 @@
    private final StockUninventoryMapper stockUninventoryMapper;
    private final StockOutRecordService stockOutRecordService;
    private final StockInRecordService stockInRecordService;
    private final ProductModelMapper productModelMapper;
    private final StockInventoryMapper stockInventoryMapper;
    @Override
    public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
@@ -120,7 +127,11 @@
        stockInRecordDto.setRecordId(stockUninventoryDto.getRecordId());
        stockInRecordDto.setRecordType(stockUninventoryDto.getRecordType());
        stockInRecordDto.setStockInNum(stockUninventoryDto.getQualitity());
        stockInRecordDto.setBatchNo(stockUninventoryDto.getBatchNo());
        String batchNo = StringUtils.trim(stockUninventoryDto.getBatchNo());
        if (StringUtils.isEmpty(batchNo)) {
            batchNo = generateAutoBatchNo(stockUninventoryDto.getProductModelId());
        }
        stockInRecordDto.setBatchNo(batchNo);
        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockInRecordDto.setType("1");
        stockInRecordDto.setRemark(stockUninventoryDto.getRemark());
@@ -200,4 +211,80 @@
        stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
        return this.updateById(stockUninventory);
    }
    //规则生成:20260424-产品编号-001
    private String generateAutoBatchNo(Long productModelId) {
        if (productModelId == null) {
            throw new ServiceException("产品规格ID不能为空");
        }
        ProductModel productModel = productModelMapper.selectById(productModelId);
        if (productModel == null) {
            throw new ServiceException("产品规格不存在,ID=" + productModelId);
        }
        String productCode = StringUtils.trim(productModel.getProductCode());
        if (StringUtils.isEmpty(productCode)) {
            throw new ServiceException("产品规格未维护产品编码,ID=" + productModelId);
        }
        String dateText = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
        String prefix = dateText + "-" + productCode + "-";
        int maxSequence = resolveMaxSequence(prefix);
        int sequence = maxSequence + 1;
        while (sequence < 1_000_000) {
            String batchNo = prefix + String.format("%03d", sequence);
            if (!isBatchNoExists(batchNo)) {
                return batchNo;
            }
            sequence++;
        }
        throw new ServiceException("批号序号超出范围,请检查批号数据");
    }
    private int resolveMaxSequence(String prefix) {
        int maxSequence = 0;
        List<StockInventory> stockInventoryList = stockInventoryMapper.selectList(
                Wrappers.<StockInventory>lambdaQuery()
                        .select(StockInventory::getBatchNo)
                        .likeRight(StockInventory::getBatchNo, prefix));
        for (StockInventory stockInventory : stockInventoryList) {
            maxSequence = Math.max(maxSequence, parseSequence(stockInventory.getBatchNo(), prefix));
        }
        List<StockInRecord> stockInRecordList = stockInRecordService.list(
                Wrappers.<StockInRecord>lambdaQuery()
                        .select(StockInRecord::getBatchNo)
                        .likeRight(StockInRecord::getBatchNo, prefix));
        for (StockInRecord stockInRecord : stockInRecordList) {
            maxSequence = Math.max(maxSequence, parseSequence(stockInRecord.getBatchNo(), prefix));
        }
        return maxSequence;
    }
    private int parseSequence(String batchNo, String prefix) {
        if (StringUtils.isEmpty(batchNo) || StringUtils.isEmpty(prefix) || !batchNo.startsWith(prefix)) {
            return 0;
        }
        String sequenceText = batchNo.substring(prefix.length());
        if (StringUtils.isEmpty(sequenceText) || !sequenceText.matches("\\d+")) {
            return 0;
        }
        try {
            return Integer.parseInt(sequenceText);
        } catch (NumberFormatException ignored) {
            return 0;
        }
    }
    private boolean isBatchNoExists(String batchNo) {
        if (StringUtils.isEmpty(batchNo)) {
            return false;
        }
        Long inventoryCount = stockInventoryMapper.selectCount(
                Wrappers.<StockInventory>lambdaQuery().eq(StockInventory::getBatchNo, batchNo));
        if (inventoryCount != null && inventoryCount > 0) {
            return true;
        }
        return stockInRecordService.count(
                Wrappers.<StockInRecord>lambdaQuery().eq(StockInRecord::getBatchNo, batchNo)) > 0;
    }
}
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
@@ -16,6 +16,9 @@
               pm.model                                     as model,
               pm.unit                                      as unit,
               rsp.*,
            sor.outbound_batches,
            sor.stock_out_num,
            sor.batch_no,
               GREATEST(sor.stock_out_num - COALESCE(rsp.num, 0), 0) AS un_quantity,
               COALESCE(rs.total_return_num, 0)                             AS total_return_num
        FROM return_sale_product rsp
@@ -34,11 +37,15 @@
        where rm.id =#{returnManagementId}
    </select>
    <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto">
        select rsp.*,slp.tax_inclusive_unit_price ,slp.tax_inclusive_total_price*rsp.num as price
        select rsp.*,
               sor.batch_no,
               slp.tax_inclusive_unit_price ,
               slp.tax_inclusive_total_price*rsp.num as price
        from return_sale_product rsp
                 LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
                 LEFT JOIN shipping_info si ON si.id = rm.shipping_id
                 LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
                 LEFT JOIN stock_out_record sor ON rsp.stock_out_record_id = sor.id
        where rsp.return_management_id = #{returnManagementId}
    </select>
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -72,6 +72,7 @@
            slp.product_category,
            slp.specification_model,
            slp.unit,
            slp.product_model_id,
            sor.outbound_batches,
            sor.stock_out_num,
            sor.batch_no,
@@ -97,6 +98,7 @@
                si.id = #{shippingId}
            </if>
        </where>
        order by sor.id
    </select>
    <select id="getShippingInfoByCustomerName" resultType="com.ruoyi.sales.pojo.ShippingInfo">
        select * from shipping_info si