gongchunyi
19 小时以前 6b4cfc6f9d660b92be99ba4e3411a3267bc57155
feat: 销售/采购订单的扫码合格/不合格出入库功能
已添加2个文件
已修改19个文件
961 ■■■■■ 文件已修改
doc/河南鹤壁天沐钢化玻璃厂.sql 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockOutUnQualifiedRecordTypeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseScanStockDto.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 370 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesScanInboundDto.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockUninventoryMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ºÓÄϺױÚÌìãå¸Ö»¯²£Á§³§.sql
@@ -122,4 +122,10 @@
    MODIFY COLUMN `stock_out_num` decimal(16, 4) NULL DEFAULT NULL COMMENT '出库数量' AFTER `outbound_batches`,
    MODIFY COLUMN `record_id` int NULL DEFAULT NULL COMMENT '出库来源id' AFTER `stock_out_num`,
    ADD COLUMN `sales_ledger_id`         bigint NULL COMMENT '销售订单ID' AFTER `type`,
    ADD COLUMN `sales_ledger_product_id` bigint NULL COMMENT '销售订单产品ID' AFTER `sales_ledger_id`;
    ADD COLUMN `sales_ledger_product_id` bigint NULL COMMENT '销售订单产品ID' AFTER `sales_ledger_id`;
ALTER TABLE `sales_ledger_product`
    ADD COLUMN `stocked_quantity` decimal(18, 2) DEFAULT 0.00 COMMENT '已入库数量' AFTER `product_stock_status`;
ALTER TABLE `sales_ledger_product`
    ADD COLUMN `remaining_quantity` decimal(18, 2) DEFAULT '0.00' COMMENT '剩余待入库数量' AFTER `stocked_quantity`
src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
@@ -12,7 +12,9 @@
    QUALITYINSPECT_STOCK_IN("6", "质检-合格入库"),
    DEFECTIVE_PASS("11", "不合格-让步放行"),
    RETURN_HE_IN("14", "销售退货-合格入库"),
    SALE_STOCK_IN("15", "销售订单-合格入库");
    SALE_STOCK_IN("15", "销售订单-合格入库"),
    SALE_SCAN_STOCK_IN("17", "销售订单扫码-合格入库"),
    PURCHASE_SCAN_STOCK_IN("18", "采购订单扫码-合格入库");
    private final String code;
src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java
@@ -11,7 +11,9 @@
    PRODUCTION_SCRAP("5", "生产报工-报废"),
    CUSTOMIZATION_UNSTOCK_IN("9", "不合格自定义入库"),
    QUALITYINSPECT_UNSTOCK_IN("12", "质检-不合格入库"),
    RETURN_UNSTOCK_IN("15", "销售退货-不合格入库");
    RETURN_UNSTOCK_IN("15", "销售退货-不合格入库"),
    SALES_SCAN_UNSTOCK_IN("16", "销售订单扫码-不合格入库"),
    PURCHASE_SCAN_UNSTOCK_IN("19", "采购订单扫码-不合格入库");
    private final String code;
src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
@@ -9,7 +9,9 @@
    PRODUCTION_REPORT_STOCK_OUT("3", "生产报工-出库"),
    SALE_STOCK_OUT("8", "销售-出库"),
    PURCHASE_RETURN_STOCK_OUT("9", "采购退货"),
    SALE_SHIP_STOCK_OUT("13", "销售-发货出库");
    SALE_SHIP_STOCK_OUT("13", "销售-发货出库"),
    SALE_SCAN_STOCK_OUT("17", "销售订单扫码-合格出库"),
    PURCHASE_SCAN_STOCK_OUT("16", "采购订单扫码-合格出库");
    private final String code;
    private final String value;
src/main/java/com/ruoyi/common/enums/StockOutUnQualifiedRecordTypeEnum.java
@@ -7,7 +7,9 @@
public enum StockOutUnQualifiedRecordTypeEnum implements BaseEnum<String> {
    CUSTOMIZATION_UNSTOCK_OUT("10", "不合格自定义出库");
    CUSTOMIZATION_UNSTOCK_OUT("10", "不合格自定义出库"),
    SALE_SCAN_UNSTOCK_OUT("11", "销售订单扫码-不合格出库"),
    PURCHASE_SCAN_UNSTOCK_OUT("12", "采购订单扫码-不合格出库");
    private final String code;
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -2,13 +2,17 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
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.StockInRecordService;
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.stock.service.StockOutRecordService;
@@ -34,6 +38,52 @@
    private final StockOutRecordService stockOutRecordService;
    /**
     * åˆæ ¼åº“存出库前校验:按 stock_inventory å¯ç”¨æ•°é‡ï¼ˆæ•°é‡ âˆ’ å†»ç»“)
     */
    public void assertQualifiedAvailable(Long productModelId, BigDecimal outboundQty) {
        if (productModelId == null) {
            throw new ServiceException("出库失败,产品规格未维护");
        }
        if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        StockInventory inv = stockInventoryService.getOne(
                Wrappers.<StockInventory>lambdaQuery().eq(StockInventory::getProductModelId, productModelId));
        if (inv == null) {
            throw new ServiceException("出库失败,合格库中无该产品库存");
        }
        BigDecimal locked = inv.getLockedQuantity() == null ? BigDecimal.ZERO : inv.getLockedQuantity();
        BigDecimal qty = inv.getQualitity() == null ? BigDecimal.ZERO : inv.getQualitity();
        BigDecimal available = qty.subtract(locked);
        if (outboundQty.compareTo(available) > 0) {
            throw new ServiceException("出库失败,出库数量不能大于当前合格库存可用数量");
        }
    }
    /**
     * ä¸åˆæ ¼åº“存出库前校验:按 stock_uninventory å¯ç”¨æ•°é‡ï¼ˆæ•°é‡ âˆ’ å†»ç»“)
     */
    public void assertUnqualifiedAvailable(Long productModelId, BigDecimal outboundQty) {
        if (productModelId == null) {
            throw new ServiceException("出库失败,产品规格未维护");
        }
        if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        StockUninventory inv = stockUninventoryService.getOne(
                Wrappers.<StockUninventory>lambdaQuery().eq(StockUninventory::getProductModelId, productModelId));
        if (inv == null) {
            throw new ServiceException("出库失败,不合格库中无该产品库存");
        }
        BigDecimal locked = inv.getLockedQuantity() == null ? BigDecimal.ZERO : inv.getLockedQuantity();
        BigDecimal qty = inv.getQualitity() == null ? BigDecimal.ZERO : inv.getQualitity();
        BigDecimal available = qty.subtract(locked);
        if (outboundQty.compareTo(available) > 0) {
            throw new ServiceException("出库失败,出库数量不能大于当前不合格库存可用数量");
        }
    }
    /**
     * ä¸åˆæ ¼å…¥åº“
     *
     * @param productModelId
@@ -42,28 +92,41 @@
     * @param recordId
     */
    public void addUnStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) {
        addUnStock(null, null, productModelId, quantity, recordType, recordId);
    }
    /**
     * ä¸åˆæ ¼å…¥åº“(带销售订单关联,写入入库记录)
     */
    public void addUnStock(Long salesLedgerId, Long salesLedgerProductId, 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);
        stockUninventoryDto.setSalesLedgerId(salesLedgerId);
        stockUninventoryDto.setSalesLedgerProductId(salesLedgerProductId);
        stockUninventoryService.addStockUninventory(stockUninventoryDto);
    }
    /**
     * ä¸åˆæ ¼å‡ºåº“
     *
     * @param productModelId
     * @param quantity
     * @param recordType
     * @param recordId
     */
    public void subtractUnStock(Long productModelId, BigDecimal quantity, Integer recordType, Long recordId) {
    public void subtractUnStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) {
        subtractUnStock(null, null, productModelId, quantity, recordType, recordId);
    }
    /**
     * ä¸åˆæ ¼å‡ºåº“(可带销售订单关联写入出库记录)
     */
    public void subtractUnStock(Long salesLedgerId, Long salesLedgerProductId, Long productModelId, BigDecimal quantity, String recordType, Long recordId) {
        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
        stockUninventoryDto.setRecordId(recordId);
        stockUninventoryDto.setRecordType(String.valueOf(recordType));
        stockUninventoryDto.setRecordType(recordType);
        stockUninventoryDto.setQualitity(quantity);
        stockUninventoryDto.setProductModelId(productModelId);
        stockUninventoryDto.setSalesLedgerId(salesLedgerId);
        stockUninventoryDto.setSalesLedgerProductId(salesLedgerProductId);
        stockUninventoryService.subtractStockUninventory(stockUninventoryDto);
    }
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java
@@ -11,6 +11,7 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseScanStockDto;
import com.ruoyi.purchase.mapper.PurchaseLedgerTemplateMapper;
import com.ruoyi.purchase.mapper.SalesLedgerProductTemplateMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
@@ -268,4 +269,32 @@
    public AjaxResult createPurchaseNo() {
        return AjaxResult.success("生成成功",purchaseLedgerService.getPurchaseNo());
    }
    @PostMapping("/scanInbound")
    @ApiOperation("采购订单扫码-合格入库")
    public AjaxResult scanInbound(@RequestBody PurchaseScanStockDto dto) {
        purchaseLedgerService.scanInbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanInboundUnqualified")
    @ApiOperation("采购订单扫码-不合格入库")
    public AjaxResult scanInboundUnqualified(@RequestBody PurchaseScanStockDto dto) {
        purchaseLedgerService.scanInboundUnqualified(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanOutbound")
    @ApiOperation("采购订单扫码-合格出库")
    public AjaxResult scanOutbound(@RequestBody PurchaseScanStockDto dto) {
        purchaseLedgerService.scanOutbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanOutboundUnqualified")
    @ApiOperation("采购订单扫码-不合格出库")
    public AjaxResult scanOutboundUnqualified(@RequestBody PurchaseScanStockDto dto) {
        purchaseLedgerService.scanOutboundUnqualified(dto);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/purchase/dto/PurchaseScanStockDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.purchase.dto;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * é‡‡è´­è®¢å•扫码入出库(合格/不合格)Dto
 */
@Data
@ApiModel(value = "PurchaseScanStockDto", description = "采购订单扫码入出库(合格/不合格)")
public class PurchaseScanStockDto {
    @ApiModelProperty("采购订单Id")
    private Long purchaseLedgerId;
    @ApiModelProperty("采购产品行数据")
    private List<SalesLedgerProduct> salesLedgerProductList;
}
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java
@@ -5,11 +5,11 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseScanStockDto;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.sales.pojo.InvoiceRegistrationProduct;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
@@ -45,4 +45,12 @@
    AjaxResult importData(MultipartFile file);
    PurchaseLedgerDto getPurchaseByCode(PurchaseLedgerDto purchaseLedgerDto);
    void scanInbound(PurchaseScanStockDto dto);
    void scanInboundUnqualified(PurchaseScanStockDto dto);
    void scanOutbound(PurchaseScanStockDto dto);
    void scanOutboundUnqualified(PurchaseScanStockDto dto);
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -1,29 +1,28 @@
package com.ruoyi.purchase.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.pojo.AccountExpense;
import com.ruoyi.account.pojo.AccountIncome;
import com.ruoyi.account.service.AccountExpenseService;
import com.ruoyi.account.service.AccountIncomeService;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.approve.vo.ApproveProcessVO;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -32,26 +31,27 @@
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseLedgerImportDto;
import com.ruoyi.purchase.dto.PurchaseLedgerProductImportDto;
import com.ruoyi.purchase.dto.PurchaseScanStockDto;
import com.ruoyi.purchase.mapper.*;
import com.ruoyi.purchase.pojo.*;
import com.ruoyi.purchase.service.IPurchaseLedgerService;
import com.ruoyi.quality.mapper.*;
import com.ruoyi.quality.pojo.*;
import com.ruoyi.sales.dto.SalesLedgerImportDto;
import com.ruoyi.sales.dto.SalesLedgerProductImportDto;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.InvoiceRegistrationProduct;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import lombok.RequiredArgsConstructor;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.service.StockInventoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
@@ -78,6 +78,8 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.ruoyi.common.enums.SaleEnum.PURCHASE;
/**
 * é‡‡è´­å°è´¦Service业务层处理
 *
@@ -88,68 +90,72 @@
@Slf4j
public class PurchaseLedgerServiceImpl extends ServiceImpl<PurchaseLedgerMapper, PurchaseLedger> implements IPurchaseLedgerService {
    @Autowired
    private  AccountExpenseService accountExpenseService;
    private AccountExpenseService accountExpenseService;
    @Autowired
    private  PurchaseLedgerMapper purchaseLedgerMapper;
    private PurchaseLedgerMapper purchaseLedgerMapper;
    @Autowired
    private  SalesLedgerMapper salesLedgerMapper;
    private SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private  SalesLedgerProductMapper salesLedgerProductMapper;
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private  SysUserMapper userMapper;
    private SysUserMapper userMapper;
    @Autowired
    private  TempFileMapper tempFileMapper;
    private TempFileMapper tempFileMapper;
    @Autowired
    private  CommonFileMapper commonFileMapper;
    private CommonFileMapper commonFileMapper;
    @Autowired
    private  SupplierManageMapper supplierManageMapper;
    private SupplierManageMapper supplierManageMapper;
    @Autowired
    private  ProductMapper productMapper;
    private ProductMapper productMapper;
    @Autowired
    private  ProductModelMapper productModelMapper;
    private ProductModelMapper productModelMapper;
    @Autowired
    private  SysUserMapper sysUserMapper;
    private SysUserMapper sysUserMapper;
    @Autowired
    private  TicketRegistrationMapper ticketRegistrationMapper;
    private TicketRegistrationMapper ticketRegistrationMapper;
    @Autowired
    private  ProductRecordMapper productRecordMapper;
    private ProductRecordMapper productRecordMapper;
    @Autowired
    private  PaymentRegistrationMapper paymentRegistrationMapper;
    private PaymentRegistrationMapper paymentRegistrationMapper;
    @Autowired
    private  InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    @Autowired
    private  StringRedisTemplate redisTemplate;
    private StringRedisTemplate redisTemplate;
    @Autowired
    private  QualityInspectMapper qualityInspectMapper;
    private QualityInspectMapper qualityInspectMapper;
    @Autowired
    private  CommonFileServiceImpl commonFileService;
    private CommonFileServiceImpl commonFileService;
    @Autowired
    private  QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
    private QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
    @Autowired
    private  QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private QualityTestStandardParamMapper qualityTestStandardParamMapper;
    @Autowired
    private  QualityTestStandardMapper qualityTestStandardMapper;
    private QualityTestStandardMapper qualityTestStandardMapper;
    @Autowired
    private  QualityInspectParamMapper qualityInspectParamMapper;
    private QualityInspectParamMapper qualityInspectParamMapper;
    @Autowired
    private  ApproveProcessServiceImpl approveProcessService;
    private ApproveProcessServiceImpl approveProcessService;
    @Autowired
    private  ProcurementRecordMapper procurementRecordStorageMapper;
    private ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
    private  PurchaseLedgerTemplateMapper purchaseLedgerTemplateMapper;
    private PurchaseLedgerTemplateMapper purchaseLedgerTemplateMapper;
    @Autowired
    private  SalesLedgerProductTemplateMapper salesLedgerProductTemplateMapper;
    private SalesLedgerProductTemplateMapper salesLedgerProductTemplateMapper;
    @Autowired
    private StockInventoryService stockInventoryService;
    @Autowired
    private StockUtils stockUtils;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -159,7 +165,7 @@
        if (StringUtils.isNotBlank(purchaseLedger.getPurchaseContractNumber())) {
            queryWrapper.like(PurchaseLedger::getPurchaseContractNumber, purchaseLedger.getPurchaseContractNumber());
        }
        if(purchaseLedger.getSupplierId()!=null){
        if (purchaseLedger.getSupplierId() != null) {
            queryWrapper.eq(PurchaseLedger::getSupplierId, purchaseLedger.getSupplierId());
        }
        if (purchaseLedger.getApprovalStatus() != null) {
@@ -244,12 +250,12 @@
        qualityInspect.setUnit(saleProduct.getUnit());
        qualityInspect.setQuantity(saleProduct.getQuantity());
        qualityInspectMapper.insert(qualityInspect);
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0,null);
        if (qualityTestStandard.size()>0){
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null);
        if (qualityTestStandard.size() > 0) {
            qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
            qualityInspectMapper.updateById(qualityInspect);
            qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
                    .eq(QualityTestStandardParam::getTestStandardId,qualityTestStandard.get(0).getId()))
                            .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))
                    .forEach(qualityTestStandardParam -> {
                        QualityInspectParam param = new QualityInspectParam();
                        com.ruoyi.common.utils.bean.BeanUtils.copyProperties(qualityTestStandardParam, param);
@@ -316,6 +322,7 @@
        if (!updateList.isEmpty()) {
            for (SalesLedgerProduct product : updateList) {
                product.setType(type);
                product.fillRemainingQuantity();
                salesLedgerProductMapper.updateById(product);
            }
        }
@@ -330,6 +337,7 @@
                salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
                salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.setPendingTicketsTotal(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.fillRemainingQuantity();
                salesLedgerProductMapper.insert(salesLedgerProduct);
            }
        }
@@ -425,12 +433,12 @@
    @Transactional(rollbackFor = Exception.class)
    public int deletePurchaseLedgerByIds(Long[] ids) {
        if (ids == null || ids.length == 0) {
           throw new BaseException("请选中至少一条数据");
            throw new BaseException("请选中至少一条数据");
        }
        for (Long id : ids) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
            if (purchaseLedger.getApprovalStatus().equals(3)) {
                throw new BaseException(purchaseLedger.getPurchaseContractNumber()+"已经审批通过,不允许删除");
                throw new BaseException(purchaseLedger.getPurchaseContractNumber() + "已经审批通过,不允许删除");
            }
        }
        // æ‰¹é‡åˆ é™¤å…³è”的采购入库记录
@@ -453,11 +461,11 @@
        salesLedgerProductMapper.delete(queryWrapper);
        // æ‰¹é‡åˆ é™¤å…³è”的采购台账的来票登记
        LambdaQueryWrapper<TicketRegistration> ticketRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ticketRegistrationLambdaQueryWrapper.in(TicketRegistration::getPurchaseLedgerId,ids);
        ticketRegistrationLambdaQueryWrapper.in(TicketRegistration::getPurchaseLedgerId, ids);
        ticketRegistrationMapper.delete(ticketRegistrationLambdaQueryWrapper);
        // æ‰¹é‡åˆ é™¤å…³è”的采购台账的来票登记记录
        LambdaQueryWrapper<ProductRecord> productRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productRecordLambdaQueryWrapper.in(ProductRecord::getPurchaseLedgerId,ids);
        productRecordLambdaQueryWrapper.in(ProductRecord::getPurchaseLedgerId, ids);
        productRecordMapper.delete(productRecordLambdaQueryWrapper);
        // æ‰¹é‡åˆ é™¤ä»˜æ¬¾ç™»è®°
        LambdaQueryWrapper<PaymentRegistration> paymentRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -469,7 +477,7 @@
        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(materialInspectLambdaQueryWrapper);
        qualityInspects.stream().forEach(qualityInspect -> {
            if (ObjectUtils.isNotEmpty(qualityInspect.getInspectState())&&qualityInspect.getInspectState().equals(1)) {
            if (ObjectUtils.isNotEmpty(qualityInspect.getInspectState()) && qualityInspect.getInspectState().equals(1)) {
                throw new BaseException("已提交的检验单不能删除");
            }
        });
@@ -485,7 +493,7 @@
        // åˆ é™¤é‡‡è´­å®¡æ‰¹è®°å½•
        for (Long id : ids) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
            if(purchaseLedger != null){
            if (purchaseLedger != null) {
                ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                        .eq(ApproveProcess::getApproveType, 5)
                        .eq(ApproveProcess::getApproveDelete, 0)
@@ -517,11 +525,12 @@
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, purchaseLedgerDto.getType());
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        products.forEach(SalesLedgerProduct::fillRemainingQuantity);
        // 3.查询上传文件
        LambdaQueryWrapper<CommonFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>();
        salesLedgerFileWrapper.eq(CommonFile::getCommonId, purchaseLedger.getId())
                .eq(CommonFile::getType,FileNameType.PURCHASE.getValue());
                .eq(CommonFile::getType, FileNameType.PURCHASE.getValue());
        List<CommonFile> salesLedgerFiles = commonFileMapper.selectList(salesLedgerFileWrapper);
        // 4. è½¬æ¢ DTO
@@ -577,6 +586,7 @@
            product.setTicketsAmount(null);
            product.setTempFutureTickets(product.getFutureTickets());
            product.setTempFutureTicketsAmount(product.getFutureTicketsAmount());
            product.fillRemainingQuantity();
        });
        resultDto.setProductData(productList);
        return resultDto;
@@ -672,7 +682,7 @@
            // ä¾›åº”商数据
            List<SupplierManage> customers = supplierManageMapper.selectList(new LambdaQueryWrapper<SupplierManage>().in(SupplierManage::getSupplierName,
                    salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getSupplierName).collect(Collectors.toList())));
            List<Map<String,Object>> list = productModelMapper.getProductAndModelList();
            List<Map<String, Object>> list = productModelMapper.getProductAndModelList();
            // å½•入人数据
            List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
                    salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getRecorderName).collect(Collectors.toList())));
@@ -680,7 +690,7 @@
                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                        .eq(PurchaseLedger::getPurchaseContractNumber, salesLedgerImportDto.getPurchaseContractNumber())
                        .last("limit 1"));
                if(purchaseLedger != null){
                if (purchaseLedger != null) {
                    continue;
                }
                PurchaseLedger salesLedger = new PurchaseLedger();
@@ -707,12 +717,12 @@
                    throw new RuntimeException("采购单号:" + salesLedgerImportDto.getPurchaseContractNumber() + ",无对应产品数据!");
                salesLedger.setContractAmount(salesLedgerProductImportDtos.stream()
                        .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice)
                        .reduce(BigDecimal.ZERO,BigDecimal::add));
                        .reduce(BigDecimal.ZERO, BigDecimal::add));
                // é€šè¿‡é”€å”®å•号绑定销售
                SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                        .eq(SalesLedger::getSalesContractNo, salesLedger.getSalesContractNo())
                        .last("LIMIT 1"));
                if(salesLedger1 != null){
                if (salesLedger1 != null) {
                    salesLedger.setSalesLedgerId(salesLedger1.getId());
                }
                // é‡‡è´­å®¡æ ¸
@@ -753,13 +763,14 @@
                    salesLedgerProduct.setPendingTicketsTotal(salesLedgerProductImportDto.getTaxInclusiveTotalPrice());
                    // æ˜¯å¦è´¨æ£€åˆ¤æ–­
                    salesLedgerProduct.setIsChecked(salesLedgerProductImportDto.getIsChecked() == 1);
                    if(salesLedgerProductImportDto.getIsChecked() == 1){
                    if (salesLedgerProductImportDto.getIsChecked() == 1) {
                        addQualityInspect(salesLedger, salesLedgerProduct);
                    }
                    salesLedgerProduct.fillRemainingQuantity();
                    salesLedgerProductMapper.insert(salesLedgerProduct);
                }
                // é‡‡è´­å®¡æ ¸
                addApproveByPurchase(loginUser,salesLedger);
                addApproveByPurchase(loginUser, salesLedger);
            }
            return AjaxResult.success("导入成功");
@@ -784,6 +795,7 @@
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        products.forEach(SalesLedgerProduct::fillRemainingQuantity);
        // 4. è½¬æ¢ DTO
        PurchaseLedgerDto resultDto = new PurchaseLedgerDto();
@@ -795,7 +807,7 @@
        return resultDto;
    }
    public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) throws Exception {
    public void addApproveByPurchase(LoginUser loginUser, PurchaseLedger purchaseLedger) throws Exception {
        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
        approveProcessVO.setApproveType(5);
        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
@@ -806,6 +818,258 @@
        approveProcessService.addApprove(approveProcessVO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanInbound(PurchaseScanStockDto dto) {
        if (dto == null || dto.getPurchaseLedgerId() == null) {
            throw new ServiceException("采购入库失败,入库数据不能为空");
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(dto.getPurchaseLedgerId());
        if (purchaseLedger == null) {
            throw new ServiceException("入库失败,采购台账不存在");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("采购入库失败,入库产品不能为空");
        }
        int purchaseType = PURCHASE.getCode();
        Map<Long, BigDecimal> inboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct inbound : dto.getSalesLedgerProductList()) {
            if (inbound == null || inbound.getId() == null) {
                throw new ServiceException("入库失败,采购产品信息不完整");
            }
            BigDecimal inboundQty = inbound.getStockedQuantity();
            if (inboundQty == null) {
                throw new ServiceException("入库失败,入库数量不能为空");
            }
            if (inboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("入库失败,入库数量不能为负数");
            }
            inboundQtyByLineId.merge(inbound.getId(), inboundQty, BigDecimal::add);
        }
        Long purchaseId = purchaseLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : inboundQtyByLineId.entrySet()) {
            Long productLineId = entry.getKey();
            BigDecimal inboundThisLine = entry.getValue();
            if (inboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(productLineId);
            if (dbProduct == null) {
                throw new ServiceException("入库失败,采购产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), purchaseId) || !Objects.equals(dbProduct.getType(), purchaseType)) {
                throw new ServiceException("入库失败,产品与采购台账不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("入库失败,产品规格未维护,无法入库");
            }
            BigDecimal oldStocked = dbProduct.getStockedQuantity() == null ? BigDecimal.ZERO : dbProduct.getStockedQuantity();
            BigDecimal newStocked = oldStocked.add(inboundThisLine);
            StockInventoryDto stockInventoryDto = new StockInventoryDto();
            stockInventoryDto.setRecordId(dbProduct.getId());
            stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.PURCHASE_SCAN_STOCK_IN.getCode());
            stockInventoryDto.setQualitity(inboundThisLine);
            stockInventoryDto.setProductModelId(dbProduct.getProductModelId());
            stockInventoryDto.setSalesLedgerId(null);
            stockInventoryDto.setSalesLedgerProductId(dbProduct.getId());
            stockInventoryService.addstockInventory(stockInventoryDto);
            BigDecimal orderQty = dbProduct.getQuantity() == null ? BigDecimal.ZERO : dbProduct.getQuantity();
            int lineStockStatus;
            if (newStocked.compareTo(BigDecimal.ZERO) <= 0) {
                lineStockStatus = 0;
            } else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) {
                lineStockStatus = 1;
            } else {
                lineStockStatus = 2;
            }
            dbProduct.setStockedQuantity(newStocked);
            dbProduct.setProductStockStatus(lineStockStatus);
            dbProduct.fillRemainingQuantity();
            salesLedgerProductMapper.updateById(dbProduct);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanOutbound(PurchaseScanStockDto dto) {
        if (dto == null || dto.getPurchaseLedgerId() == null) {
            throw new ServiceException("采购出库失败,出库数据不能为空");
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(dto.getPurchaseLedgerId());
        if (purchaseLedger == null) {
            throw new ServiceException("出库失败,采购台账不存在");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("采购出库失败,出库产品不能为空");
        }
        int purchaseType = PURCHASE.getCode();
        Map<Long, BigDecimal> outboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct line : dto.getSalesLedgerProductList()) {
            if (line == null || line.getId() == null) {
                throw new ServiceException("出库失败,采购产品信息不完整");
            }
            BigDecimal outboundQty = line.getStockedQuantity();
            if (outboundQty == null) {
                throw new ServiceException("出库失败,出库数量不能为空");
            }
            if (outboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("出库失败,出库数量不能为负数");
            }
            outboundQtyByLineId.merge(line.getId(), outboundQty, BigDecimal::add);
        }
        Long purchaseId = purchaseLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : outboundQtyByLineId.entrySet()) {
            Long productLineId = entry.getKey();
            BigDecimal outboundThisLine = entry.getValue();
            if (outboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(productLineId);
            if (dbProduct == null) {
                throw new ServiceException("出库失败,采购产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), purchaseId) || !Objects.equals(dbProduct.getType(), purchaseType)) {
                throw new ServiceException("出库失败,产品与采购台账不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("出库失败,产品规格未维护,无法出库");
            }
            stockUtils.assertQualifiedAvailable(dbProduct.getProductModelId(), outboundThisLine);
            BigDecimal oldStocked = dbProduct.getStockedQuantity() == null ? BigDecimal.ZERO : dbProduct.getStockedQuantity();
            BigDecimal newStocked = oldStocked.subtract(outboundThisLine);
            if (newStocked.compareTo(BigDecimal.ZERO) < 0) {
                newStocked = BigDecimal.ZERO;
            }
            StockInventoryDto stockInventoryDto = new StockInventoryDto();
            stockInventoryDto.setRecordId(dbProduct.getId());
            stockInventoryDto.setRecordType(StockOutQualifiedRecordTypeEnum.PURCHASE_SCAN_STOCK_OUT.getCode());
            stockInventoryDto.setQualitity(outboundThisLine);
            stockInventoryDto.setProductModelId(dbProduct.getProductModelId());
            stockInventoryDto.setSalesLedgerId(null);
            stockInventoryDto.setSalesLedgerProductId(dbProduct.getId());
            stockInventoryService.subtractStockInventory(stockInventoryDto);
            BigDecimal orderQty = dbProduct.getQuantity() == null ? BigDecimal.ZERO : dbProduct.getQuantity();
            int lineStockStatus;
            if (newStocked.compareTo(BigDecimal.ZERO) <= 0) {
                lineStockStatus = 0;
            } else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) {
                lineStockStatus = 1;
            } else {
                lineStockStatus = 2;
            }
            dbProduct.setStockedQuantity(newStocked);
            dbProduct.setProductStockStatus(lineStockStatus);
            dbProduct.fillRemainingQuantity();
            salesLedgerProductMapper.updateById(dbProduct);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanInboundUnqualified(PurchaseScanStockDto dto) {
        if (dto == null || dto.getPurchaseLedgerId() == null) {
            throw new ServiceException("采购不合格入库失败,入库数据不能为空");
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(dto.getPurchaseLedgerId());
        if (purchaseLedger == null) {
            throw new ServiceException("不合格入库失败,采购台账不存在");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("采购不合格入库失败,入库产品不能为空");
        }
        int purchaseType = PURCHASE.getCode();
        Map<Long, BigDecimal> inboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct inbound : dto.getSalesLedgerProductList()) {
            if (inbound == null || inbound.getId() == null) {
                throw new ServiceException("不合格入库失败,采购产品信息不完整");
            }
            BigDecimal inboundQty = inbound.getStockedQuantity();
            if (inboundQty == null) {
                throw new ServiceException("不合格入库失败,入库数量不能为空");
            }
            if (inboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("不合格入库失败,入库数量不能为负数");
            }
            inboundQtyByLineId.merge(inbound.getId(), inboundQty, BigDecimal::add);
        }
        Long purchaseId = purchaseLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : inboundQtyByLineId.entrySet()) {
            Long productLineId = entry.getKey();
            BigDecimal inboundThisLine = entry.getValue();
            if (inboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(productLineId);
            if (dbProduct == null) {
                throw new ServiceException("不合格入库失败,采购产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), purchaseId) || !Objects.equals(dbProduct.getType(), purchaseType)) {
                throw new ServiceException("不合格入库失败,产品与采购台账不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("不合格入库失败,产品规格未维护,无法入库");
            }
            stockUtils.addUnStock(null, null, dbProduct.getProductModelId(), inboundThisLine,
                    StockInUnQualifiedRecordTypeEnum.PURCHASE_SCAN_UNSTOCK_IN.getCode(), dbProduct.getId());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanOutboundUnqualified(PurchaseScanStockDto dto) {
        if (dto == null || dto.getPurchaseLedgerId() == null) {
            throw new ServiceException("采购不合格出库失败,出库数据不能为空");
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(dto.getPurchaseLedgerId());
        if (purchaseLedger == null) {
            throw new ServiceException("不合格出库失败,采购台账不存在");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("采购不合格出库失败,出库产品不能为空");
        }
        int purchaseType = PURCHASE.getCode();
        Map<Long, BigDecimal> outboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct line : dto.getSalesLedgerProductList()) {
            if (line == null || line.getId() == null) {
                throw new ServiceException("不合格出库失败,采购产品信息不完整");
            }
            BigDecimal outboundQty = line.getStockedQuantity();
            if (outboundQty == null) {
                throw new ServiceException("不合格出库失败,出库数量不能为空");
            }
            if (outboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("不合格出库失败,出库数量不能为负数");
            }
            outboundQtyByLineId.merge(line.getId(), outboundQty, BigDecimal::add);
        }
        Long purchaseId = purchaseLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : outboundQtyByLineId.entrySet()) {
            Long productLineId = entry.getKey();
            BigDecimal outboundThisLine = entry.getValue();
            if (outboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(productLineId);
            if (dbProduct == null) {
                throw new ServiceException("不合格出库失败,采购产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), purchaseId) || !Objects.equals(dbProduct.getType(), purchaseType)) {
                throw new ServiceException("不合格出库失败,产品与采购台账不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("不合格出库失败,产品规格未维护,无法出库");
            }
            stockUtils.assertUnqualifiedAvailable(dbProduct.getProductModelId(), outboundThisLine);
            stockUtils.subtractUnStock(null, null, dbProduct.getProductModelId(), outboundThisLine,
                    StockOutUnQualifiedRecordTypeEnum.PURCHASE_SCAN_UNSTOCK_OUT.getCode(), dbProduct.getId());
        }
    }
    /**
     * ä¸‹åˆ’线命名转驼峰命名
     */
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -411,4 +411,32 @@
        return AjaxResult.success(list);
    }
    @PostMapping("/scanInbound")
    @ApiOperation("销售订单扫码-合格入库")
    public AjaxResult scanInbound(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanInbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanInboundUnqualified")
    @ApiOperation("销售订单扫码-不合格入库")
    public AjaxResult scanInboundUnqualified(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanInboundUnqualified(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanOutbound")
    @ApiOperation("销售订单扫码-合格出库")
    public AjaxResult scanOutbound(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanOutbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanOutboundUnqualified")
    @ApiOperation("销售订单扫码-不合格出库")
    public AjaxResult scanOutboundUnqualified(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanOutboundUnqualified(dto);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/sales/dto/SalesScanInboundDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.sales.dto;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * é”€å”®è®¢å•扫码入出库(合格/不合格)Dto
 */
@Data
@ApiModel(value = "SalesScanInboundDto", description = "销售订单扫码入出库(合格/不合格)")
public class SalesScanInboundDto {
    @ApiModelProperty("销售订单Id")
    private Long SalesLedgerId;
    @ApiModelProperty("销售产品行数据")
    private List<SalesLedgerProduct> salesLedgerProductList;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -72,8 +72,22 @@
     */
    @Excel(name = "数量")
    private BigDecimal quantity;
    /**
     * å·²å…¥åº“数量
     */
    private BigDecimal stockedQuantity;
    @Excel(name = "最低库存数量")
    private BigDecimal minStock;
    /**
     * å‰©ä½™å¾…入库数量(订单数量 âˆ’ åˆæ ¼å·²å…¥åº“数量,多入时记为 0)
     */
    @Excel(name = "剩余数量")
    @ApiModelProperty("剩余待入库数量(订单数量-合格已入库)")
    private BigDecimal remainingQuantity;
    /**
     * ç¨Žçއ
     */
@@ -293,9 +307,9 @@
    private String floorCode;
    /**
     * äº§å“å…¥åº“状态   0-未入库,1-已入库
     * äº§å“å…¥åº“状态:0-未入库,1-部分入库,2-已入库
     */
    @ApiModelProperty("产品入库状态")
    @ApiModelProperty("产品入库状态:0-未入库,1-部分入库,2-已入库")
    private Integer productStockStatus;
    @TableField(exist = false)
@@ -312,4 +326,12 @@
    // å¯ç”¨æ•°é‡  quantity - returnQuality
    @TableField(exist = false)
    private BigDecimal availableQuality;
    public void fillRemainingQuantity() {
        BigDecimal q = this.quantity == null ? BigDecimal.ZERO : this.quantity;
        BigDecimal stocked = this.stockedQuantity == null ? BigDecimal.ZERO : this.stockedQuantity;
        BigDecimal rem = q.subtract(stocked);
        this.remainingQuantity = rem.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : rem;
    }
}
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -70,4 +70,11 @@
    List<Customer> shippedCustomers();
    void scanInbound(SalesScanInboundDto dto);
    void scanInboundUnqualified(SalesScanInboundDto dto);
    void scanOutbound(SalesScanInboundDto dto);
    void scanOutboundUnqualified(SalesScanInboundDto dto);
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -100,7 +100,11 @@
    @Override
    public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
        return salesLedgerProductMapper.selectById(id);
        SalesLedgerProduct row = salesLedgerProductMapper.selectById(id);
        if (row != null) {
            row.fillRemainingQuantity();
        }
        return row;
    }
    @Override
@@ -201,6 +205,7 @@
            item.setReturnQuality(returnQuality);
            BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity();
            item.setAvailableQuality(quantity.subtract(returnQuality));
            item.fillRemainingQuantity();
            ProductModel productModel = finalProductModelMap.get(item.getProductModelId());
            if (productModel != null) {
                item.setThickness(productModel.getThickness());
@@ -275,6 +280,7 @@
        int result;
        Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
        salesLedgerProduct.fillRemainingQuantity();
        if (salesLedgerProduct.getId() == null) {
            salesLedgerProduct.setRegisterDate(LocalDateTime.now());
            result = salesLedgerProductMapper.insert(salesLedgerProduct);
@@ -497,6 +503,7 @@
            } else {
                item.setStatusName("未完成付款");
            }
            item.fillRemainingQuantity();
        });
        return salesLedgerProductDtoIPage;
    }
@@ -511,6 +518,7 @@
            } else {
                item.setStatusName("未完成付款");
            }
            item.fillRemainingQuantity();
        });
        return salesLedgerProductDtoIPage;
    }
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -19,6 +19,9 @@
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
@@ -37,6 +40,7 @@
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.quality.mapper.QualityInspectMapper;
@@ -139,6 +143,7 @@
    private final StockOutRecordMapper stockOutRecordMapper;
    private final StockInRecordService stockInRecordService;
    private final StockOutRecordService stockOutRecordService;
    private final StockUtils stockUtils;
    @Autowired
    private SysDeptMapper sysDeptMapper;
@@ -272,6 +277,7 @@
                    product.setActualTotalArea(pieceArea.multiply(quantity).setScale(2, RoundingMode.HALF_UP));
                }
            }
            product.fillRemainingQuantity();
        }
        // 3.查询上传文件
@@ -620,6 +626,7 @@
                salesLedgerProduct.setNoInvoiceNum(quantity);
                salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxExclusiveTotalPrice());
                salesLedgerProduct.setPendingInvoiceTotal(taxInclusiveTotalPrice);
                salesLedgerProduct.fillRemainingQuantity();
                salesLedgerProductMapper.insert(salesLedgerProduct);
                if (!processList.isEmpty()) {
                    salesLedgerProductProcessBindService.updateProductProcessBind(processList, salesLedgerProduct.getId());
@@ -1000,6 +1007,7 @@
            for (SalesLedgerProduct product : updateList) {
                product.setType(type.getCode());
                product.setProductStockStatus(0);
                product.fillRemainingQuantity();
                salesLedgerProductMapper.updateById(product);
                //  æ¸…空销售产品绑定的加工
                salesLedgerProductProcessBindService.updateProductProcessBind(product.getSalesProductProcessList(), product.getId());
@@ -1013,6 +1021,7 @@
                salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProduct.setProductStockStatus(0);
                salesLedgerProduct.fillRemainingQuantity();
                salesLedgerProductMapper.insert(salesLedgerProduct);
                //  ç»‘定产品额外加工
                //  æ¸…空销售产品绑定的加工
@@ -1717,4 +1726,298 @@
                .in(Customer::getId, customerIds)
                .orderByAsc(Customer::getCustomerName));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanInbound(SalesScanInboundDto dto) {
        if (dto == null || dto.getSalesLedgerId() == null) {
            throw new ServiceException("销售订单入库失败,入库数据不能为空");
        }
        SalesLedger salesLedger = baseMapper.selectById(dto.getSalesLedgerId());
        if (salesLedger == null) {
            throw new ServiceException("入库失败,销售订单不存在");
        }
        if (salesLedger.getStockStatus() == null) {
            throw new ServiceException("入库失败,销售订单状态异常");
        }
        if (salesLedger.getStockStatus() == 2) {
            throw new ServiceException("入库失败,该销售订单已全部入库");
        }
        if (salesLedger.getDeliveryStatus() == 5) {
            throw new ServiceException("入库失败,该销售订单已发货");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("销售订单入库失败,入库产品不能为空");
        }
        //  æœ¬æ¬¡å…¥åº“数量可以大于订单数量,但不能为负
        Map<Long, BigDecimal> inboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct inbound : dto.getSalesLedgerProductList()) {
            if (inbound == null || inbound.getId() == null) {
                throw new ServiceException("入库失败,销售产品信息不完整");
            }
            BigDecimal inboundQty = inbound.getStockedQuantity();
            if (inboundQty == null) {
                throw new ServiceException("入库失败,入库数量不能为空");
            }
            if (inboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("入库失败,入库数量不能为负数");
            }
            inboundQtyByLineId.merge(inbound.getId(), inboundQty, BigDecimal::add);
        }
        Long ledgerId = salesLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : inboundQtyByLineId.entrySet()) {
            Long salesLedgerProductId = entry.getKey();
            BigDecimal inboundThisLine = entry.getValue();
            if (inboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(salesLedgerProductId);
            if (dbProduct == null) {
                throw new ServiceException("入库失败,销售产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), ledgerId)) {
                throw new ServiceException("入库失败,销售产品与订单不匹配");
            }
            if (!Objects.equals(dbProduct.getType(), SaleEnum.SALE.getCode())) {
                throw new ServiceException("入库失败,仅支持销售订单产品行");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("入库失败,产品规格未维护,无法入库");
            }
            BigDecimal oldStocked = dbProduct.getStockedQuantity() == null ? BigDecimal.ZERO : dbProduct.getStockedQuantity();
            BigDecimal newStocked = oldStocked.add(inboundThisLine);
            StockInventoryDto stockInventoryDto = new StockInventoryDto();
            stockInventoryDto.setRecordId(dbProduct.getId());
            stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.SALE_SCAN_STOCK_IN.getCode());
            stockInventoryDto.setQualitity(inboundThisLine);
            stockInventoryDto.setProductModelId(dbProduct.getProductModelId());
            stockInventoryDto.setSalesLedgerId(ledgerId);
            stockInventoryDto.setSalesLedgerProductId(dbProduct.getId());
            stockInventoryService.addstockInventory(stockInventoryDto);
            BigDecimal orderQty = dbProduct.getQuantity() == null ? BigDecimal.ZERO : dbProduct.getQuantity();
            int lineStockStatus;
            if (newStocked.compareTo(BigDecimal.ZERO) <= 0) {
                lineStockStatus = 0;
            } else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) {
                lineStockStatus = 1;
            } else {
                lineStockStatus = 2;
            }
            dbProduct.setStockedQuantity(newStocked);
            dbProduct.setProductStockStatus(lineStockStatus);
            dbProduct.fillRemainingQuantity();
            salesLedgerProductMapper.updateById(dbProduct);
        }
        List<SalesLedgerProduct> ledgerAllProducts = salesLedgerProductMapper.selectList(
                Wrappers.<SalesLedgerProduct>lambdaQuery().eq(SalesLedgerProduct::getSalesLedgerId, ledgerId));
        boolean anyInbound = ledgerAllProducts.stream().anyMatch(p -> {
            BigDecimal sq = p.getStockedQuantity();
            return sq != null && sq.compareTo(BigDecimal.ZERO) > 0;
        });
        boolean allLinesFull = ledgerAllProducts.stream().allMatch(p -> Objects.equals(p.getProductStockStatus(), 2));
        salesLedger.setStockStatus(allLinesFull ? 2 : (anyInbound ? 1 : 0));
        baseMapper.updateById(salesLedger);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanInboundUnqualified(SalesScanInboundDto dto) {
        if (dto == null || dto.getSalesLedgerId() == null) {
            throw new ServiceException("销售订单不合格入库失败,入库数据不能为空");
        }
        SalesLedger salesLedger = baseMapper.selectById(dto.getSalesLedgerId());
        if (salesLedger == null) {
            throw new ServiceException("不合格入库失败,销售订单不存在");
        }
        if (salesLedger.getDeliveryStatus() != null && salesLedger.getDeliveryStatus() == 5) {
            throw new ServiceException("不合格入库失败,该销售订单已发货");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("销售订单不合格入库失败,入库产品不能为空");
        }
        Map<Long, BigDecimal> inboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct inbound : dto.getSalesLedgerProductList()) {
            if (inbound == null || inbound.getId() == null) {
                throw new ServiceException("不合格入库失败,销售产品信息不完整");
            }
            BigDecimal inboundQty = inbound.getStockedQuantity();
            if (inboundQty == null) {
                throw new ServiceException("不合格入库失败,入库数量不能为空");
            }
            if (inboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("不合格入库失败,入库数量不能为负数");
            }
            inboundQtyByLineId.merge(inbound.getId(), inboundQty, BigDecimal::add);
        }
        Long ledgerId = salesLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : inboundQtyByLineId.entrySet()) {
            Long salesLedgerProductId = entry.getKey();
            BigDecimal inboundThisLine = entry.getValue();
            if (inboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(salesLedgerProductId);
            if (dbProduct == null) {
                throw new ServiceException("不合格入库失败,销售产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), ledgerId)) {
                throw new ServiceException("不合格入库失败,销售产品与订单不匹配");
            }
            if (!Objects.equals(dbProduct.getType(), SaleEnum.SALE.getCode())) {
                throw new ServiceException("不合格入库失败,仅支持销售订单产品行");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("不合格入库失败,产品规格未维护,无法入库");
            }
            stockUtils.addUnStock(ledgerId, dbProduct.getId(), dbProduct.getProductModelId(), inboundThisLine,
                    StockInUnQualifiedRecordTypeEnum.SALES_SCAN_UNSTOCK_IN.getCode(), dbProduct.getId());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanOutbound(SalesScanInboundDto dto) {
        if (dto == null || dto.getSalesLedgerId() == null) {
            throw new ServiceException("销售订单出库失败,出库数据不能为空");
        }
        SalesLedger salesLedger = baseMapper.selectById(dto.getSalesLedgerId());
        if (salesLedger == null) {
            throw new ServiceException("出库失败,销售订单不存在");
        }
        if (salesLedger.getDeliveryStatus() != null && salesLedger.getDeliveryStatus() == 5) {
            throw new ServiceException("出库失败,该销售订单已发货");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("销售订单出库失败,出库产品不能为空");
        }
        int saleType = SaleEnum.SALE.getCode();
        Map<Long, BigDecimal> outboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct line : dto.getSalesLedgerProductList()) {
            if (line == null || line.getId() == null) {
                throw new ServiceException("出库失败,销售产品信息不完整");
            }
            BigDecimal outboundQty = line.getStockedQuantity();
            if (outboundQty == null) {
                throw new ServiceException("出库失败,出库数量不能为空");
            }
            if (outboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("出库失败,出库数量不能为负数");
            }
            outboundQtyByLineId.merge(line.getId(), outboundQty, BigDecimal::add);
        }
        Long ledgerId = salesLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : outboundQtyByLineId.entrySet()) {
            Long salesLedgerProductId = entry.getKey();
            BigDecimal outboundThisLine = entry.getValue();
            if (outboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(salesLedgerProductId);
            if (dbProduct == null) {
                throw new ServiceException("出库失败,销售产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), ledgerId) || !Objects.equals(dbProduct.getType(), saleType)) {
                throw new ServiceException("出库失败,销售产品与订单不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("出库失败,产品规格未维护,无法出库");
            }
            stockUtils.assertQualifiedAvailable(dbProduct.getProductModelId(), outboundThisLine);
            BigDecimal oldStocked = dbProduct.getStockedQuantity() == null ? BigDecimal.ZERO : dbProduct.getStockedQuantity();
            BigDecimal newStocked = oldStocked.subtract(outboundThisLine);
            if (newStocked.compareTo(BigDecimal.ZERO) < 0) {
                newStocked = BigDecimal.ZERO;
            }
            StockInventoryDto stockInventoryDto = new StockInventoryDto();
            stockInventoryDto.setRecordId(dbProduct.getId());
            stockInventoryDto.setRecordType(StockOutQualifiedRecordTypeEnum.SALE_SCAN_STOCK_OUT.getCode());
            stockInventoryDto.setQualitity(outboundThisLine);
            stockInventoryDto.setProductModelId(dbProduct.getProductModelId());
            stockInventoryDto.setSalesLedgerId(ledgerId);
            stockInventoryDto.setSalesLedgerProductId(dbProduct.getId());
            stockInventoryService.subtractStockInventory(stockInventoryDto);
            BigDecimal orderQty = dbProduct.getQuantity() == null ? BigDecimal.ZERO : dbProduct.getQuantity();
            int lineStockStatus;
            if (newStocked.compareTo(BigDecimal.ZERO) <= 0) {
                lineStockStatus = 0;
            } else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) {
                lineStockStatus = 1;
            } else {
                lineStockStatus = 2;
            }
            dbProduct.setStockedQuantity(newStocked);
            dbProduct.setProductStockStatus(lineStockStatus);
            dbProduct.fillRemainingQuantity();
            salesLedgerProductMapper.updateById(dbProduct);
        }
        List<SalesLedgerProduct> ledgerAllProducts = salesLedgerProductMapper.selectList(
                Wrappers.<SalesLedgerProduct>lambdaQuery().eq(SalesLedgerProduct::getSalesLedgerId, ledgerId));
        boolean anyInbound = ledgerAllProducts.stream().anyMatch(p -> {
            BigDecimal sq = p.getStockedQuantity();
            return sq != null && sq.compareTo(BigDecimal.ZERO) > 0;
        });
        boolean allLinesFull = ledgerAllProducts.stream().allMatch(p -> Objects.equals(p.getProductStockStatus(), 2));
        salesLedger.setStockStatus(allLinesFull ? 2 : (anyInbound ? 1 : 0));
        baseMapper.updateById(salesLedger);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scanOutboundUnqualified(SalesScanInboundDto dto) {
        if (dto == null || dto.getSalesLedgerId() == null) {
            throw new ServiceException("销售订单不合格出库失败,出库数据不能为空");
        }
        SalesLedger salesLedger = baseMapper.selectById(dto.getSalesLedgerId());
        if (salesLedger == null) {
            throw new ServiceException("不合格出库失败,销售订单不存在");
        }
        if (salesLedger.getDeliveryStatus() != null && salesLedger.getDeliveryStatus() == 5) {
            throw new ServiceException("不合格出库失败,该销售订单已发货");
        }
        if (CollectionUtils.isEmpty(dto.getSalesLedgerProductList())) {
            throw new ServiceException("销售订单不合格出库失败,出库产品不能为空");
        }
        int saleType = SaleEnum.SALE.getCode();
        Map<Long, BigDecimal> outboundQtyByLineId = new LinkedHashMap<>();
        for (SalesLedgerProduct line : dto.getSalesLedgerProductList()) {
            if (line == null || line.getId() == null) {
                throw new ServiceException("不合格出库失败,销售产品信息不完整");
            }
            BigDecimal outboundQty = line.getStockedQuantity();
            if (outboundQty == null) {
                throw new ServiceException("不合格出库失败,出库数量不能为空");
            }
            if (outboundQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("不合格出库失败,出库数量不能为负数");
            }
            outboundQtyByLineId.merge(line.getId(), outboundQty, BigDecimal::add);
        }
        Long ledgerId = salesLedger.getId();
        for (Map.Entry<Long, BigDecimal> entry : outboundQtyByLineId.entrySet()) {
            Long salesLedgerProductId = entry.getKey();
            BigDecimal outboundThisLine = entry.getValue();
            if (outboundThisLine.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            SalesLedgerProduct dbProduct = salesLedgerProductMapper.selectById(salesLedgerProductId);
            if (dbProduct == null) {
                throw new ServiceException("不合格出库失败,销售产品不存在");
            }
            if (!Objects.equals(dbProduct.getSalesLedgerId(), ledgerId) || !Objects.equals(dbProduct.getType(), saleType)) {
                throw new ServiceException("不合格出库失败,销售产品与订单不匹配");
            }
            if (dbProduct.getProductModelId() == null) {
                throw new ServiceException("不合格出库失败,产品规格未维护,无法出库");
            }
            stockUtils.assertUnqualifiedAvailable(dbProduct.getProductModelId(), outboundThisLine);
            stockUtils.subtractUnStock(ledgerId, dbProduct.getId(), dbProduct.getProductModelId(), outboundThisLine,
                    StockOutUnQualifiedRecordTypeEnum.SALE_SCAN_UNSTOCK_OUT.getCode(), dbProduct.getId());
        }
    }
}
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java
@@ -6,7 +6,7 @@
import java.math.BigDecimal;
@Data
public class StockUninventoryDto  extends StockUninventory {
public class StockUninventoryDto extends StockUninventory {
    private String productName;
    private String model;
    private String unit;
@@ -19,4 +19,14 @@
    private Long recordId;
    private BigDecimal unLockedQuantity;
    /**
     * é”€å”®è®¢å•ID
     */
    private Long salesLedgerId;
    /**
     * é”€å”®è®¢å•产品行ID
     */
    private Long salesLedgerProductId;
}
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -119,7 +119,10 @@
            throw new RuntimeException("库存不足无法出库");
        }
        stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
        int affectRows = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
        if (affectRows <= 0) {
            throw new RuntimeException("库存不足无法出库");
        }
        return true;
    }
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -54,6 +54,8 @@
        stockInRecordDto.setStockInNum(stockUninventoryDto.getQualitity());
        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockInRecordDto.setType("1");
        stockInRecordDto.setSalesLedgerId(stockUninventoryDto.getSalesLedgerId());
        stockInRecordDto.setSalesLedgerProductId(stockUninventoryDto.getSalesLedgerProductId());
        stockInRecordService.add(stockInRecordDto);
        //再进行新增库存数量库存
        //先查询库存表中的产品是否存在,不存在新增,存在更新
@@ -82,12 +84,17 @@
        stockOutRecordDto.setStockOutNum(stockUninventoryDto.getQualitity());
        stockOutRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockOutRecordDto.setType("1");
        stockOutRecordDto.setSalesLedgerId(stockUninventoryDto.getSalesLedgerId());
        stockOutRecordDto.setSalesLedgerProductId(stockUninventoryDto.getSalesLedgerProductId());
        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);
            int affectRows = stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
            if (affectRows <= 0) {
                throw new RuntimeException("库存不足无法出库");
            }
        }
        return 1;
    }
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -49,7 +49,8 @@
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
        where product_model_id = #{ew.productModelId}
          and (qualitity - COALESCE(locked_quantity, 0)) >= #{ew.qualitity}
    </update>
    <select id="pagestockInventory" resultType="com.ruoyi.stock.dto.StockInventoryDto">
src/main/resources/mapper/stock/StockUninventoryMapper.xml
@@ -25,7 +25,8 @@
            </if>
            update_time = now()
        </set>
        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
        where product_model_id = #{ew.productModelId}
          and (qualitity - COALESCE(locked_quantity, 0)) >= #{ew.qualitity}
    </update>
    <update id="updateAddStockUnInventory">
        update stock_uninventory