gongchunyi
6 天以前 bbd9823e935293b4815963647fecf48e3ba7ee1b
Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
已添加3个文件
已修改14个文件
866 ■■■■ 文件已修改
doc/20260522_StockInRecord列表源单号前端联调文档.md 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260522_StockInRecord列表源单号前端联调文档_276补充.md 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260522_采购台账入库状态_销售产品入库审核状态前端联调文档.md 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/dto/StatisticsReceivablePayableDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockInRecordController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInRecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInRecordMapper.xml 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260522_StockInRecordÁбíÔ´µ¥ºÅǰ¶ËÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
# StockInRecord åˆ—表源单号前端联调文档
更新时间:2026-05-22
适用接口:`GET /stockInRecord/listPage`
## 1. å˜æ›´è¯´æ˜Ž
本次对入库管理列表接口增加返回字段 `sourceOrderNo`(源单号),用于原材料场景展示采购来源单号。
生效条件:
- å½“请求参数 `topParentProductId = 278` æ—¶ï¼ŒåŽç«¯è¿”回 `sourceOrderNo`。
- å…¶ä»– `topParentProductId` åœºæ™¯ä¸‹ï¼Œè¯¥å­—段返回 `null`(或不展示)。
## 2. å­—段定义
新增字段:
- `sourceOrderNo`:`string`,源单号(采购合同号)。
## 3. å–值规则
仅在 `topParentProductId = 278` æ—¶æŒ‰â€œæ¥æºâ€è®¡ç®—:
1. æ¥æº = `采购-入库`(`recordType = 7`)
   - å…ˆæŒ‰ `recordId` æŸ¥é‡‡è´­äº§å“è¡¨ `sales_ledger_product`(`type=2`);
   - å†é€šè¿‡ `sales_ledger_product.sales_ledger_id` æŸ¥é‡‡è´­å°è´¦è¡¨ `purchase_ledger`;
   - è¿”回 `purchase_ledger.purchase_contract_number` ä½œä¸º `sourceOrderNo`。
   - å…¼å®¹å…œåº•:若未命中采购产品链路,则按 `recordId` ç›´æŽ¥æŸ¥ `purchase_ledger.id` å–单号。
2. æ¥æº = `采购-质检-合格入库`(`recordType = 10`)
   - å…ˆæŒ‰ `recordId` æŸ¥è´¨æ£€è¡¨ `quality_inspect`;
   - å†é€šè¿‡ `quality_inspect.purchase_ledger_id` æŸ¥é‡‡è´­å°è´¦è¡¨ `purchase_ledger`;
   - è¿”回 `purchase_ledger.purchase_contract_number` ä½œä¸º `sourceOrderNo`。
## 4. è¿”回示例
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "records": [
      {
        "id": 1024,
        "recordType": "7",
        "productName": "铜排",
        "model": "T2-30x3",
        "sourceOrderNo": "CG-2026-00128"
      },
      {
        "id": 1025,
        "recordType": "10",
        "productName": "铜排",
        "model": "T2-30x3",
        "sourceOrderNo": "CG-2026-00131"
      }
    ],
    "total": 2
  }
}
```
## 5. å‰ç«¯è”调建议
1. åˆ—表列新增“源单号”,读取字段 `sourceOrderNo`。
2. å»ºè®®ä»…在 `topParentProductId = 278` çš„页面/筛选条件下展示该列。
3. å½“ `sourceOrderNo` ä¸ºç©ºæ—¶å±•示 `--`,避免空白。
## 6. å›žå½’清单
1. `topParentProductId=278` + `recordType=7`:应返回采购合同号。
2. `topParentProductId=278` + `recordType=10`:应返回采购合同号(经质检链路)。
3. `topParentProductId!=278`:`sourceOrderNo` åº”为 `null` æˆ–前端不展示。
4. åŽŸæœ‰å­—æ®µï¼ˆ`productName/model/unit/createBy` ç­‰ï¼‰ä¸å—影响。
doc/20260522_StockInRecordÁбíÔ´µ¥ºÅǰ¶ËÁªµ÷Îĵµ_276²¹³ä.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
# StockInRecord åˆ—表源单号前端联调文档(`topParentProductId=276`补充)
更新时间:2026-05-22
适用接口:`GET /stockInRecord/listPage`
## 1. å˜æ›´è¯´æ˜Ž
在已有 `sourceOrderNo` åŸºç¡€ä¸Šï¼Œæ–°å¢ž `topParentProductId = 276` çš„æºå•号溯源逻辑:
- æ ¹æ®â€œæ¥æºï¼ˆrecordType)+ recordId”溯源;
- ä¼˜å…ˆè¿”回销售单号(销售合同号);
- è‹¥é”€å”®å•号为空,则回退返回生产订单号;
- ä¸è€ƒè™‘自定义入库(`recordType=0/9`)。
## 2. è¿”回字段
字段无新增,继续使用:
- `sourceOrderNo`:`string`,源单号。
## 3. 276 åœºæ™¯å–值规则
请求参数满足 `topParentProductId = 276` æ—¶ï¼š
1. `recordType = 14/15`(销售退货-合格/不合格入库)
   - `stock_in_record.record_id -> return_sale_product.id -> return_management.shipping_id -> shipping_info.sales_ledger_id -> sales_ledger.sales_contract_no`
   - è¿”回销售合同号。
2. `recordType = 2/5`(生产报工-入库/报废)
   - `stock_in_record.record_id -> production_product_main.id -> production_operation_task.production_order_id -> production_order`
   - å…ˆå–该生产订单关联销售合同号(由生产计划关联销售台账聚合);
   - é”€å”®åˆåŒå·ä¸ºç©ºæ—¶ï¼Œè¿”回 `production_order.nps_no`。
3. `recordType = 6`(质检-合格入库)
   - `stock_in_record.record_id -> quality_inspect.id -> quality_inspect.product_main_id -> production_product_main -> production_operation_task -> production_order`
   - å…ˆå–销售合同号,空则回退 `production_order.nps_no`。
4. `recordType = 4/11`(不合格处理-报废/让步放行)
   - `stock_in_record.record_id -> quality_unqualified.id -> quality_unqualified.inspect_id -> quality_inspect -> production_product_main -> production_operation_task -> production_order`
   - å…ˆå–销售合同号,空则回退 `production_order.nps_no`。
5. `recordType = 20/22`(领料退料/生产退料-合格入库)
   - `stock_in_record.record_id -> production_order_pick.id -> production_order`
   - å…ˆå–销售合同号,空则回退 `production_order.nps_no`。
6. `recordType = 0/9`(自定义入库)
   - ä¸å‚与溯源,`sourceOrderNo = null`。
## 4. å…¶ä»–场景说明
- `topParentProductId = 278` çš„采购链路源单号逻辑保持不变。
- å…¶ä»– `topParentProductId` ä¸è§¦å‘本次 276 è§„则,`sourceOrderNo` ä¸ºç©ºã€‚
## 5. å‰ç«¯è”调建议
1. åœ¨ `topParentProductId=276` çš„列表场景展示“源单号”列,读取 `sourceOrderNo`。
2. å»ºè®®ç©ºå€¼ç»Ÿä¸€å±•示 `--`。
3. ä¸éœ€è¦æ–°å¢žè¯·æ±‚参数,沿用现有 `/stockInRecord/listPage`。
## 6. å›žå½’清单
1. `topParentProductId=276` + `recordType=14/15`:应返回销售合同号。
2. `topParentProductId=276` + `recordType=2/5/6/4/11/20/22`:优先销售合同号,缺失时返回生产订单号。
3. `topParentProductId=276` + `recordType=0/9`:`sourceOrderNo` ä¸ºç©ºã€‚
4. `topParentProductId=278`:仍按采购链路返回采购合同号。
doc/20260522_²É¹ºÌ¨ÕËÈë¿â״̬_ÏúÊÛ²úÆ·Èë¿âÉóºË״̬ǰ¶ËÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,127 @@
# é‡‡è´­å…¥åº“状态前端联调文档
更新时间:2026-05-22
适用版本:本次后端变更后
## 1. å˜æ›´èŒƒå›´
1. `GET /purchaseLedger/listPage`
   - æ–°å¢žæŸ¥è¯¢æ¡ä»¶ï¼š`stockInStatus`(入库状态)
   - æ–°å¢žè¿”回字段:`stockInStatus`(入库状态)
2. `GET /salesLedgerProduct/list`
   - æ–°å¢žè¿”回字段:`stockInApprovalStatus`(每个产品的入库审核状态)
---
## 2. å…¥åº“状态枚举(两接口一致)
- `待入库`
- `入库中`
- `完全入库`
说明:前端筛选值请直接使用以上中文枚举值。
---
## 3. æŽ¥å£ä¸€ï¼š`GET /purchaseLedger/listPage`
### 3.1 æ–°å¢žè¯·æ±‚参数
- `stockInStatus`:`string`,可选
  å¯ä¼ å€¼ï¼š`待入库` / `入库中` / `完全入库`
### 3.2 æ–°å¢žè¿”回字段
- `stockInStatus`:`string`,采购台账维度入库状态
### 3.3 çŠ¶æ€è®¡ç®—è§„åˆ™ï¼ˆé‡‡è´­å°è´¦ç»´åº¦ï¼‰
以该采购台账下 `sales_ledger_product.type = 2` çš„采购产品为计算范围:
1. å…¨éƒ¨äº§å“éƒ½è¾¾åˆ°â€œå®Œå…¨å…¥åº“” => å°è´¦çŠ¶æ€ `完全入库`
2. è‡³å°‘有一个产品存在“审核通过入库”,但未全部完全入库 => å°è´¦çŠ¶æ€ `入库中`
3. æ²¡æœ‰ä»»ä½•产品存在“审核通过入库” => å°è´¦çŠ¶æ€ `待入库`
“审核通过入库”统计口径:`stock_in_record.approval_status = 1`,并按以下来源溯源:
- `record_type = 7`(采购-入库):按采购台账+产品关联统计
- `record_type = 10`(采购-质检-合格入库):通过 `quality_inspect` å›žæº¯åˆ°é‡‡è´­å°è´¦+产品统计
### 3.4 è¿”回示例(节选)
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "records": [
      {
        "id": 1201,
        "purchaseContractNumber": "CG20260522001",
        "supplierName": "XX供应商",
        "stockInStatus": "入库中"
      }
    ],
    "total": 1
  }
}
```
---
## 4. æŽ¥å£äºŒï¼š`GET /salesLedgerProduct/list`
### 4.1 æ–°å¢žè¿”回字段
- `stockInApprovalStatus`:`string`,当前产品行的入库审核状态
### 4.2 çŠ¶æ€è®¡ç®—è§„åˆ™ï¼ˆäº§å“ç»´åº¦ï¼‰
仅当产品 `type = 2`(采购产品)时计算并返回:
1. å®¡æ ¸é€šè¿‡å…¥åº“数量 `<= 0` => `待入库`
2. å®¡æ ¸é€šè¿‡å…¥åº“数量 `>= äº§å“é‡‡è´­æ•°é‡` => `完全入库`
3. å…¶ä»–情况 => `入库中`
其中“审核通过入库数量”统计同样基于:
- `stock_in_record.approval_status = 1`
- æ¥æº `record_type = 7 / 10` çš„æº¯æºå…³è”逻辑
`type != 2` çš„产品,该字段返回 `null`。
### 4.3 è¿”回示例(节选)
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "id": 5566,
      "type": 2,
      "productCategory": "铜材",
      "specificationModel": "T2-30x3",
      "quantity": 100,
      "stockInApprovalStatus": "待入库"
    }
  ]
}
```
---
## 5. å‰ç«¯æ”¹é€ å»ºè®®
1. é‡‡è´­å°è´¦åˆ—表新增“入库状态”筛选项,值固定:`待入库/入库中/完全入库`。
2. é‡‡è´­å°è´¦åˆ—表新增“入库状态”列,展示 `stockInStatus`。
3. é‡‡è´­äº§å“åˆ—表新增“入库审核状态”列,展示 `stockInApprovalStatus`。
4. ç©ºå€¼ï¼ˆå¦‚ `type != 2`)建议展示为 `--`。
---
## 6. è”调检查清单
1. `/purchaseLedger/listPage` ä¸ä¼  `stockInStatus`:应正常返回全部数据,并带 `stockInStatus`。
2. `/purchaseLedger/listPage?stockInStatus=待入库`:仅返回待入库台账。
3. `/purchaseLedger/listPage?stockInStatus=入库中`:仅返回入库中台账。
4. `/purchaseLedger/listPage?stockInStatus=完全入库`:仅返回完全入库台账。
5. `/salesLedgerProduct/list` åœ¨é‡‡è´­äº§å“ï¼ˆ`type=2`)下返回 `stockInApprovalStatus`。
6. `/salesLedgerProduct/list` åœ¨éžé‡‡è´­äº§å“ï¼ˆ`type!=2`)下 `stockInApprovalStatus` ä¸º `null`。
src/main/java/com/ruoyi/home/dto/StatisticsReceivablePayableDto.java
@@ -19,10 +19,10 @@
    @Schema(description = "应付金额")
    private BigDecimal payableMoney;
    @Schema(description = "预收金额")
    @Schema(description = "收款金额")
    private BigDecimal advanceMoney;
    @Schema(description = "预付金额")
    @Schema(description = "付款金额")
    private BigDecimal prepayMoney;
}
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,10 +3,18 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.bean.dto.purchase.PurchaseInboundDto;
import com.ruoyi.account.bean.dto.purchase.PurchaseReturnDto;
import com.ruoyi.account.bean.dto.sales.SalesOutboundDto;
import com.ruoyi.account.bean.dto.sales.SalesReturnDto;
import com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo;
import com.ruoyi.account.bean.vo.purchase.PurchaseReturnVo;
import com.ruoyi.account.bean.vo.sales.SalesOutboundVo;
import com.ruoyi.account.bean.vo.sales.SalesReturnVo;
import com.ruoyi.account.mapper.AccountStatementMapper;
import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper;
import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
import com.ruoyi.account.pojo.AccountStatement;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
@@ -29,11 +37,13 @@
import com.ruoyi.home.dto.*;
import com.ruoyi.home.mapper.HomeMapper;
import com.ruoyi.home.service.HomeService;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.production.bean.dto.ProductionProductOutputDto;
import com.ruoyi.production.mapper.*;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
@@ -45,7 +55,9 @@
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -71,6 +83,10 @@
public class HomeServiceImpl implements HomeService {
    private final SalesLedgerMapper salesLedgerMapper;
    private final StockOutRecordMapper stockOutRecordMapper;
    private final ReturnManagementMapper returnManagementMapper;
    private final StockInRecordMapper stockInRecordMapper;
    private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
@@ -417,14 +433,26 @@
        LocalDate startDate = range[0];
        LocalDate endDate = range[1];
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //收款
        BigDecimal advanceMoney = sumCollectionAmount(startDate, endDate);
        //付款
        BigDecimal prepayMoney = sumPaymentAmount(startDate, endDate);
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(advanceMoney))));
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(prepayMoney))));
        //应收金额=销售出库-销售退货
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(salesReturnAmount))));
        //应付金额=采购入库-采购退货
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(purchaseReturnAmount))));
        //收款金额=收款单
        dto.setAdvanceMoney(scaleMoney(advanceMoney));
        //付款金额=付款单
        dto.setPrepayMoney(scaleMoney(prepayMoney));
        return dto;
    }
@@ -1239,33 +1267,21 @@
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        String collectionRate = toRateString(monthlyIncome, receivableBase);
        String currentMonthText = currentMonth.toString();
        List<AccountStatement> receivableStatements = defaultList(accountStatementMapper.selectList(
                new LambdaQueryWrapper<AccountStatement>()
                        .eq(AccountStatement::getAccountType, 1)
                        .le(AccountStatement::getStatementMonth, currentMonthText)));
        long overdueNum = receivableStatements.stream()
                .filter(item -> item.getStatementMonth() != null && item.getStatementMonth().compareTo(currentMonthText) < 0)
                .filter(item -> defaultDecimal(item.getClosingBalance()).compareTo(BigDecimal.ZERO) > 0)
                .count();
        long totalReceivableCount = receivableStatements.size();
        String overdueRate = totalReceivableCount == 0
                ? "0.00"
                : BigDecimal.valueOf(overdueNum)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(totalReceivableCount), 2, RoundingMode.HALF_UP)
                .toString();
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //回款率=收款/(销售出库-销售退货)应收
        String collectionRate = toRateString(monthlyIncome, receivableBase.subtract(salesReturnAmount));
        //逾期数=(销售出库金额-销售退货金额)应收金额-收款金额
        BigDecimal overdueAmount = receivableBase.subtract(salesReturnAmount).subtract(monthlyIncome);
        //逾期率=逾期数/应收金额
        String overdueRate = toRateString(overdueAmount, receivableBase);
        dto.setMonthlyIncome(scaleMoney(monthlyIncome));
        dto.setCollectionRate(collectionRate);
        dto.setOverdueNum(BigDecimal.valueOf(overdueNum));
        dto.setOverdueNum(overdueAmount);
        dto.setOverdueRate(overdueRate);
        return dto;
    }
@@ -1277,16 +1293,25 @@
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //支出
        BigDecimal monthlyExpenditure = sumPaymentAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //付款率=付款/(采购入库-采购退货)应付
        String paymentRate = toRateString(monthlyExpenditure, payableBase.subtract(purchaseReturnAmount));
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //毛利润= æ”¶æ¬¾-支出
        BigDecimal grossProfit = monthlyIncome.subtract(monthlyExpenditure);
        //利润率=毛利润/收款
        String profitMarginRate = toRateString(grossProfit, monthlyIncome);
        dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure));
        dto.setPaymentRate(toRateString(monthlyExpenditure, payableBase));
        dto.setPaymentRate(paymentRate);
        dto.setGrossProfit(scaleMoney(grossProfit));
        dto.setProfitMarginRate(toRateString(grossProfit, monthlyIncome));
        dto.setProfitMarginRate(profitMarginRate);
        return dto;
    }
@@ -2317,20 +2342,43 @@
        return new LocalDate[]{startDate, endDate};
    }
    //计算日期内的销售出库金额
    private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) {
        List<SalesLedger> salesLedgers = defaultList(salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, toDate(startDate))
                .lt(SalesLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(salesLedgers, SalesLedger::getContractAmount);
        SalesOutboundDto salesOutboundDto = new SalesOutboundDto();
        salesOutboundDto.setStartDate(startDate);
        salesOutboundDto.setEndDate(endDate);
        List<SalesOutboundVo> salesOutboundVos = stockOutRecordMapper.listPageAccountSales(new Page(1, -1), salesOutboundDto).getRecords();
        return sumAmount(salesOutboundVos, SalesOutboundVo::getOutboundAmount);
    }
    //计算日期内的销售退货金额
    private BigDecimal sumSalesReturnAmount(LocalDate startDate, LocalDate endDate) {
        SalesReturnDto salesReturnDto = new SalesReturnDto();
        salesReturnDto.setStartDate(startDate);
        salesReturnDto.setEndDate(endDate);
        List<SalesReturnVo> salesReturnVos = returnManagementMapper.listPageAccountSalesReturn(new Page(1, -1), salesReturnDto).getRecords();
        return sumAmount(salesReturnVos, SalesReturnVo::getRefundAmount);
    }
    //计算日期内的采购入库金额
    private BigDecimal sumPurchaseContractAmount(LocalDate startDate, LocalDate endDate) {
        List<PurchaseLedger> purchaseLedgers = defaultList(purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
                .ge(PurchaseLedger::getEntryDate, toDate(startDate))
                .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(purchaseLedgers, PurchaseLedger::getContractAmount);
        PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto();
        purchaseInboundDto.setStartDate(startDate);
        purchaseInboundDto.setEndDate(endDate);
        List<PurchaseInboundVo> purchaseInboundVos = stockInRecordMapper.listPageAccountPurchase(new Page(1, -1), purchaseInboundDto).getRecords();
        return sumAmount(purchaseInboundVos, PurchaseInboundVo::getInboundAmount);
    }
    //计算日期内的采购退货金额
    private BigDecimal sumPurchaseReturnAmount(LocalDate startDate, LocalDate endDate) {
        PurchaseReturnDto purchaseReturnDto = new PurchaseReturnDto();
        purchaseReturnDto.setStartDate(startDate);
        purchaseReturnDto.setEndDate(endDate);
        List<PurchaseReturnVo> purchaseReturnVos = purchaseReturnOrdersMapper.listPageAccountPurchaseReturn(new Page(1, -1), purchaseReturnDto).getRecords();
        return sumAmount(purchaseReturnVos, PurchaseReturnVo::getTotalAmount);
    }
    //计算日期内的总收款金额
    private BigDecimal sumCollectionAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList(
                new LambdaQueryWrapper<AccountSalesCollection>()
@@ -2339,6 +2387,7 @@
        return sumAmount(collections, AccountSalesCollection::getCollectionAmount);
    }
    //计算日期内的总付款金额
    private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList(
                new LambdaQueryWrapper<AccountPurchasePayment>()
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -27,6 +27,8 @@
    private String entryDateStart;
    private String entryDateEnd;
    @Schema(description = "入库状态")
    private String stockInStatus;
    private Long id;
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -451,12 +451,7 @@
    @Override
    public IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger) {
        IPage<PurchaseLedgerDto> purchaseLedgerDtoIPage = purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
        purchaseLedgerDtoIPage.getRecords().forEach(purchaseLedgerDto -> {
            List<CommonFile> commonFiles = commonFileMapper.selectList(new LambdaQueryWrapper<CommonFile>().eq(CommonFile::getCommonId, purchaseLedgerDto.getId()).eq(CommonFile::getType, FileNameType.PURCHASE.getValue()));
            purchaseLedgerDto.setSalesLedgerFiles(commonFiles);
        });
        return purchaseLedgerDtoIPage;
        return purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
    }
    @Override
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -149,6 +149,10 @@
    /**
     * äº§å“çŠ¶æ€
     */
    @TableField(exist = false)
    @Schema(description = "入库审核状态")
    private String stockInApprovalStatus;
//    @TableField(exist = false)
    @Schema(description = "产品状态:1-充足")
    private Integer approveStatus;
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -8,6 +8,10 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.mapper.sales.AccountInvoiceApplicationMapper;
import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
import com.ruoyi.account.pojo.sales.AccountInvoiceApplication;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.mapper.CustomerMapper;
@@ -26,7 +30,9 @@
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.mapper.ProductionProductInputMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.mapper.ProductionProductOutputMapper;
import com.ruoyi.production.service.ProductionProductMainService;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
@@ -36,8 +42,14 @@
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.sales.dto.*;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.*;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.ISalesLedgerService;
import com.ruoyi.sales.vo.SalesLedgerVo;
import lombok.RequiredArgsConstructor;
@@ -93,6 +105,8 @@
    private final QualityInspectMapper qualityInspectMapper;
    private final RedisTemplate<String, String> redisTemplate;
    private final FileUtil fileUtil;
    private final AccountInvoiceApplicationMapper accountInvoiceApplicationMapper;
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
@@ -263,23 +277,37 @@
    @Override
    public List<MonthlyAmountDto> getAmountHalfYear(Integer type) {
        LocalDate now = LocalDate.now();
        List<MonthlyAmountDto> result = new ArrayList<>();
        for (int i = 5; i >= 0; i--) {
            YearMonth yearMonth = YearMonth.from(now.minusMonths(i));
            LocalDateTime startTime = yearMonth.atDay(1).atStartOfDay();
            LocalDateTime endTime = yearMonth.atEndOfMonth().atTime(23, 59, 59);
            LocalDate startTime = yearMonth.atDay(1);
            LocalDate endTime = yearMonth.atEndOfMonth();
            MonthlyAmountDto dto = new MonthlyAmountDto();
            dto.setMonth(yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")));
            dto.setReceiptAmount(BigDecimal.ZERO);
            dto.setInvoiceAmount(BigDecimal.ZERO);
            //回款金额
            List<AccountSalesCollection> accountSalesCollections = accountSalesCollectionMapper.selectList(new LambdaQueryWrapper<AccountSalesCollection>()
                    .between(AccountSalesCollection::getCollectionDate, startTime, endTime));
            BigDecimal totalIncome = Optional.of(
                    accountSalesCollections.stream()
                            .map(AccountSalesCollection::getCollectionAmount)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
            ).orElse(BigDecimal.ZERO);
            dto.setReceiptAmount(totalIncome);
            //开票金额
            List<AccountInvoiceApplication> accountInvoiceApplications = accountInvoiceApplicationMapper.selectList(new LambdaQueryWrapper<AccountInvoiceApplication>()
                            .eq(AccountInvoiceApplication::getStatus,1)
                    .between(AccountInvoiceApplication::getApplyDate, startTime, endTime));
            BigDecimal totalInvoiceAmount = Optional.of(
                    accountInvoiceApplications.stream()
                            .map(AccountInvoiceApplication::getInvoiceAmount)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
            ).orElse(BigDecimal.ZERO);
            dto.setInvoiceAmount(totalInvoiceAmount);
            result.add(dto);
        }
        return result;
    }
src/main/java/com/ruoyi/stock/controller/StockInRecordController.java
@@ -70,4 +70,15 @@
        return AjaxResult.success();
    }
    @PostMapping("/reAudit")
    @Log(title = "入库管理-反审入库", businessType = BusinessType.UPDATE)
    @Operation(summary = "批量反审入库记录")
    public AjaxResult reAudit(@RequestBody StockInRecordDto approveDto) {
        if(CollectionUtils.isEmpty(approveDto.getIds())){
            return AjaxResult.error("请选择至少一条数据");
        }
        stockInRecordService.batchReAudit(approveDto.getIds());
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
@@ -40,4 +40,7 @@
    @Schema(description = "记录ID列表")
    private List<Long> ids;
    @Schema(description = "源单号")
    private String sourceOrderNo;
}
src/main/java/com/ruoyi/stock/service/StockInRecordService.java
@@ -23,4 +23,6 @@
    void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto);
    int batchApprove(List<Long> ids, Integer approvalStatus);
    int batchReAudit(List<Long> ids);
}
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -156,6 +156,32 @@
        return stockUninventoryMapper.selectOne(eq);
    }
    /**
     * å›žæ»šç”Ÿäº§é€€æ–™å…¥åº“的领料记录退料数量
     * @param stockInRecord å…¥åº“记录
     */
    private void rollbackFeedReturnQty(StockInRecord stockInRecord) {
        ProductionOrderPick productionOrderPick = productionOrderPickMapper.selectById(stockInRecord.getRecordId());
        if (productionOrderPick != null) {
            BigDecimal returnQty = productionOrderPick.getReturnQty();
            if (returnQty == null) {
                returnQty = BigDecimal.ZERO;
            }
            BigDecimal newReturnQty = returnQty.subtract(stockInRecord.getStockInNum());
            if (newReturnQty.compareTo(BigDecimal.ZERO) < 0) {
                newReturnQty = BigDecimal.ZERO;
            }
            productionOrderPick.setReturnQty(newReturnQty);
            // é‡æ–°è®¡ç®—实际用量
            BigDecimal actualQty = productionOrderPick.getQuantity().add(
                productionOrderPick.getFeedingQty() != null ? productionOrderPick.getFeedingQty() : BigDecimal.ZERO)
                .subtract(newReturnQty);
            productionOrderPick.setActualQty(actualQty);
            productionOrderPick.setReturned(newReturnQty.compareTo(BigDecimal.ZERO) > 0);
            productionOrderPickMapper.updateById(productionOrderPick);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDeletePending(List<Long> ids) {
@@ -166,6 +192,11 @@
            }
            if (stockInRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有待审批状态的记录才能删除,入库批次:" + stockInRecord.getInboundBatches());
            }
            // å¦‚果是生产退料入库,删除时需要回滚领料记录的退料数量
            if (StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                rollbackFeedReturnQty(stockInRecord);
            }
        }
        return stockInRecordMapper.deleteBatchIds(ids);
@@ -194,27 +225,7 @@
            // å®¡æ‰¹é©³å›žæ—¶ï¼Œå¦‚果是生产退料入库,需要回滚领料记录的退料数量
            if (ReviewStatusEnum.REJECTED.getCode().equals(approvalStatus) &&
                StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                // æ‰¾åˆ°å¯¹åº”的领料记录
                ProductionOrderPick productionOrderPick = productionOrderPickMapper.selectById(stockInRecord.getRecordId());
                if (productionOrderPick != null) {
                    // å›žæ»šé€€æ–™æ•°é‡
                    BigDecimal returnQty = productionOrderPick.getReturnQty();
                    if (returnQty == null) {
                        returnQty = BigDecimal.ZERO;
                    }
                    BigDecimal newReturnQty = returnQty.subtract(stockInRecord.getStockInNum());
                    if (newReturnQty.compareTo(BigDecimal.ZERO) < 0) {
                        newReturnQty = BigDecimal.ZERO;
                    }
                    productionOrderPick.setReturnQty(newReturnQty);
                    // é‡æ–°è®¡ç®—实际用量
                    BigDecimal actualQty = productionOrderPick.getQuantity().add(
                        productionOrderPick.getFeedingQty() != null ? productionOrderPick.getFeedingQty() : BigDecimal.ZERO)
                        .subtract(newReturnQty);
                    productionOrderPick.setActualQty(actualQty);
                    productionOrderPick.setReturned(newReturnQty.compareTo(BigDecimal.ZERO) > 0);
                    productionOrderPickMapper.updateById(productionOrderPick);
                }
                rollbackFeedReturnQty(stockInRecord);
            }
            // å®¡æ‰¹é€šè¿‡æ—¶ï¼Œåº“存增加
@@ -262,4 +273,50 @@
        }
        return ids.size();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchReAudit(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BaseException("请选择至少一条数据");
        }
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord == null) {
                throw new BaseException("入库记录不存在,无法重新审核!!!");
            }
            // åªæœ‰é©³å›žçŠ¶æ€æ‰èƒ½é‡æ–°å®¡æ ¸
            if (!ReviewStatusEnum.REJECTED.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有驳回状态的记录才能重新审核,入库批次:" + stockInRecord.getInboundBatches());
            }
            // å¦‚果是生产退料入库,恢复退料数量(因为驳回时已扣减)
            if (StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                ProductionOrderPick productionOrderPick = productionOrderPickMapper.selectById(stockInRecord.getRecordId());
                if (productionOrderPick != null) {
                    BigDecimal returnQty = productionOrderPick.getReturnQty();
                    if (returnQty == null) {
                        returnQty = BigDecimal.ZERO;
                    }
                    // é‡æ–°å®¡æ ¸æ—¶æ¢å¤é€€æ–™æ•°é‡
                    BigDecimal newReturnQty = returnQty.add(stockInRecord.getStockInNum());
                    productionOrderPick.setReturnQty(newReturnQty);
                    // é‡æ–°è®¡ç®—实际用量
                    BigDecimal actualQty = productionOrderPick.getQuantity().add(
                        productionOrderPick.getFeedingQty() != null ? productionOrderPick.getFeedingQty() : BigDecimal.ZERO)
                        .subtract(newReturnQty);
                    productionOrderPick.setActualQty(actualQty);
                    productionOrderPick.setReturned(newReturnQty.compareTo(BigDecimal.ZERO) > 0);
                    productionOrderPickMapper.updateById(productionOrderPick);
                }
            }
            // å°†çŠ¶æ€æ”¹ä¸ºå¾…å®¡æ ¸
            stockInRecord.setApprovalStatus(ReviewStatusEnum.PENDING_REVIEW.getCode());
            stockInRecordMapper.updateById(stockInRecord);
        }
        return ids.size();
    }
}
src/main/resources/application.yml
@@ -3,7 +3,7 @@
  main:
    allow-circular-references: true
  profiles:
    active: dev-pro
    active: dev
langchain4j:
  mcp:
    # MCP æœåŠ¡ç«¯åœ°å€ï¼ˆæ ¹æ®å®žé™…éƒ¨ç½²çš„ MCP æœåŠ¡è°ƒæ•´ï¼‰
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml
@@ -17,56 +17,118 @@
    </select>
    <select id="selectPurchaseLedgerListPage" resultType="com.ruoyi.purchase.dto.PurchaseLedgerDto">
        SELECT
        pl.id,
        pl.purchase_contract_number,
        pl.sales_contract_no,
        pl.supplier_id,
        pl.supplier_name,
        pl.project_name,
        pl.contract_amount,
        pl.entry_date,
        pl.execution_date,
        pl.recorder_id,
        pl.recorder_name,
        pl.template_name,
        pl.approve_user_ids,
        sm.is_white,
        pl.approval_status,
        pl.payment_method,
        pl.remarks
        FROM purchase_ledger pl
        LEFT JOIN supplier_manage sm ON pl.supplier_id = sm.id
        <where>
            <if test="c.purchaseContractNumber != null and c.purchaseContractNumber != ''">
                AND pl.purchase_contract_number LIKE CONCAT('%', #{c.purchaseContractNumber}, '%')
            </if>
            <if test="c.approvalStatus != null and c.approvalStatus != ''">
                AND pl.approval_status = #{c.approvalStatus}
            </if>
            <if test="c.supplierName != null and c.supplierName != ''">
                AND pl.supplier_name LIKE CONCAT('%', #{c.supplierName}, '%')
            </if>
            <if test="c.salesContractNo != null and c.salesContractNo != ''">
                AND pl.sales_contract_no LIKE CONCAT('%', #{c.salesContractNo}, '%')
            </if>
            <if test="c.projectName != null and c.projectName != ''">
                AND pl.project_name LIKE CONCAT('%', #{c.projectName}, '%')
            </if>
            <if test="c.entryDateStart != null and c.entryDateStart != ''">
                AND pl.entry_date &gt;= #{c.entryDateStart}
            </if>
            <if test="c.entryDateEnd != null and c.entryDateEnd != ''">
                AND pl.entry_date &lt;= #{c.entryDateEnd}
            </if>
            <if test="c.supplierId != null">
                AND pl.supplier_id = #{c.supplierId}
            </if>
            <if test="c.approvalStatus != null">
                AND pl.approval_status = #{c.approvalStatus}
            </if>
        </where>
        ORDER BY pl.entry_date DESC
        SELECT result.*
        FROM (
            SELECT
                pl.id,
                pl.purchase_contract_number,
                pl.sales_contract_no,
                pl.supplier_id,
                pl.supplier_name,
                pl.project_name,
                pl.contract_amount,
                pl.entry_date,
                pl.execution_date,
                pl.recorder_id,
                pl.recorder_name,
                pl.template_name,
                pl.approve_user_ids,
                sm.is_white,
                pl.approval_status,
                pl.payment_method,
                pl.remarks,
                CASE
                    WHEN IFNULL(ls.total_product_count, 0) = 0 THEN '待入库'
                    WHEN IFNULL(ls.full_product_count, 0) &gt;= IFNULL(ls.total_product_count, 0) THEN '完全入库'
                    WHEN IFNULL(ls.approved_product_count, 0) &gt; 0 THEN '入库中'
                    ELSE '待入库'
                END AS stock_in_status
            FROM purchase_ledger pl
            LEFT JOIN supplier_manage sm ON pl.supplier_id = sm.id
            LEFT JOIN (
                SELECT
                    product_status.sales_ledger_id,
                    COUNT(1) AS total_product_count,
                    SUM(CASE WHEN product_status.approved_stock_in_num &gt; 0 THEN 1 ELSE 0 END) AS approved_product_count,
                    SUM(CASE WHEN product_status.approved_stock_in_num &gt;= product_status.product_quantity THEN 1 ELSE 0 END) AS full_product_count
                FROM (
                    SELECT
                        slp.id AS sales_ledger_product_id,
                        slp.sales_ledger_id,
                        IFNULL(slp.quantity, 0) AS product_quantity,
                        IFNULL(approved_qty.approved_stock_in_num, 0) AS approved_stock_in_num
                    FROM sales_ledger_product slp
                    LEFT JOIN (
                        SELECT rel.sales_ledger_product_id,
                               IFNULL(SUM(rel.stock_in_num), 0) AS approved_stock_in_num
                        FROM (
                            SELECT slp.id AS sales_ledger_product_id,
                                   sir.stock_in_num
                            FROM stock_in_record sir
                            INNER JOIN sales_ledger_product slp
                                ON slp.type = 2
                                AND TRIM(sir.record_type) = '7'
                                AND sir.record_id = slp.sales_ledger_id
                                AND (
                                    (sir.batch_no IS NOT NULL AND sir.batch_no LIKE CONCAT('%-', slp.id))
                                    OR (sir.batch_no IS NULL AND sir.product_model_id = slp.product_model_id)
                                )
                            WHERE sir.approval_status = 1
                            UNION ALL
                            SELECT slp.id AS sales_ledger_product_id,
                                   sir.stock_in_num
                            FROM stock_in_record sir
                            INNER JOIN quality_inspect qi
                                ON TRIM(sir.record_type) = '10'
                                AND sir.record_id = qi.id
                            INNER JOIN sales_ledger_product slp
                                ON slp.type = 2
                                AND slp.sales_ledger_id = qi.purchase_ledger_id
                                AND slp.product_model_id = qi.product_model_id
                            WHERE sir.approval_status = 1
                        ) rel
                        GROUP BY rel.sales_ledger_product_id
                    ) approved_qty ON approved_qty.sales_ledger_product_id = slp.id
                    WHERE slp.type = 2
                ) product_status
                GROUP BY product_status.sales_ledger_id
            ) ls ON ls.sales_ledger_id = pl.id
            <where>
                <if test="c.purchaseContractNumber != null and c.purchaseContractNumber != ''">
                    AND pl.purchase_contract_number LIKE CONCAT('%', #{c.purchaseContractNumber}, '%')
                </if>
                <if test="c.approvalStatus != null and c.approvalStatus != ''">
                    AND pl.approval_status = #{c.approvalStatus}
                </if>
                <if test="c.supplierName != null and c.supplierName != ''">
                    AND pl.supplier_name LIKE CONCAT('%', #{c.supplierName}, '%')
                </if>
                <if test="c.salesContractNo != null and c.salesContractNo != ''">
                    AND pl.sales_contract_no LIKE CONCAT('%', #{c.salesContractNo}, '%')
                </if>
                <if test="c.projectName != null and c.projectName != ''">
                    AND pl.project_name LIKE CONCAT('%', #{c.projectName}, '%')
                </if>
                <if test="c.entryDateStart != null and c.entryDateStart != ''">
                    AND pl.entry_date &gt;= #{c.entryDateStart}
                </if>
                <if test="c.entryDateEnd != null and c.entryDateEnd != ''">
                    AND pl.entry_date &lt;= #{c.entryDateEnd}
                </if>
                <if test="c.supplierId != null">
                    AND pl.supplier_id = #{c.supplierId}
                </if>
                <if test="c.approvalStatus != null">
                    AND pl.approval_status = #{c.approvalStatus}
                </if>
            </where>
        ) result
        <if test="c.stockInStatus != null and c.stockInStatus != ''">
            WHERE result.stock_in_status = #{c.stockInStatus}
        </if>
        ORDER BY result.entry_date DESC
    </select>
    <select id="selectTotalPurchaseAmount" resultType="java.math.BigDecimal">
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -38,7 +38,13 @@
         WHEN IFNULL(t3.shipped_quantity, 0) = 0 THEN '待发货'
         WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '部分发货'
        ELSE '已发货'
        END as shippingStatus
        END as shippingStatus,
        CASE
         WHEN T1.type != 2 THEN NULL
         WHEN IFNULL(t4.approved_stock_in_num, 0) &lt;= 0 THEN '待入库'
         WHEN IFNULL(t4.approved_stock_in_num, 0) &gt;= IFNULL(T1.quantity, 0) THEN '完全入库'
        ELSE '入库中'
        END AS stock_in_approval_status
        FROM
        sales_ledger_product T1
        LEFT JOIN (
@@ -53,6 +59,39 @@
        where si.status != '审核拒绝'
        GROUP BY sales_ledger_product_id
        ) t3 ON t3.sales_ledger_product_id = T1.id
        LEFT JOIN (
        SELECT rel.sales_ledger_product_id,
               IFNULL(SUM(rel.stock_in_num), 0) AS approved_stock_in_num
        FROM (
            SELECT slp.id AS sales_ledger_product_id,
                   sir.stock_in_num
            FROM stock_in_record sir
            INNER JOIN sales_ledger_product slp
                ON slp.type = 2
                AND TRIM(sir.record_type) = '7'
                AND sir.record_id = slp.sales_ledger_id
                AND (
                    (sir.batch_no IS NOT NULL AND sir.batch_no LIKE CONCAT('%-', slp.id))
                    OR (sir.batch_no IS NULL AND sir.product_model_id = slp.product_model_id)
                )
            WHERE sir.approval_status = 1
            UNION ALL
            SELECT slp.id AS sales_ledger_product_id,
                   sir.stock_in_num
            FROM stock_in_record sir
            INNER JOIN quality_inspect qi
                ON TRIM(sir.record_type) = '10'
                AND sir.record_id = qi.id
            INNER JOIN sales_ledger_product slp
                ON slp.type = 2
                AND slp.sales_ledger_id = qi.purchase_ledger_id
                AND slp.product_model_id = qi.product_model_id
            WHERE sir.approval_status = 1
        ) rel
        GROUP BY rel.sales_ledger_product_id
        ) t4 ON t4.sales_ledger_product_id = T1.id
        left join product_model pm ON T1.product_model_id = pm.id
        left join product p ON pm.product_id = p.id
        <where>
src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -19,11 +19,96 @@
        p.product_name as product_name,
        pm.model,
        pm.unit,
        u.nick_name as createBy
        u.nick_name as createBy,
        CASE
            WHEN #{params.topParentProductId} = 278 AND TRIM(sir.record_type) = '7' THEN
                COALESCE(pl_by_product.purchase_contract_number, pl_direct.purchase_contract_number)
            WHEN #{params.topParentProductId} = 278 AND TRIM(sir.record_type) = '10' THEN
                pl_by_quality.purchase_contract_number
            WHEN #{params.topParentProductId} = 276 THEN
                CASE
                    WHEN TRIM(sir.record_type) IN ('14', '15') THEN
                        sl_return.sales_contract_no
                    WHEN TRIM(sir.record_type) IN ('2', '5') THEN
                        COALESCE(po_sales_main.sales_contract_no, po_main.nps_no)
                    WHEN TRIM(sir.record_type) = '6' THEN
                        COALESCE(po_sales_qi.sales_contract_no, po_qi.nps_no)
                    WHEN TRIM(sir.record_type) IN ('4', '11') THEN
                        COALESCE(po_sales_uq.sales_contract_no, po_uq.nps_no)
                    WHEN TRIM(sir.record_type) IN ('20', '22') THEN
                        COALESCE(po_sales_pick.sales_contract_no, po_pick.nps_no)
                    ELSE NULL
                END
            ELSE NULL
        END AS sourceOrderNo
        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
        LEFT JOIN quality_inspect as qi_purchase on TRIM(sir.record_type) = '10' and sir.record_id = qi_purchase.id
        LEFT JOIN sales_ledger_product as slp on TRIM(sir.record_type) = '7' and slp.id = sir.record_id and slp.type = 2
        LEFT JOIN purchase_ledger as pl_by_product on TRIM(sir.record_type) = '7' and pl_by_product.id = slp.sales_ledger_id
        LEFT JOIN purchase_ledger as pl_direct on TRIM(sir.record_type) = '7' and pl_direct.id = sir.record_id
        LEFT JOIN purchase_ledger as pl_by_quality on TRIM(sir.record_type) = '10' and pl_by_quality.id = qi_purchase.purchase_ledger_id
        LEFT JOIN production_product_main as ppm_main on TRIM(sir.record_type) IN ('2', '5') and sir.record_id = ppm_main.id
        LEFT JOIN production_operation_task as pot_main on ppm_main.production_operation_task_id = pot_main.id
        LEFT JOIN production_order as po_main on pot_main.production_order_id = po_main.id
        LEFT JOIN (
            select po2.id as production_order_id,
                   group_concat(distinct sl2.sales_contract_no order by sl2.sales_contract_no separator ',') as sales_contract_no
            from production_order po2
                     left join production_plan pp2
                               on find_in_set(pp2.id, replace(replace(replace(po2.production_plan_ids, '[', ''), ']', ''), ' ', '')) > 0
                     left join sales_ledger sl2 on sl2.id = pp2.sales_ledger_id
            group by po2.id
        ) as po_sales_main on po_sales_main.production_order_id = po_main.id
        LEFT JOIN quality_inspect as qi_prod on TRIM(sir.record_type) = '6' and sir.record_id = qi_prod.id
        LEFT JOIN production_product_main as ppm_qi on qi_prod.product_main_id = ppm_qi.id
        LEFT JOIN production_operation_task as pot_qi on ppm_qi.production_operation_task_id = pot_qi.id
        LEFT JOIN production_order as po_qi on pot_qi.production_order_id = po_qi.id
        LEFT JOIN (
            select po2.id as production_order_id,
                   group_concat(distinct sl2.sales_contract_no order by sl2.sales_contract_no separator ',') as sales_contract_no
            from production_order po2
                     left join production_plan pp2
                               on find_in_set(pp2.id, replace(replace(replace(po2.production_plan_ids, '[', ''), ']', ''), ' ', '')) > 0
                     left join sales_ledger sl2 on sl2.id = pp2.sales_ledger_id
            group by po2.id
        ) as po_sales_qi on po_sales_qi.production_order_id = po_qi.id
        LEFT JOIN quality_unqualified as qu on TRIM(sir.record_type) IN ('4', '11') and sir.record_id = qu.id
        LEFT JOIN quality_inspect as qi_uq on qu.inspect_id = qi_uq.id
        LEFT JOIN production_product_main as ppm_uq on qi_uq.product_main_id = ppm_uq.id
        LEFT JOIN production_operation_task as pot_uq on ppm_uq.production_operation_task_id = pot_uq.id
        LEFT JOIN production_order as po_uq on pot_uq.production_order_id = po_uq.id
        LEFT JOIN (
            select po2.id as production_order_id,
                   group_concat(distinct sl2.sales_contract_no order by sl2.sales_contract_no separator ',') as sales_contract_no
            from production_order po2
                     left join production_plan pp2
                               on find_in_set(pp2.id, replace(replace(replace(po2.production_plan_ids, '[', ''), ']', ''), ' ', '')) > 0
                     left join sales_ledger sl2 on sl2.id = pp2.sales_ledger_id
            group by po2.id
        ) as po_sales_uq on po_sales_uq.production_order_id = po_uq.id
        LEFT JOIN production_order_pick as pop on TRIM(sir.record_type) IN ('20', '22') and sir.record_id = pop.id
        LEFT JOIN production_order as po_pick on pop.production_order_id = po_pick.id
        LEFT JOIN (
            select po2.id as production_order_id,
                   group_concat(distinct sl2.sales_contract_no order by sl2.sales_contract_no separator ',') as sales_contract_no
            from production_order po2
                     left join production_plan pp2
                               on find_in_set(pp2.id, replace(replace(replace(po2.production_plan_ids, '[', ''), ']', ''), ' ', '')) > 0
                     left join sales_ledger sl2 on sl2.id = pp2.sales_ledger_id
            group by po2.id
        ) as po_sales_pick on po_sales_pick.production_order_id = po_pick.id
        LEFT JOIN return_sale_product as rsp on TRIM(sir.record_type) IN ('14', '15') and sir.record_id = rsp.id
        LEFT JOIN return_management as rm on rsp.return_management_id = rm.id
        LEFT JOIN shipping_info as si_return on rm.shipping_id = si_return.id
        LEFT JOIN sales_ledger as sl_return on si_return.sales_ledger_id = sl_return.id
        <where>
            <if test="params.timeStr != null and params.timeStr != ''">
                and sir.create_time like concat('%',#{params.timeStr},'%')