liyong
11 小时以前 ee359b8abc3708e9ed4796cede561685a54aafba
refactor(stock): 库存代码迁移
已添加38个文件
已修改9个文件
2531 ■■■■■ 文件已修改
src/main/java/com/ruoyi/basic/controller/EnumController.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/utils/EnumUtils.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/BaseEnum.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/EnumUtil.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockInRecordController.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInventory.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockUninventory.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInRecordService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInventoryService.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockOutRecordService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockUninventoryService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInRecordMapper.xml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockOutRecordMapper.xml 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockUninventoryMapper.xml 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/EnumController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.basic.controller;
import com.ruoyi.basic.utils.EnumUtils;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.web.domain.R;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@Api(tags = "枚举接口")
@RequestMapping("/basic/enum")
public class EnumController {
    //获取库存枚举
    @GetMapping("/stockRecordType")
    @Anonymous
    public R  getStockRecordTypeEnum(){
        List<Map<String, Object>> list = EnumUtil.toList(StockQualifiedRecordTypeEnum.class);
        return R.ok(list);
    }
    @GetMapping("/{className}")
    @Anonymous
    public R test(@PathVariable String className){
        return R.ok(EnumUtils.getOptions("com.ruoyi.common.enums." + className));
    }
}
src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.basic.dto;
import lombok.Data;
@Data
public class SelectOptionDTO<T> {
    private T value;
    private String label;
    public SelectOptionDTO(T code, String value) {
        this.value = code;
        this.label = value;
    }
}
src/main/java/com/ruoyi/basic/utils/EnumUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.basic.utils;
import com.ruoyi.basic.dto.SelectOptionDTO;
import com.ruoyi.common.enums.BaseEnum;
import java.util.ArrayList;
import java.util.List;
public class EnumUtils {
    public static List<SelectOptionDTO<?>> getOptions(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return getOptionsByClass(clazz);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    @SuppressWarnings("unchecked")
    private static List<SelectOptionDTO<?>> getOptionsByClass(Class<?> clazz) {
        if (!clazz.isEnum()) {
            throw new RuntimeException("不是枚举:" + clazz.getName());
        }
        if (!BaseEnum.class.isAssignableFrom(clazz)) {
            throw new RuntimeException("未实现 BaseEnum:" + clazz.getName());
        }
        return (List<SelectOptionDTO<?>>) getOptionsInternal((Class) clazz);
    }
    private static <T> List<SelectOptionDTO<T>> getOptionsInternal(
            Class<? extends BaseEnum<T>> clazz) {
        Enum<?>[] enums = (Enum<?>[]) clazz.getEnumConstants();
        List<SelectOptionDTO<T>> list = new ArrayList<>();
        for (Enum<?> e : enums) {
            BaseEnum<T> option = (BaseEnum<T>) e;
            list.add(new SelectOptionDTO<>(
                    option.getCode(),
                    option.getValue()
            ));
        }
        return list;
    }
}
src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java
@@ -35,5 +35,9 @@
        IGNORE_TABLES.add("sys_job_log");
        IGNORE_TABLES.add("gen_table");
        IGNORE_TABLES.add("gen_table_column");
        IGNORE_TABLES.add("stock_in_record");
        IGNORE_TABLES.add("stock_inventory");
        IGNORE_TABLES.add("stock_out_record");
        IGNORE_TABLES.add("stock_uninventory");
    }
}
src/main/java/com/ruoyi/common/enums/BaseEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.common.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
public interface BaseEnum<T> {
    @JsonValue
    T getCode();
    String getValue();
    /**
     * é€šç”¨é™æ€å·¥å…·æ–¹æ³•(可被所有枚举调用)
     */
    /**
     * é€šç”¨é™æ€å·¥å…·æ–¹æ³•:支持从 Integer æˆ– String ç±»åž‹çš„ Code è¿›è¡Œååºåˆ—化
     */
    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    static <E extends Enum<E> & BaseEnum<?>> E fromCode(Class<E> enumClass, Object code) {
        if (code == null) {
            return null;
        }
        // ç›®æ ‡ Code çš„æ•´æ•°å€¼
        Integer targetCode = null;
        if (code instanceof Integer) {
            // 1. å¦‚果传入的是数字 (Integer)
            targetCode = (Integer) code;
        } else if (code instanceof String) {
            // 2. å¦‚果传入的是字符串 ("1")
            try {
                // å°è¯•将字符串转换为整数
                targetCode = Integer.valueOf((String) code);
            } catch (NumberFormatException e) {
                // å¦‚果字符串不是有效的数字(例如 "Unknown"),则 targetCode ä¿æŒä¸º null
                // æ‚¨ä¹Ÿå¯ä»¥åœ¨è¿™é‡Œè®°å½•日志或执行其他错误处理
                // System.err.println("无法将字符串 Code è½¬æ¢ä¸ºæ•°å­—: " + code);
                return null; // æˆ–者在找不到匹配的情况下返回 null
            }
        }
        // else if (code instanceof Long) { ... æ‚¨ä¹Ÿå¯ä»¥æ·»åР坹 Long ç±»åž‹çš„æ”¯æŒ }
        if (targetCode == null) {
            return null;
        }
        // ä½¿ç”¨èŽ·å–åˆ°çš„æ•´æ•°å€¼è¿›è¡ŒæŸ¥æ‰¾
        for (E e : enumClass.getEnumConstants()) {
            if (e.getCode().equals(targetCode)) {
                return e;
            }
        }
        return null;
    }
}
src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.enums;
import lombok.Getter;
@Getter
public enum StockQualifiedRecordTypeEnum implements BaseEnum<String> {
    CUSTOMIZATION_STOCK_IN("0", "合格自定义入库"),
    CUSTOMIZATION_STOCK_OUT("1", "合格自定义出库"),
    PRODUCTION_REPORT_STOCK_IN("2", "生产报工-入库"),
    PRODUCTION_REPORT_STOCK_OUT("3", "生产报工-出库"),
    DEFECTIVE_PASS("6", "不合格处理-让步放行"),
    PURCHASE_STOCK_IN("7", "采购-入库"),
    SALE_STOCK_OUT("8", "销售-出库"),
    QUALITYINSPECT_STOCK_IN("11", "质检-合格入库"),
    SALE_SHIP_STOCK_OUT("13", "销售-发货出库");
    private final String code;
    private final String value;
    StockQualifiedRecordTypeEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }
    }
src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.common.enums;
import lombok.Getter;
@Getter
public enum StockUnQualifiedRecordTypeEnum implements BaseEnum<String> {
    PRODUCTION_SCRAP("4", "生产报工-报废"),
    DEFECTIVE_SCRAP("5", "不合格处理-报废"),
    CUSTOMIZATION_UNSTOCK_IN("9", "不合格自定义入库"),
    CUSTOMIZATION_UNSTOCK_OUT("10", "不合格自定义出库"),
    QUALITYINSPECT_UNSTOCK_IN("12", "质检-不合格入库");
    private final String code;
    private final String value;
    StockUnQualifiedRecordTypeEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }
}
src/main/java/com/ruoyi/common/utils/EnumUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.enums.BaseEnum;
import java.util.*;
import java.util.stream.Collectors;
/**
 * é€šç”¨æžšä¸¾å·¥å…·ç±»
 * é€‚用于所有实现 BaseEnum æŽ¥å£çš„æžšä¸¾
 *
 * @author Bu
 */
public class EnumUtil {
    /**
     * æ ¹æ® code èŽ·å–æžšä¸¾å®žä¾‹
     */
    public static <E extends Enum<E> & BaseEnum> E fromCode(Class<E> enumClass, int code) {
        for (E e : enumClass.getEnumConstants()) {
            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的 code: " + code + " å¯¹äºŽæžšä¸¾ " + enumClass.getSimpleName());
    }
    public static <E extends Enum<E> & BaseEnum> E fromCodeHasNull(Class<E> enumClass, int code) {
        for (E e : enumClass.getEnumConstants()) {
            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
                return e;
            }
        }
        return null;
    }
    public static <E extends Enum<E> & BaseEnum> E fromCodeHasDefault(Class<E> enumClass, int code,E defaultE) {
        for (E e : enumClass.getEnumConstants()) {
            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
                return e;
            }
        }
        return defaultE;
    }
    /**
     * æ ¹æ® value èŽ·å–æžšä¸¾å®žä¾‹
     */
    public static <E extends Enum<E> & BaseEnum> E fromValue(Class<E> enumClass, String value) {
        for (E e : enumClass.getEnumConstants()) {
            if (Objects.equals(e.getValue(), value)) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的 value: " + value + " å¯¹äºŽæžšä¸¾ " + enumClass.getSimpleName());
    }
    /**
     * èŽ·å–æ‰€æœ‰æžšä¸¾é¡¹åˆ—è¡¨
     */
    public static <E extends Enum<E> & BaseEnum> List<E> listAll(Class<E> enumClass) {
        return Arrays.asList(enumClass.getEnumConstants());
    }
    /**
     * è½¬ä¸º List<Map>(适合返回给前端)
     * æ ¼å¼: [{ "code": 1, "value": "绞线" }, ...]
     */
    public static <E extends Enum<E> & BaseEnum> List<Map<String, Object>> toList(Class<E> enumClass) {
        return Arrays.stream(enumClass.getEnumConstants())
                .map(e -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("key", e.getCode().toString());
                    map.put("value", e.getValue());
                    return map;
                })
                .collect(Collectors.toList());
    }
//    /**
//     * è½¬ä¸º Map<Integer, String>
//     * æ ¼å¼: { 1: "绞线", 0: "拉丝" }
//     */
//    public static <E extends Enum<E> & BaseEnum> Map<Integer, String> toMap(Class<E> enumClass) {
//        return Arrays.stream(enumClass.getEnumConstants())
//                .collect(Collectors.toMap((BaseEnum::getCode, BaseEnum::getValue));
//    }
//
//
//    public static void main(String[] args) {
//        // âœ… 1. æ ¹æ® code èŽ·å–æžšä¸¾
//        StockRecordTypeEnum status = EnumUtil.fromCode(StockRecordTypeEnum.class, 1);
//        System.out.println(status.getValue()); // ç»žçº¿
//
//        // âœ… 2. è½¬æˆ list (适合前端)
//        System.out.println(EnumUtil.toList(StockRecordTypeEnum.class));
//        // è¾“出: [{code=1, value=绞线}, {code=0, value=拉丝}]
//
//        // âœ… 3. è½¬æˆ map
//        System.out.println(EnumUtil.toMap(StockRecordTypeEnum.class));
//        // è¾“出: {1=绞线, 0=拉丝}
//    }
}
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -1,11 +1,19 @@
package com.ruoyi.procurementrecord.utils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.stock.service.StockUninventoryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -14,6 +22,9 @@
public class StockUtils {
    private final ProcurementRecordOutMapper procurementRecordOutMapper;
    private final ProcurementRecordMapper procurementRecordMapper;
    private final StockUninventoryService stockUninventoryService;
    private final StockInventoryService stockInventoryService;
    private final StockInRecordService stockInRecordService;
    // èŽ·å–å•†å“å…¥åº“æ•°é‡,出库数量,剩余库存
    public Map<String, BigDecimal> getStockQuantity(Long productModelId) {
@@ -29,4 +40,77 @@
        stockMap.put("stockQuantity", stockQuantity);
        return stockMap;
    }
    /**
     * ä¸åˆæ ¼å…¥åº“
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void addUnStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
        stockUninventoryDto.setRecordId(recordId);
        stockUninventoryDto.setRecordType(String.valueOf(recordType));
        stockUninventoryDto.setQualitity(quantity);
        stockUninventoryDto.setProductModelId(productModelId);
        stockUninventoryService.addStockUninventory(stockUninventoryDto);
    }
    /**
     * ä¸åˆæ ¼å‡ºåº“
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void subtractUnStock(Long productModelId, BigDecimal quantity, Integer recordType,Long recordId) {
        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
        stockUninventoryDto.setRecordId(recordId);
        stockUninventoryDto.setRecordType(String.valueOf(recordType));
        stockUninventoryDto.setQualitity(quantity);
        stockUninventoryDto.setProductModelId(productModelId);
        stockUninventoryService.subtractStockUninventory(stockUninventoryDto);
    }
    /**
     * åˆæ ¼å…¥åº“
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void addStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
        StockInventoryDto stockInventoryDto = new StockInventoryDto();
        stockInventoryDto.setRecordId(recordId);
        stockInventoryDto.setRecordType(String.valueOf(recordType));
        stockInventoryDto.setQualitity(quantity);
        stockInventoryDto.setProductModelId(productModelId);
        stockInventoryService.addstockInventory(stockInventoryDto);
    }
    /**
     * åˆæ ¼å‡ºåº“
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void substractStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
        StockInventoryDto stockInventoryDto = new StockInventoryDto();
        stockInventoryDto.setRecordId(recordId);
        stockInventoryDto.setRecordType(String.valueOf(recordType));
        stockInventoryDto.setQualitity(quantity);
        stockInventoryDto.setProductModelId(productModelId);
        stockInventoryService.subtractStockInventory(stockInventoryDto);
    }
    //不合格库存删除
    public void deleteStockRecord(Long recordId, String recordType) {
        StockInRecord one = stockInRecordService.getOne(new QueryWrapper<StockInRecord>()
                .lambda().eq(StockInRecord::getRecordId, recordId)
                .eq(StockInRecord::getRecordType, recordType));
        stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -13,6 +13,7 @@
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
@@ -158,33 +159,14 @@
        for (ProductStructureDto productStructureDto : productStructureDtos) {
            ProductModel productModel1 = productModelMapper.selectById(productStructureDto.getProductModelId());
            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()+"产品的"+productModel1.getModel() + "的规格库存为0");
            }
            if (stockQuantity.compareTo(productStructureDto.getUnitQuantity().multiply(dto.getQuantity())) < 0) {
                throw new RuntimeException(product.getProductName()+"产品的"+productModel1.getModel() + "的规格库存不足");
            }
            ProductionProductInput productionProductInput = new ProductionProductInput();
            productionProductInput.setProductModelId(productStructureDto.getProductModelId());
            productionProductInput.setQuantity(productStructureDto.getUnitQuantity().multiply(dto.getQuantity()));
            productionProductInput.setProductMainId(productionProductMain.getId());
            productionProductInputMapper.insert(productionProductInput);
            //对应的库存出库
            DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
            LocalDate now = LocalDate.now();
            ProcurementRecordOut procurementRecordOut1 = procurementRecordOutMapper.selectCode(dateFormat.format(now));
            Long aLong = procurementRecordOut1 == null ? 1L : Long.valueOf(procurementRecordOut1.getCode().split("LS" + dateFormat.format(now))[1]);
            ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
                    .procurementRecordStorageId(0)
                    .code("LS" + dateFormat.format(now) + String.format("%03d", aLong + 1))
                    .salesLedgerProductId(productionProductMain.getId())//关联报工产出
                    .inboundBatches(aLong.equals(0L) ? "第1批次" : "第" + (aLong + 1) + "批次")
                    .inboundNum(productionProductInput.getQuantity())
                    .type(4)
                    .createBy(user.getNickName())
                    .productModelId(productModel1.getId());
            procurementRecordOutMapper.insert(procurementRecordOut.build());
            stockUtils.substractStock(productStructureDto.getProductModelId(), productStructureDto.getUnitQuantity().multiply(dto.getQuantity()), StockQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId());
        }
        /*新增报工产出表*/
        ProductionProductOutput productionProductOutput = new ProductionProductOutput();
@@ -293,6 +275,7 @@
            productOrderMapper.updateById(productOrder);
        }
        /*删除产出*/
        //删除质检
        qualityInspectMapper.selectList(
                new LambdaQueryWrapper<QualityInspect>()
@@ -309,6 +292,8 @@
        /*删除投入*/
        procurementRecordOutMapper.delete(new LambdaQueryWrapper<ProcurementRecordOut>()
                .eq(ProcurementRecordOut::getSalesLedgerProductId, productionProductMain.getId()));
        //删除出库记录
        stockUtils.deleteStockRecord(productionProductMain.getId(), StockQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
        productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productionProductMain.getId()));
        // åˆ é™¤ä¸»è¡¨
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -10,6 +10,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
import com.ruoyi.common.utils.SecurityUtils;
@@ -19,6 +20,7 @@
import com.ruoyi.procurementrecord.dto.ProcurementAddDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.quality.dto.QualityInspectDto;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityInspectParamMapper;
@@ -58,6 +60,7 @@
@Transactional(rollbackFor = Exception.class)
public class QualityInspectServiceImpl extends ServiceImpl<QualityInspectMapper, QualityInspect> implements IQualityInspectService {
    private final StockUtils stockUtils;
    private QualityInspectMapper qualityInspectMapper;
    private IQualityInspectParamService qualityInspectParamService;
@@ -109,83 +112,8 @@
            qualityUnqualified.setDefectivePhenomena(text + "这些指标中存在不合格");//不合格现象
            qualityUnqualifiedMapper.insert(qualityUnqualified);
        } else {
            if (qualityInspect.getInspectType() == 0) {
                if ("合格".equals(qualityInspect.getCheckResult())) {
                    ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
                    procurementRecordOutAdd.setType(1);
                    procurementRecordOutAdd.setTypeName("采购原材料检验合格入库");
                    procurementRecordOutAdd.setNickName(loginUser.getNickName());
                    procurementRecordOutAdd.setPurchaseLedgerId(Math.toIntExact(qualityInspect.getPurchaseLedgerId()));
                    if (qualityInspect.getPurchaseLedgerId() == null) {
                        throw new BaseException("请选择采购单");
                    }
                    SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(qualityInspect.getPurchaseLedgerId());
                    ArrayList<Details> detailss = new ArrayList<>();
                    Details details = new Details();
                    details.setId(Math.toIntExact(salesLedgerProduct.getId()));
                    details.setInboundQuantity(qualityInspect.getQuantity());
                    details.setWarnNum(salesLedgerProduct.getWarnNum());
                    details.setUnitPrice(salesLedgerProduct.getTaxInclusiveUnitPrice());
                    details.setTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice());
                    details.setProductModelId(qualityInspect.getProductModelId());
                    detailss.add(details);
                    procurementRecordOutAdd.setDetails(detailss);
                    procurementRecordOutAdd.setQualityInspectId(qualityInspect.getId());
                    procurementRecordService.add(procurementRecordOutAdd);
                }
            } else if (qualityInspect.getInspectType() == 1) {
                //查询UnitPrice/TotalPrice
                ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
                procurementRecordOutAdd.setType(2);
                procurementRecordOutAdd.setTypeName("生产过程检验合格入库");
                procurementRecordOutAdd.setNickName(loginUser.getNickName());
                List<Details> details = new ArrayList<>();
                Details details1 = new Details();
                details1.setInboundQuantity(qualityInspect.getQuantity());
                details1.setProductModelId(qualityInspect.getProductModelId());
                procurementRecordOutAdd.setDetails(details);
                ProcurementRecordStorage.ProcurementRecordStorageBuilder procurementRecordBuilder = ProcurementRecordStorage.builder()
                        .salesLedgerProductId(0)
                        .inboundBatches("生产半成品入库")
                        .inboundNum(details1.getInboundQuantity())
                        .type(2)
                        .warnNum(new BigDecimal(0))
                        .unitPrice(new BigDecimal(0))
                        .totalPrice(new BigDecimal(0))
                        .createTime(LocalDateTime.now())
                        .createUser(loginUser.getUserId())
                        .updateTime(LocalDateTime.now())
                        .updateUser(loginUser.getUserId())
                        .createBy(procurementRecordOutAdd.getNickName())
                        .productModelId(details1.getProductModelId())
                        .qualityInspectId(qualityInspect.getId());
                procurementRecordService.save(procurementRecordBuilder.build());
            } else if (qualityInspect.getInspectType() == 2) {
                //查询UnitPrice/TotalPrice
                SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectSalesLedgerProductByMainId(qualityInspect.getProductMainId());
                ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
                procurementRecordOutAdd.setType(2);
                procurementRecordOutAdd.setTypeName("生产出厂检验合格入库");
                procurementRecordOutAdd.setNickName(loginUser.getNickName());
                List<Details> details = new ArrayList<>();
                Details details1 = new Details();
                details1.setInboundQuantity(qualityInspect.getQuantity());
                details1.setId(Math.toIntExact(salesLedgerProduct.getId()));
                details1.setUnitPrice(salesLedgerProduct.getTaxInclusiveUnitPrice());
                details1.setTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice());
                details1.setProductModelId(salesLedgerProduct.getProductModelId());
                details.add(details1);
                procurementRecordOutAdd.setDetails(details);
                procurementRecordOutAdd.setQualityInspectId(qualityInspect.getId());
                procurementRecordService.add(procurementRecordOutAdd);
            }
           stockUtils.addStock(inspect.getProductModelId(), inspect.getQuantity(), StockQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), inspect.getId());
        }
        qualityInspect.setInspectState(1);//已提交
        return qualityInspectMapper.updateById(qualityInspect);
    }
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
@@ -52,9 +52,9 @@
    {
        List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
        list.forEach(item -> {
                if (item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
                    item.setFutureTickets(item.getQuantity());
                }
            if (item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
                item.setFutureTickets(item.getQuantity());
            }
            if (item.getFutureTicketsAmount().compareTo(BigDecimal.ZERO) == 0) {
                item.setFutureTicketsAmount(item.getTaxInclusiveTotalPrice());
            }
@@ -62,12 +62,14 @@
//            procurementDto.setSalesLedgerProductId(item.getId());
//            procurementDto.setProductCategory(item.getProductCategory());
//            IPage<ProcurementPageDtoCopy> result = procurementRecordService.listPageCopyByProduction(new Page<>(1,-1), procurementDto);
            BigDecimal stockQuantity = stockUtils.getStockQuantity(item.getProductModelId()).get("stockQuantity");
            if(stockQuantity != null) {
//            BigDecimal stockQuantity = stockUtils.getStockQuantity(item.getProductModelId()).get("stockQuantity");
//                ProcurementPageDtoCopy procurementDtoCopy = result.getRecords().get(0);
                if (item.getQuantity().compareTo(stockQuantity) <= 0 && item.getApproveStatus() == 0) {
            if (item.getApproveStatus() != 2) {
                if (item.getHasSufficientStock() == 0) {
                    item.setApproveStatus(0);
                }else {
                    item.setApproveStatus(1);
                    salesLedgerProductService.updateById(item);
                }
            }
        });
src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.math.LongMath;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.controller.BaseController;
@@ -14,6 +15,7 @@
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.mapper.ShipmentApprovalMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
@@ -50,6 +52,8 @@
    private ProcurementRecordOutService procurementRecordOutService;
    @Autowired
    private ProcurementRecordService procurementRecordStorageService;
    @Autowired
    private StockUtils stockUtils;
    @GetMapping("/listPage")
    @ApiOperation("发货审批列表")
@@ -100,17 +104,7 @@
//            }
            //  ç”Ÿæˆå‡ºåº“记录
            ProcurementRecordOutAdd procurementRecordOutAdd = new ProcurementRecordOutAdd();
//            procurementRecordOutAdd.setId(procurementRecordStorage.getId());
            procurementRecordOutAdd.setId(0);
            procurementRecordOutAdd.setProductModelId(salesLedgerProduct.getProductModelId());
            procurementRecordOutAdd.setSalesLedgerProductId((long) Math.toIntExact(salesLedgerProduct.getId()));
            procurementRecordOutAdd.setType(2);
            procurementRecordOutAdd.setUserId(Math.toIntExact(getUserId()));
            procurementRecordOutAdd.setQuantity(salesLedgerProduct.getQuantity().toPlainString());
            procurementRecordOutAdd.setTime(LocalDate.now().toString());
            procurementRecordOutService.stockout(procurementRecordOutAdd);
            stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shipmentApproval.getId());
        }
        return AjaxResult.success();
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -15,6 +15,9 @@
public interface SalesLedgerProductMapper extends MyBaseMapper<SalesLedgerProduct> {
    List<SalesLedgerProduct> selectSalesLedgerProductList(@Param("salesLedgerProduct") SalesLedgerProduct salesLedgerProduct);
    List<SalesLedgerProduct> selectProduct() ;
    SalesLedgerProduct selectSalesLedgerProductByMainId(@Param("productMainId") Long productMainId);
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -212,4 +212,8 @@
    
    @ApiModelProperty(value = "是否质检")
    private Boolean isChecked;
    @TableField(exist = false)
    private Integer hasSufficientStock;
}
src/main/java/com/ruoyi/stock/controller/StockInRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.stock.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.service.StockInRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
@Api(tags = "入库")
@RequestMapping("/stockInRecord")
public class StockInRecordController {
    @Autowired
    private StockInRecordService stockInRecordService;
    @GetMapping("/listPage")
    @Log(title = "生产入库-入库管理-列表", businessType = BusinessType.OTHER)
    @ApiOperation(value = "入库管理列表")
    public AjaxResult listPage(Page page, StockInRecordDto stockInRecordDto) {
        IPage<StockInRecordDto> result = stockInRecordService.listPage(page, stockInRecordDto);
        return AjaxResult.success(result);
    }
    @DeleteMapping("")
    @Log(title = "入库管理-删除入库", businessType = BusinessType.DELETE)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return AjaxResult.error("请选择至少一条数据");
        }
        return AjaxResult.success(stockInRecordService.batchDelete(ids));
    }
    @PostMapping("/exportStockInRecord")
    @ApiOperation("导出入库记录")
    public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
        stockInRecordService.exportStockInRecord(response,stockInRecordDto);
    }
}
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package com.ruoyi.stock.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
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.domain.R;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.service.StockInventoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
 * åº“存表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 04:16:36
 */
@RestController
@RequestMapping("/stockInventory")
@Api(tags = "库存表")
public class StockInventoryController {
    @Autowired
    private StockInventoryService stockInventoryService;
    @GetMapping("/pagestockInventory")
    @ApiOperation("分页查询库存")
    public R pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
        IPage<StockInventoryDto> stockInventoryDtoIPage = stockInventoryService.pagestockInventory(page, stockInventoryDto);
        return R.ok(stockInventoryDtoIPage);
    }
    @PostMapping("/addstockInventory")
    @ApiOperation("新增库存")
    public R addstockInventory(@RequestBody StockInventoryDto stockInventoryDto) {
        stockInventoryDto.setRecordType(String.valueOf(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode()));
        stockInventoryDto.setRecordId(0L);
        return R.ok(stockInventoryService.addstockInventory(stockInventoryDto));
    }
    @PostMapping("/subtractStockInventory")
    @ApiOperation("扣减库存")
    public R subtractStockInventory(@RequestBody StockInventoryDto stockInventoryDto) {
        stockInventoryDto.setRecordType(String.valueOf(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_OUT.getCode()));
        stockInventoryDto.setRecordId(0L);
        return R.ok(stockInventoryService.subtractStockInventory(stockInventoryDto));
    }
    @PostMapping("importStockInventory")
    @ApiOperation("导入库存")
    public R importStockInventory(MultipartFile file) {
        return stockInventoryService.importStockInventory(file);
    }
    @Log(title = "下载库存导入模板", businessType = BusinessType.EXPORT)
    @PostMapping("/downloadStockInventory")
    public void downloadStockInventory(HttpServletResponse response) {
        List<StockInventoryExportData> list = new ArrayList<>();
        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
        util.exportExcel(response, list, "库存模板");
    }
    @PostMapping("/exportStockInventory")
    @ApiOperation("导出库存")
    public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
        stockInventoryService.exportStockInventory(response, stockInventoryDto);
    }
    @GetMapping("stockInventoryPage")
    @ApiOperation("库存报表查询")
    public R stockInventoryPage(Page page, StockInventoryDto stockInventoryDto) {
        return R.ok(stockInventoryService.stockInventoryPage(stockInventoryDto,page));
    }
    @GetMapping("stockInAndOutRecord")
    @ApiOperation("统计各个产品的入库和出库记录")
    public R stockInAndOutRecord(StockInventoryDto stockInventoryDto,Page page) {
        return R.ok(stockInventoryService.stockInAndOutRecord(stockInventoryDto,page));
    }
    @PostMapping("/frozenStock")
    @ApiOperation("冻结库存")
    public R frozenStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockInventoryService.frozenStock(stockInventoryDto));
    }
    @PostMapping("/thawStock")
    @ApiOperation("解冻库存")
    public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockInventoryService.thawStock(stockInventoryDto));
    }
}
src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.stock.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.service.StockOutRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * å‡ºåº“记录表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 05:27:04
 */
@Api(tags = "出库")
@RestController
@RequestMapping("/stockOutRecord")
public class StockOutRecordController {
    @Autowired
    private StockOutRecordService stockOutRecordService;
    @GetMapping("/listPage")
    @Log(title = "生产出库-出库管理-列表", businessType = BusinessType.OTHER)
    @ApiOperation(value = "出库管理列表")
    public AjaxResult listPage(Page page, StockOutRecordDto stockOutRecordDto) {
        IPage<StockOutRecordDto> result = stockOutRecordService.listPage(page, stockOutRecordDto);
        return AjaxResult.success(result);
    }
    @PostMapping("")
    @Log(title = "出库管理-新增出库", businessType = BusinessType.INSERT)
    public AjaxResult add(@RequestBody StockOutRecordDto stockOutRecordDto) {
        return AjaxResult.success(stockOutRecordService.add(stockOutRecordDto));
    }
    @PutMapping("/{id}")
    @Log(title = "出库管理-更新出库", businessType = BusinessType.UPDATE)
    public AjaxResult update(@PathVariable("id") Long id, @RequestBody StockOutRecordDto stockOutRecordDto) {
        return AjaxResult.success(stockOutRecordService.update(id, stockOutRecordDto));
    }
    @DeleteMapping("")
    @Log(title = "出库管理-删除出库", businessType = BusinessType.DELETE)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return AjaxResult.error("请选择至少一条数据");
        }
        return AjaxResult.success(stockOutRecordService.batchDelete(ids));
    }
    @PostMapping("/exportStockOutRecord")
    @ApiOperation("导出出库记录")
    public void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto) {
        stockOutRecordService.exportStockOutRecord(response,stockOutRecordDto);
    }
}
src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.stock.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.service.StockUninventoryService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
 * ä¸åˆæ ¼åº“存表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-22 10:17:45
 */
@RestController
@RequestMapping("/stockUninventory")
public class StockUninventoryController {
    @Autowired
    private StockUninventoryService stockUninventoryService;
    @GetMapping("/pagestockUninventory")
    @ApiOperation("分页查询库存")
    public R pagestockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
        IPage<StockUninventoryDto> stockUninventoryDtoIPage = stockUninventoryService.pageStockUninventory(page, stockUninventoryDto);
        return R.ok(stockUninventoryDtoIPage);
    }
    @PostMapping("/addstockUninventory")
    @ApiOperation("新增库存")
    public R addstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()));
        stockUninventoryDto.setRecordId(0L);
        return R.ok(stockUninventoryService.addStockUninventory(stockUninventoryDto));
    }
    @PostMapping("/subtractstockUninventory")
    @ApiOperation("扣减库存")
    public R subtractstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode()));
        stockUninventoryDto.setRecordId(0L);
        return R.ok(stockUninventoryService.subtractStockUninventory(stockUninventoryDto));
    }
    @PostMapping("/exportStockUninventory")
    @ApiOperation("导出库存")
    public void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto) {
        stockUninventoryService.exportStockUninventory(response,stockUninventoryDto);
    }
    @PostMapping("/frozenStock")
    @ApiOperation("冻结库存")
    public R frozenStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockUninventoryService.frozenStock(stockInventoryDto));
    }
    @PostMapping("/thawStock")
    @ApiOperation("解冻库存")
    public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockUninventoryService.thawStock(stockInventoryDto));
    }
}
src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.stock.dto;
import com.ruoyi.stock.pojo.StockInRecord;
import lombok.Data;
@Data
public class StockInRecordDto extends StockInRecord {
    /**
     * äº§å“åç§°
     */
    private String productName;
    /**
     * äº§å“è§„æ ¼
     */
    private String model;
    /**
     * äº§å“å•位
     */
    private String unit;
    private String timeStr;
    private String createBy;
    //现存量
    private String currentStock;
}
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.stock.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.stock.pojo.StockInventory;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class StockInventoryDto extends StockInventory {
    private String productName;
    private String model;
    private String unit;
    //入库类型
    private String recordType;
    //入库类型对应的id
    private Long recordId;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate reportDate;
    //库存月报查询字段
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate startMonth;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate endMonth;
    private BigDecimal totalStockIn;
    private BigDecimal totalStockOut;
    private BigDecimal currentStock;
    private String  unLockedQuantity;
}
src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.stock.dto;
import com.ruoyi.stock.pojo.StockOutRecord;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StockOutRecordDto extends StockOutRecord {
    /**
     * äº§å“åç§°
     */
    private String productName;
    /**
     * äº§å“è§„æ ¼
     */
    private String model;
    /**
     * äº§å“å•位
     */
    private String unit;
    private String timeStr;
    private String createBy;
}
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.stock.dto;
import com.ruoyi.stock.pojo.StockUninventory;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class StockUninventoryDto  extends StockUninventory {
    private String productName;
    private String model;
    private String unit;
    //入库类型
    private String recordType;
    //入库类型对应的id
    private Long recordId;
    private BigDecimal unLockedQuantity;
}
src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.stock.execl;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
public class StockInRecordExportData {
    @Excel(name = "入库批次")
    private String inboundBatches;
    @Excel(name = "产品名称")
    private String productName;
    @Excel(name = "规格型号")
    private String model;
    @Excel(name = "单位")
    private String unit;
    @Excel(name = "入库来源")
    private String recordType;
    @Excel(name = "入库数量")
    private String stockInNum;
    @Excel(name = "入库时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @Excel(isExport = false)
    private String type;
}
src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.stock.execl;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class StockInventoryExportData {
    @Excel(name = "产品名称")
    private String productName;
    @Excel(name = "规格")
    private String model;
    @Excel(name = "单位")
    private String unit;
    @Excel(name = "库存数量")
    private BigDecimal qualitity;
    @Excel(name = "备注")
    private String remark;
//
//    @Excel(name = "最新更新时间")
//    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//    private LocalDateTime updateTime;
}
src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.stock.execl;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
public class StockOutRecordExportData {
    @Excel(name = "出库批次")
    private String outboundBatches;
    @Excel(name = "产品名称")
    private String productName;
    @Excel(name = "规格型号")
    private String model;
    @Excel(name = "单位")
    private String unit;
    @Excel(name = "出库来源")
    private String recordType;
    @Excel(name = "出库数量")
    private String stockInNum;
    @Excel(name = "出库时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @Excel(isExport = false)
    private String type;
}
src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.stock.mapper;
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.stock.dto.StockInRecordDto;
import com.ruoyi.stock.execl.StockInRecordExportData;
import com.ruoyi.stock.pojo.StockInRecord;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface StockInRecordMapper extends BaseMapper<StockInRecord> {
    IPage<StockInRecordDto> listPage(Page page, @Param("params") StockInRecordDto stockInRecordDto);
    List<StockInRecordExportData> listStockInRecordExportData(@Param("params") StockInRecordDto stockInRecordDto);
}
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.stock.mapper;
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.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.pojo.StockInventory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 * åº“存表 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 04:16:36
 */
@Mapper
public interface StockInventoryMapper extends BaseMapper<StockInventory> {
    IPage<StockInventoryDto> pagestockInventory(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
    int updateAddStockInventory(@Param("ew") StockInventoryDto stockInventoryDto);
    int updateSubtractStockInventory(@Param("ew") StockInventoryDto stockInventoryDto);
    List<StockInventoryExportData> listStockInventoryExportData(@Param("ew") StockInventoryDto stockInventoryDto);
    IPage<StockInRecordDto> stockInventoryPage(@Param("ew") StockInventoryDto stockInventoryDto, Page page);
    IPage<StockInventoryDto> stockInAndOutRecord(@Param("ew") StockInventoryDto stockInventoryDto, Page page);
}
src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.stock.mapper;
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.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.execl.StockOutRecordExportData;
import com.ruoyi.stock.pojo.StockOutRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 * å‡ºåº“记录表 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 05:27:04
 */
@Mapper
public interface StockOutRecordMapper extends BaseMapper<StockOutRecord> {
    IPage<StockOutRecordDto> listPage(Page page, @Param("params") StockOutRecordDto stockOutRecordDto);
    List<StockOutRecordExportData> listStockOutRecordExportData(@Param("params") StockOutRecordDto stockOutRecordDto);
}
src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.stock.mapper;
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.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.pojo.StockUninventory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 * ä¸åˆæ ¼åº“存表 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-22 10:17:45
 */
@Mapper
public interface StockUninventoryMapper extends BaseMapper<StockUninventory> {
    IPage<StockUninventoryDto> pageStockUninventory(Page page, @Param("ew") StockUninventoryDto stockUninventoryDto);
    int updateSubtractStockUnInventory(@Param("ew") StockUninventoryDto stockUninventoryDto);
    int updateAddStockUnInventory(@Param("ew") StockUninventoryDto stockUninventoryDto);
    List<StockInventoryExportData> listStockInventoryExportData(@Param("ew") StockUninventoryDto stockUninventoryDto);
}
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.stock.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("stock_in_record")
@ApiModel("入库管理")
public class StockInRecord {
    private static final long serialVersionUID = 1L;
    /**
     * åºå·
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "入库批次")
    private String inboundBatches;
    @ApiModelProperty(value = "入库数量")
    private BigDecimal stockInNum;
    @ApiModelProperty(value = "记录类型  æžšä¸¾")
    private String recordType;
    @ApiModelProperty(value = "记录ID  ")
    private Long recordId;
    @ApiModelProperty(value = "产品规格ID")
    private Long productModelId;
    @ApiModelProperty(value = "备注")
    private String remark;
    @ApiModelProperty(value = "类型  0合格入库 1不合格入库")
    private String type;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "修改用户")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
}
src/main/java/com/ruoyi/stock/pojo/StockInventory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.stock.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 * åº“存表
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 04:16:36
 */
@Getter
@Setter
@TableName("stock_inventory")
@ApiModel(value = "StockInventory对象", description = "库存表")
public class StockInventory implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("主键id")
    private Long id;
    @ApiModelProperty("规格id")
    @NotBlank(message = "不能为空")
    private Long productModelId;
    @ApiModelProperty("数量")
    private BigDecimal qualitity;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @ApiModelProperty("版本号")
    private Integer version;
    @ApiModelProperty("被订单锁定数量")
    private BigDecimal lockedQuantity;
    @ApiModelProperty("预警数量")
    private Integer warnNum;
    @ApiModelProperty("备注")
    private String remark;
}
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package com.ruoyi.stock.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 * å‡ºåº“记录表
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 05:27:04
 */
@Getter
@Setter
@TableName("stock_out_record")
@ApiModel(value = "StockOutRecord对象", description = "出库记录表")
public class StockOutRecord implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("入库批次")
    private String outboundBatches;
    @ApiModelProperty("入库数量")
    private BigDecimal stockOutNum;
    @ApiModelProperty("入库来源id")
    private Long recordId;
    @ApiModelProperty("入库类型")
    private String recordType;
    @ApiModelProperty("产品规格id")
    private Long productModelId;
    @ApiModelProperty("备注")
    private String remark;
    @ApiModelProperty("创建时间")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @ApiModelProperty("创建人")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty("更新人")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @ApiModelProperty(value = "类型  0合格入库 1不合格入库")
    private String type;
}
src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.ruoyi.stock.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 * ä¸åˆæ ¼åº“存表
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-22 10:17:45
 */
@Getter
@Setter
@TableName("stock_uninventory")
@ApiModel(value = "StockUninventory对象", description = "不合格库存表")
public class StockUninventory implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("主键id")
    private Long id;
    @ApiModelProperty("规格id")
    private Long productModelId;
    @ApiModelProperty("数量")
    private BigDecimal qualitity;
    @TableField(fill = FieldFill.INSERT)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
    @ApiModelProperty("版本号")
    private Integer version;
    @ApiModelProperty("备注")
    private String remark;
    @ApiModelProperty("被订单锁定数量")
    private BigDecimal lockedQuantity;
}
src/main/java/com/ruoyi/stock/service/StockInRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.stock.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.pojo.StockInRecord;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface StockInRecordService extends IService<StockInRecord> {
    IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto);
    int add(StockInRecordDto stockInRecordDto);
    int update(Long id, StockInRecordDto stockInRecordDto);
    int batchDelete(List<Long> ids);
    void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto);
}
src/main/java/com/ruoyi/stock/service/StockInventoryService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.stock.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.pojo.StockInventory;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
 * åº“存表 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 04:16:36
 */
public interface StockInventoryService extends IService<StockInventory> {
    IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto);
    Boolean addstockInventory(StockInventoryDto stockInventoryDto);
    Boolean subtractStockInventory(StockInventoryDto stockInventoryDto);
    R importStockInventory(MultipartFile file);
    void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto);
    IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto,Page page);
    IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto,Page page);
    Boolean frozenStock(StockInventoryDto stockInventoryDto);
    Boolean thawStock(StockInventoryDto stockInventoryDto);
}
src/main/java/com/ruoyi/stock/service/StockOutRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.stock.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.pojo.StockOutRecord;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * å‡ºåº“记录表 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 05:27:04
 */
public interface StockOutRecordService extends IService<StockOutRecord> {
    IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto);
    int add(StockOutRecordDto stockOutRecordDto);
    int update(Long id, StockOutRecordDto stockOutRecordDto);
    int batchDelete(List<Long> ids);
    void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto);
}
src/main/java/com/ruoyi/stock/service/StockUninventoryService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.stock.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.pojo.StockUninventory;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
 * ä¸åˆæ ¼åº“存表 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-22 10:17:45
 */
public interface StockUninventoryService extends IService<StockUninventory> {
    IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto);
    Integer addStockUninventory(StockUninventoryDto stockUninventoryDto);
    Integer subtractStockUninventory(StockUninventoryDto stockUninventoryDto);
    void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto);
    Boolean frozenStock(StockInventoryDto stockInventoryDto);
    Boolean thawStock(StockInventoryDto stockInventoryDto);
}
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package com.ruoyi.stock.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockInRecordExportData;
import com.ruoyi.stock.mapper.StockInRecordMapper;
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 lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Service
@AllArgsConstructor
public class StockInRecordServiceImpl extends ServiceImpl<StockInRecordMapper, StockInRecord> implements StockInRecordService {
    private StockInRecordMapper stockInRecordMapper;
    private StockInventoryMapper stockInventoryMapper;
    private StockUninventoryMapper stockUninventoryMapper;
    @Override
    public IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto) {
        return stockInRecordMapper.listPage(page, stockInRecordDto);
    }
    // æ–°å¢žå…¥åº“
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(StockInRecordDto stockInRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK");
        stockInRecordDto.setInboundBatches(no);
        StockInRecord stockInRecord = new StockInRecord();
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord);
        return stockInRecordMapper.insert(stockInRecord);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(Long id, StockInRecordDto stockInRecordDto) {
        // åˆ¤æ–­å¯¹è±¡æ˜¯å¦å­˜åœ¨
        StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
        if (stockInRecord == null){
            throw new BaseException("该入库记录不存在,无法更新!!!");
        }
        String[] ignoreProperties = {"id", "inbound_batches"};//排除id属性
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord, ignoreProperties);
        return stockInRecordMapper.updateById(stockInRecord);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDelete(List<Long> ids) {
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord.getType().equals("0")) {
                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockInRecord.getProductModelId()));
                if (stockInventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
                    stockInRecordDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
                }
            }else if (stockInRecord.getType().equals("1")) {
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId()));
                if (stockUninventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                }
            }
        }
        return stockInRecordMapper.deleteBatchIds(ids);
    }
    @Override
    public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
        List<StockInRecordExportData> list = stockInRecordMapper.listStockInRecordExportData(stockInRecordDto);
        for (StockInRecordExportData stockInRecordExportData : list) {
            if (stockInRecordExportData.getType().equals("0")) {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }else {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }
        }
        ExcelUtil<StockInRecordExportData> util = new ExcelUtil<>(StockInRecordExportData.class);
        util.exportExcel(response,list, "入库记录信息");
    }
}
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,197 @@
package com.ruoyi.stock.service.impl;
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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.stock.service.StockOutRecordService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
 * åº“存表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 04:16:36
 */
@Service
@AllArgsConstructor
public class StockInventoryServiceImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryService {
    private  StockInventoryMapper stockInventoryMapper;
    private StockInRecordService stockInRecordService;
    private StockOutRecordService stockOutRecordService;
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Override
    public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
        return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
    }
    //入库调用
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addstockInventory(StockInventoryDto stockInventoryDto) {
        //新增入库记录再添加库存
        StockInRecordDto stockInRecordDto = new StockInRecordDto();
        stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
        stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
        stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
        stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
        stockInRecordDto.setType("0");
        stockInRecordService.add(stockInRecordDto);
        //再进行新增库存数量库存
        //先查询库存表中的产品是否存在,不存在新增,存在更新
        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
        if (ObjectUtils.isEmpty(oldStockInventory)) {
            StockInventory newStockInventory = new StockInventory();
            newStockInventory.setProductModelId(stockInventoryDto.getProductModelId());
            newStockInventory.setQualitity(stockInventoryDto.getQualitity());
            newStockInventory.setVersion(1);
            newStockInventory.setRemark(stockInventoryDto.getRemark());
            stockInventoryMapper.insert(newStockInventory);
        }else {
             stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
        }
        return true;
    }
    //出库调用
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) {
            //  æ–°å¢žå‡ºåº“记录
        StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
        stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId());
        stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType());
        stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity());
        stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
        stockOutRecordDto.setType("0");
        stockOutRecordService.add(stockOutRecordDto);
        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
        if (stockInventoryDto.getQualitity().compareTo( oldStockInventory.getQualitity().subtract(oldStockInventory.getLockedQuantity()))>0) {
            throw new RuntimeException("库存不足无法出库");
        }
        if (ObjectUtils.isEmpty(oldStockInventory)) {
            throw new RuntimeException("产品库存不存在");
        }else {
            stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
        }
        return true;
    }
    @Override
    public R importStockInventory(MultipartFile file) {
        try {
            // æŸ¥è¯¢æ‰€æœ‰çš„产品
            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectProduct();
            ExcelUtil<StockInventoryExportData> util = new ExcelUtil<StockInventoryExportData>(StockInventoryExportData.class);
            List<StockInventoryExportData> list = util.importExcel(file.getInputStream());
            // è®°å½•未找到匹配项的数据
            List<String> unmatchedRecords = new ArrayList<>();
            list.forEach(dto -> {
                boolean matched = false;
                for (SalesLedgerProduct item : salesLedgerProducts) {
                    if (item.getProductCategory().equals(dto.getProductName()) &&
                            item.getSpecificationModel().equals(dto.getModel())) {
                        StockInventoryDto stockInventoryDto = new StockInventoryDto();
                        stockInventoryDto.setRecordId(0L);
                        stockInventoryDto.setRecordType(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode());
                        stockInventoryDto.setQualitity(dto.getQualitity());
                        stockInventoryDto.setRemark(dto.getRemark());
                        stockInventoryDto.setProductModelId(item.getProductModelId());
                        this.addstockInventory(stockInventoryDto);
                        matched = true;
                        break; // æ‰¾åˆ°åŒ¹é…é¡¹åŽè·³å‡ºå¾ªçޝ
                    }
                }
                if (!matched) {
                    // è®°å½•未匹配的数据
                    String unmatchedInfo = String.format("产品名称:%s,规格型号:%s",
                            dto.getProductName(), dto.getModel());
                    unmatchedRecords.add(unmatchedInfo);
                }
            });
            // æž„建返回信息
            StringBuilder message = new StringBuilder();
            if (!unmatchedRecords.isEmpty()) {
                message.append("以下产品未找到匹配项:\n");
                for (String record : unmatchedRecords) {
                    message.append(record).append("\n");
                }
                throw new RuntimeException(message.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("导入失败:" + e.getMessage());
        }
        return R.ok("导入成功");
    }
    @Override
    public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
        List<StockInventoryExportData> list = stockInventoryMapper.listStockInventoryExportData(stockInventoryDto);
        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
        util.exportExcel(response,list, "库存信息");
    }
    @Override
    public IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto, Page page) {
        return stockInventoryMapper.stockInventoryPage(stockInventoryDto,page);
    }
    @Override
    public IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto, Page page) {
        return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto,page);
    }
    @Override
    public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
        if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
            throw new RuntimeException("冻结数量不能超过库存数量");
        }
        if (ObjectUtils.isEmpty(stockInventory.getLockedQuantity())) {
            stockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
        }else {
            stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
        }
        return this.updateById(stockInventory);
    }
    @Override
    public Boolean thawStock(StockInventoryDto stockInventoryDto) {
        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
        if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
            throw new RuntimeException("解冻数量不能超过冻结数量");
        }
        stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
        return this.updateById(stockInventory);
    }
}
src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package com.ruoyi.stock.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockOutRecordExportData;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.pojo.StockOutRecord;
import com.ruoyi.stock.pojo.StockUninventory;
import com.ruoyi.stock.service.StockOutRecordService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * å‡ºåº“记录表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-21 05:27:04
 */
@Service
@AllArgsConstructor
public class StockOutRecordServiceImpl extends ServiceImpl<StockOutRecordMapper, StockOutRecord> implements StockOutRecordService {
    private StockOutRecordMapper stockOutRecordMapper;
    private StockInventoryMapper stockInventoryMapper;
    private StockUninventoryMapper stockUninventoryMapper;
    @Override
    public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) {
        return stockOutRecordMapper.listPage(page, stockOutRecordDto);
    }
    @Override
    public int add(StockOutRecordDto stockOutRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK");
        stockOutRecordDto.setOutboundBatches(no);
        StockInRecord stockInRecord = new StockInRecord();
        BeanUtils.copyProperties(stockOutRecordDto, stockInRecord);
        return stockOutRecordMapper.insert(stockOutRecordDto);
    }
    @Override
    public int update(Long id, StockOutRecordDto stockOutRecordDto) {
        // åˆ¤æ–­å¯¹è±¡æ˜¯å¦å­˜åœ¨
        StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
        if (stockOutRecord == null){
            throw new BaseException("该出库记录不存在,无法更新!!!");
        }
        String[] ignoreProperties = {"id", "outbound_batches"};//排除id属性
        BeanUtils.copyProperties(stockOutRecordDto, stockOutRecord, ignoreProperties);
        return stockOutRecordMapper.updateById(stockOutRecord);
    }
    @Override
    public int batchDelete(List<Long> ids) {
        for (Long id : ids) {
            StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
            if (stockOutRecord.getType().equals("0")) {
                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId()));
                if (stockInventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
                    stockInRecordDto.setQualitity(stockOutRecord.getStockOutNum());
                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
                }
            }else if (stockOutRecord.getType().equals("1")) {
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId()));
                if (stockUninventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
                    stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum());
                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                }
            }
        }
        return stockOutRecordMapper.deleteBatchIds(ids);
    }
    @Override
    public void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto) {
        List<StockOutRecordExportData> list = stockOutRecordMapper.listStockOutRecordExportData(stockOutRecordDto);
        for (StockOutRecordExportData stockInRecordExportData : list) {
            if (stockInRecordExportData.getType().equals("0")) {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }else {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }
        }
        ExcelUtil<StockOutRecordExportData> util = new ExcelUtil<>(StockOutRecordExportData.class);
        util.exportExcel(response,list, "出库记录信息");
    }
}
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,126 @@
package com.ruoyi.stock.service.impl;
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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
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 org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * ä¸åˆæ ¼åº“存表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-22 10:17:45
 */
@Service
@AllArgsConstructor
public class StockUninventoryServiceImpl extends ServiceImpl<StockUninventoryMapper, StockUninventory> implements StockUninventoryService {
    private StockUninventoryMapper stockUninventoryMapper;
    private StockOutRecordService stockOutRecordService;
    private StockInRecordService stockInRecordService;
    @Override
    public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
        return stockUninventoryMapper.pageStockUninventory(page, stockUninventoryDto);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer addStockUninventory(StockUninventoryDto stockUninventoryDto) {
        //新增入库记录再添加库存
        StockInRecordDto stockInRecordDto = new StockInRecordDto();
        stockInRecordDto.setRecordId(stockUninventoryDto.getRecordId());
        stockInRecordDto.setRecordType(stockUninventoryDto.getRecordType());
        stockInRecordDto.setStockInNum(stockUninventoryDto.getQualitity());
        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockInRecordDto.setType("1");
        stockInRecordService.add(stockInRecordDto);
        //再进行新增库存数量库存
        //先查询库存表中的产品是否存在,不存在新增,存在更新
        StockUninventory oldStockUnInventory = stockUninventoryMapper.selectOne(new QueryWrapper<StockUninventory>().lambda().eq(StockUninventory::getProductModelId, stockUninventoryDto.getProductModelId()));
        if (ObjectUtils.isEmpty(oldStockUnInventory)) {
            StockUninventory newStockUnInventory = new StockUninventory();
            newStockUnInventory.setProductModelId(stockUninventoryDto.getProductModelId());
            newStockUnInventory.setQualitity(stockUninventoryDto.getQualitity());
            newStockUnInventory.setVersion(1);
            newStockUnInventory.setRemark(stockUninventoryDto.getRemark());
            stockUninventoryMapper.insert(newStockUnInventory);
        }else {
            stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
        }
        return 1;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer subtractStockUninventory(StockUninventoryDto stockUninventoryDto) {
        //  æ–°å¢žå‡ºåº“记录
        StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
        stockOutRecordDto.setRecordId(stockUninventoryDto.getRecordId());
        stockOutRecordDto.setRecordType(stockUninventoryDto.getRecordType());
        stockOutRecordDto.setStockOutNum(stockUninventoryDto.getQualitity());
        stockOutRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockOutRecordDto.setType("1");
        stockOutRecordService.add(stockOutRecordDto);
        StockUninventory oldStockInventory = stockUninventoryMapper.selectOne(new QueryWrapper<StockUninventory>().lambda().eq(StockUninventory::getProductModelId, stockUninventoryDto.getProductModelId()));
        if (ObjectUtils.isEmpty(oldStockInventory)) {
            throw new RuntimeException("产品库存不存在");
        }else {
            stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
        }
        return 1;
    }
    @Override
    public void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto) {
        List<StockInventoryExportData> list = stockUninventoryMapper.listStockInventoryExportData(stockUninventoryDto);
        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
        util.exportExcel(response,list, "不合格库存信息");
    }
    @Override
    public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
        StockUninventory stockUninventory = stockUninventoryMapper.selectById(stockInventoryDto.getId());
        if (stockUninventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
            throw new RuntimeException("冻结数量不能超过库存数量");
        }
        if (ObjectUtils.isEmpty(stockUninventory.getLockedQuantity())) {
            stockUninventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
        }else {
            stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
        }
        return this.updateById(stockUninventory);
    }
    @Override
    public Boolean thawStock(StockInventoryDto stockInventoryDto) {
        StockUninventory stockUninventory = stockUninventoryMapper.selectById(stockInventoryDto.getId());
        if (stockUninventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
            throw new RuntimeException("解冻数量不能超过冻结数量");
        }
        stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
        return this.updateById(stockUninventory);
    }
}
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -8,17 +8,22 @@
        SELECT
        T1.*,
        t3.shipping_car_number,
        CASE
        WHEN (t2.qualitity - COALESCE(t2.locked_quantity, 0) )> T1.quantity THEN 1
        ELSE 0
        END as has_sufficient_stock,
        t3.shipping_date
        FROM
        sales_ledger_product T1
        left join shipping_info t3 on T1.id = t3.sales_ledger_product_id
        LEFT JOIN stock_inventory t2 ON T1.product_model_id = t2.product_model_id
        <where>
            1=1
            <if test="salesLedgerProduct.salesLedgerId != null and salesLedgerProduct.salesLedgerId != '' ">
                AND  T1.sales_ledger_id = #{salesLedgerProduct.salesLedgerId}
                AND T1.sales_ledger_id = #{salesLedgerProduct.salesLedgerId}
            </if>
            <if test="salesLedgerProduct.type != null and salesLedgerProduct.type != '' ">
                AND  T1.type = #{salesLedgerProduct.type}
                AND T1.type = #{salesLedgerProduct.type}
            </if>
        </where>
    </select>
@@ -33,4 +38,12 @@
    </select>
    <select id="selectProduct" resultType="com.ruoyi.sales.pojo.SalesLedgerProduct">
        select
            p.product_name as product_category,
            pm.model as specification_model,
            pm.id as product_model_id
        from product_model pm
                 left join product p on pm.product_id = p.id
    </select>
</mapper>
src/main/resources/mapper/stock/StockInRecordMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.stock.mapper.StockInRecordMapper">
    <select id="listPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
        SELECT
        sir.*,
        p.product_name as product_name,
        pm.model,
        pm.unit,
        u.nick_name as createBy
        FROM stock_in_record as sir
        LEFT JOIN product_model as pm on sir.product_model_id = pm.id
        LEFT JOIN product as p on pm.product_id = p.id
        LEFT JOIN sys_user as u on sir.create_user = u.user_id
        <where>
            <if test="params.timeStr != null and params.timeStr != ''">
                and sir.create_time like concat('%',#{params.timeStr},'%')
            </if>
            <if test="params.productName != null and params.productName != ''">
                and p.product_name like concat('%',#{params.productName},'%')
            </if>
            <if test="params.type != null and params.type != ''">
                and sir.type = #{params.type}
            </if>
            <if test="params.recordType != null and params.recordType != ''">
                and sir.record_type = #{params.recordType}
            </if>
        </where>
        order by sir.id desc
    </select>
    <select id="listStockInRecordExportData" resultType="com.ruoyi.stock.execl.StockInRecordExportData">
        SELECT
        sir.*,
        p.product_name as product_name,
        pm.model,
        pm.unit,
        u.nick_name as createBy
        FROM stock_in_record as sir
        LEFT JOIN product_model as pm on sir.product_model_id = pm.id
        LEFT JOIN product as p on pm.product_id = p.id
        LEFT JOIN sys_user as u on sir.create_user = u.user_id
        <where>
            <if test="params.timeStr != null and params.timeStr != ''">
                and sir.create_time like concat('%',#{params.timeStr},'%')
            </if>
            <if test="params.productName != null and params.productName != ''">
                and p.product_name like concat('%',#{params.productName},'%')
            </if>
            <if test="params.type != null and params.type != ''">
                and sir.type = #{params.type}
            </if>
            <if test="params.recordType != null and params.recordType != ''">
                and sir.record_type = #{params.recordType}
            </if>
        </where>
        order by sir.id desc
    </select>
</mapper>
src/main/resources/mapper/stock/StockInventoryMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.stock.mapper.StockInventoryMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockInventory">
        <result column="id" property="id"/>
        <result column="product_model_id" property="productModelId"/>
        <result column="qualitity" property="qualitity"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="version" property="version"/>
        <result column="locked_quantity" property="lockedQuantity"/>
        <result column="warn_num" property="warnNum"/>
    </resultMap>
    <update id="updateAddStockInventory">
        update stock_inventory
        <set>
            <if test="ew.qualitity != null">
                qualitity = qualitity + #{ew.qualitity},
            </if>
            <if test="ew.version != null">
                version = version + 1,
            </if>
            <if test="ew.remark != null and ew.remark !=''">
                remark = #{ew.remark},
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId}
    </update>
    <update id="updateSubtractStockInventory">
        update stock_inventory
        <set>
            <if test="ew.qualitity != null">
                qualitity = qualitity - #{ew.qualitity},
            </if>
            <if test="ew.version != null">
                version = version + 1,
            </if>
            <if test="ew.remark != null and ew.remark !=''">
                remark = #{ew.remark},
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
    </update>
    <select id="pagestockInventory" resultType="com.ruoyi.stock.dto.StockInventoryDto">
        select si.id,
        si.qualitity,
        COALESCE(si.locked_quantity, 0) as locked_quantity,
        si.product_model_id,
        si.create_time,
        si.update_time,
        si.warn_num,
        si.version,
        (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
        pm.model,
        pm.unit,
        p.product_name
        from stock_inventory si
        left join product_model pm on si.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        where 1 = 1
        <if test="ew.productName != null and ew.productName !=''">
            and p.product_name like concat('%',#{ew.productName},'%')
        </if>
    </select>
    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
        select si.qualitity,
        pm.model,
        pm.unit,
        p.product_name,
        si.remark,
        si.update_time
        from stock_inventory si
        left join product_model pm on si.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        where 1 = 1
        <if test="ew.productName != null and ew.productName !=''">
            and p.product_name like concat('%',#{ew.productName},'%')
        </if>
    </select>
    <select id="stockInventoryPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
        select sir.*,si.qualitity,
        pm.model,
        pm.unit,
        p.product_name
        from
        stock_in_record sir
        left join stock_inventory si on sir.product_model_id = si.product_model_id
        left join product_model pm on sir.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        <where>
            <if test="ew.reportDate != null">
                and sir.create_time >= #{ew.reportDate}
                and sir.create_time &lt; DATE_ADD(#{ew.reportDate}, INTERVAL 1 DAY)
            </if>
            <if test="ew.startMonth != null">
                and sir.create_time &gt;= #{ew.startMonth}
            </if>
            <if test="ew.endMonth != null">
                and sir.create_time &lt;= #{ew.endMonth}
            </if>
        </where>
    </select>
    <select id="stockInAndOutRecord" resultType="com.ruoyi.stock.dto.StockInventoryDto">
        SELECT
        pm.model,
        pm.unit,
        p.product_name,
        MAX(current_inventory) as current_stock,
        SUM(CASE WHEN record_type = 'in' THEN amount ELSE 0 END) as total_stock_in,
        SUM(CASE WHEN record_type = 'out' THEN amount ELSE 0 END) as total_stock_out
        FROM (
        SELECT
        product_model_id,
        SUM(qualitity) as current_inventory,
        0 as amount,
        '' as record_type
        FROM stock_inventory
        GROUP BY product_model_id
        UNION ALL
        SELECT
        product_model_id,
        0 as current_inventory,
        SUM(stock_in_num) as amount,
        'in' as record_type
        FROM stock_in_record
        <where>
            type = 0
            <if test="ew.startMonth != null">
                and stock_in_record.create_time &gt;= #{ew.startMonth}
            </if>
            <if test="ew.endMonth != null">
                and stock_in_record.create_time &lt;= #{ew.endMonth}
            </if>
        </where>
        GROUP BY product_model_id
        UNION ALL
        SELECT
        product_model_id,
        0 as current_inventory,
        SUM(stock_out_num) as amount,
        'out' as record_type
        FROM stock_out_record
        <where>
            type = 0
            <if test="ew.startMonth != null">
                and stock_out_record.create_time &gt;= #{ew.startMonth}
            </if>
            <if test="ew.endMonth != null">
                and stock_out_record.create_time &lt;= #{ew.endMonth}
            </if>
        </where>
        GROUP BY product_model_id
        ) combined_data
        LEFT JOIN product_model pm ON pm.id = combined_data.product_model_id
        LEFT JOIN product p ON p.id = pm.product_id
        <where>
            <if test="ew.productName != null and ew.productName !=''">
                and p.product_name like concat('%',#{ew.productName},'%')
            </if>
            <if test="ew.model != null and ew.model !=''">
                and pm.model like concat('%',#{ew.model},'%')
            </if>
        </where>
        GROUP BY
        pm.model,
        pm.unit,
        p.product_name
    </select>
</mapper>
src/main/resources/mapper/stock/StockOutRecordMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.stock.mapper.StockOutRecordMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockOutRecord">
        <id column="id" property="id" />
        <result column="outbound_batches" property="outboundBatches" />
        <result column="stock_out_num" property="stockOutNum" />
        <result column="record_id" property="recordId" />
        <result column="record_type" property="recordType" />
        <result column="product_model_id" property="productModelId" />
        <result column="remark" property="remark" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="create_user" property="createUser" />
        <result column="update_user" property="updateUser" />
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.stock.dto.StockOutRecordDto">
        SELECT
        sor.*,
        p.product_name as productName,
        pm.model,
        pm.unit,
        u.nick_name as createBy
        FROM stock_out_record as sor
        LEFT JOIN product_model as pm on sor.product_model_id = pm.id
        LEFT JOIN product as p on pm.product_id = p.id
        LEFT JOIN sys_user as u on sor.create_user = u.user_id
        <where>
            <if test="params.timeStr != null and params.timeStr != ''">
                and sor.create_time like concat('%',#{params.timeStr},'%')
            </if>
            <if test="params.productName != null and params.productName != ''">
                and p.product_name like concat('%',#{params.productName},'%')
            </if>
            <if test="params.type != null and params.type != ''">
                and sor.type = #{params.type}
            </if>
            <if test="params.recordType != null and params.recordType != ''">
                and sor.record_type = #{params.recordType}
            </if>
        </where>
        order by sor.id desc
    </select>
    <select id="listStockOutRecordExportData" resultType="com.ruoyi.stock.execl.StockOutRecordExportData">
        SELECT
        sor.*,
        p.product_name as productName,
        pm.model,
        pm.unit,
        u.nick_name as createBy
        FROM stock_out_record as sor
        LEFT JOIN product_model as pm on sor.product_model_id = pm.id
        LEFT JOIN product as p on pm.product_id = p.id
        LEFT JOIN sys_user as u on sor.create_user = u.user_id
        <where>
            <if test="params.timeStr != null and params.timeStr != ''">
                and sor.create_time like concat('%',#{params.timeStr},'%')
            </if>
            <if test="params.productName != null and params.productName != ''">
                and p.product_name like concat('%',#{params.productName},'%')
            </if>
            <if test="params.type != null and params.type != ''">
                and sor.type = #{params.type}
            </if>
            <if test="params.recordType != null and params.recordType != ''">
                and sor.record_type = #{params.recordType}
            </if>
        </where>
        order by sor.id desc
    </select>
</mapper>
src/main/resources/mapper/stock/StockUninventoryMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.stock.mapper.StockUninventoryMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockUninventory">
        <result column="id" property="id" />
        <result column="product_model_id" property="productModelId" />
        <result column="qualitity" property="qualitity" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="version" property="version" />
    </resultMap>
    <update id="updateSubtractStockUnInventory">
        update stock_uninventory
        <set>
            <if test="ew.qualitity != null">
                qualitity = qualitity - #{ew.qualitity},
            </if>
            <if test="ew.version != null">
                version = version + 1,
            </if>
            <if test="ew.remark != null and ew.remark !=''">
                remark = #{ew.remark},
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
    </update>
    <update id="updateAddStockUnInventory">
        update stock_uninventory
        <set>
            <if test="ew.qualitity != null">
                qualitity = qualitity + #{ew.qualitity},
            </if>
            <if test="ew.version != null">
                version = version + 1,
            </if>
            <if test="ew.remark != null and ew.remark !=''">
                remark = #{ew.remark},
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId}
    </update>
    <select id="pageStockUninventory" resultType="com.ruoyi.stock.dto.StockUninventoryDto">
        select su.id,
        su.qualitity,
        COALESCE(su.locked_quantity, 0) as locked_quantity,
        su.product_model_id,
        su.create_time,
        su.update_time,
        su.version,
        su.update_time,
        (su.qualitity - COALESCE(su.locked_quantity, 0)) as un_locked_quantity,
        pm.model,
        pm.unit,
        p.product_name
        from stock_uninventory su
        left join product_model pm on su.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        where 1 = 1
        <if test="ew.productName != null and ew.productName !=''">
            and p.product_name like concat('%',#{ew.productName},'%')
        </if>
    </select>
    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
        select su.*,
        pm.model,
        pm.unit,
        p.product_name
        from stock_uninventory su
        left join product_model pm on su.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        where 1 = 1
        <if test="ew.productName != null and ew.productName !=''">
            and p.product_name like concat('%',#{ew.productName},'%')
        </if>
    </select>
</mapper>