Merge remote-tracking branch 'origin/dev_天津_意博凯信' into dev_天津_意博凯信
已修改51个文件
已重命名1个文件
已添加7个文件
已删除58个文件
15447 ■■■■■ 文件已修改
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/新增付款接口联调文档.md 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/新增回款接口联调文档.md 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/purchase/AccountPurchasePaymentMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/sales/AccountSalesCollectionMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/purchase/AccountPurchasePaymentServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/sales/AccountSalesCollectionServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/purchase/AccountPurchasePaymentService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/sales/AccountSalesCollectionService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/controller/HomeController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/mapper/HomeMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/HomeService.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java 258 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/SavePaymentDto.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/VatDto.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SaveReceiptPaymentDto.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-bdsm.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-bhmy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-btw.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-cjny.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-ckgm.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-cmny.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-demo.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dhdc.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hbkj.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hbtmblc.yml 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hbxm.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hckx.yml 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hckxTest.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hcmy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hhkj.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hqjc.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hsmy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hsxny.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hthb.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hxgy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hxsj.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hysn.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jfhy.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jjxm.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jlsn.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jsmy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jsyny.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jtwy.yml 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jyhj.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-khl.yml 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-khyy.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-lsy.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-mkzs.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-mqsp.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-mxsc.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-myhb-pro.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-native.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-nhxcl.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-nydl.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-phmk.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-prod.yml 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-qfsw.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-qlmc-pro.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-qlmc.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-rfsy.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-rzny.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-tjxm.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-tymk.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-wdsy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xhks.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xyhb.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-ybkx-test.yml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-yss.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zdjc.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zjyj.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zqhx.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zxsq.yml 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zyrq.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zyrqCopy.yml 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/AccountStatementMapper.xml 135 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/purchase/AccountPurchasePaymentMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/sales/AccountSalesCollectionMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/SupplierManageMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/home/HomeMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -11,6 +11,7 @@
######################################################################
# Claude Code
.claude/
.mcp.json
### STS ###
docs/ÐÂÔö¸¶¿î½Ó¿ÚÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
# æ–°å¢žä»˜æ¬¾æŽ¥å£è”调文档
## æ¦‚è¿°
在付款台账页面(`src/views/procurementManagement/paymentLedger/index.vue`)右侧供应商明细表格中,每条数据新增了「新增付款」操作按钮。点击后弹出表单,填写付款信息并提交。
---
## æŽ¥å£ä¿¡æ¯
- **URL**: `/purchase/report/savePayment`
- **Method**: `POST`
- **Content-Type**: `application/json`
---
## è¯·æ±‚参数
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| `supplierId` | String | æ˜¯ | ä¾›åº”商ID,来自主表(左侧)当前选中行的 `supplierId` |
| `purchaseContractNumber` | String | æ˜¯ | é‡‡è´­åˆåŒå·ï¼Œæ¥è‡ªæ˜Žç»†è¡Œï¼ˆå³ä¾§ï¼‰æ•°æ®çš„ `purchaseContractNumber` |
| `paymentDate` | String | æ˜¯ | ä»˜æ¬¾æ—¥æœŸï¼Œæ ¼å¼ `YYYY-MM-DD` |
| `paymentAmount` | Number | æ˜¯ | ä»˜æ¬¾é‡‘额,保留两位小数,最小值 0.01 |
| `paymentMethod` | String | æ˜¯ | ä»˜æ¬¾æ–¹å¼ï¼Œå¯é€‰å€¼è§ä¸‹æ–¹æžšä¸¾è¡¨ |
| `remark` | String | å¦ | å¤‡æ³¨ä¿¡æ¯ |
### ä»˜æ¬¾æ–¹å¼æžšä¸¾å€¼
| å€¼ | è¯´æ˜Ž |
|------|------|
| `银行转账` | é“¶è¡Œè½¬è´¦ |
| `现金` | çް金 |
| `支票` | æ”¯ç¥¨ |
| `微信/支付宝` | å¾®ä¿¡/支付宝 |
| `其他` | å…¶ä»–方式 |
---
## è¯·æ±‚示例
```json
{
  "supplierId": "SUPP001",
  "purchaseContractNumber": "CG-2024-0001",
  "paymentDate": "2024-08-12",
  "paymentAmount": 50000.00,
  "paymentMethod": "银行转账",
  "remark": "合同第一期付款"
}
```
---
## å“åº”格式
### æˆåŠŸ
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": null
}
```
### å¤±è´¥
```json
{
  "code": 500,
  "msg": "具体错误信息",
  "data": null
}
```
---
## å‰ç«¯è°ƒç”¨æµç¨‹
1. ç”¨æˆ·åœ¨å·¦ä¾§ä¸»è¡¨ç‚¹å‡»æŸä¾›åº”商行,右侧加载该供应商的合同明细
2. ç”¨æˆ·åœ¨å³ä¾§æ˜Žç»†è¡¨ç‚¹å‡»æŸè¡Œçš„「新增付款」按钮
3. å¼¹å‡ºå¯¹è¯æ¡†ï¼Œå±•示当前合同的采购合同号(只读)
4. ç”¨æˆ·å¡«å†™ä»˜æ¬¾æ—¥æœŸã€é‡‘额、方式、备注后点击确认
5. å‰ç«¯è°ƒç”¨ `POST /purchase/report/savePayment`
6. æˆåŠŸåŽå…³é—­å¼¹æ¡†ï¼Œè‡ªåŠ¨åˆ·æ–°å·¦ä¾§ä¾›åº”å•†æ±‡æ€»è¡¨å’Œå³ä¾§æ˜Žç»†è¡¨
7. å¤±è´¥åˆ™æç¤ºåŽç«¯è¿”回的 `msg` é”™è¯¯ä¿¡æ¯
---
## å‰ç«¯è¡¨å•校验规则
| å­—段 | è§„则 |
|------|------|
| `paymentDate` | å¿…填,未选择时提示"请选择付款日期" |
| `paymentAmount` | å¿…填;不能超过可付款余额(应付金额 - ä»˜æ¬¾é‡‘额),超限时提示"付款金额不能超过可付款余额 XXX å…ƒ" |
| `paymentMethod` | å¿…填,未选择时提示"请选择付款方式" |
| `remark` | é€‰å¡«ï¼Œæ— æ ¡éªŒ |
### å¯ä»˜æ¬¾ä½™é¢è®¡ç®—
当前行可付款余额 = `payableAmount`(应付金额) - `paymentAmount`(付款金额)
每次打开新增付款弹框时,付款金额输入框下方会显示黄色提示文字"当前可付款余额:XXX å…ƒ"。
docs/ÐÂÔö»Ø¿î½Ó¿ÚÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
# æ–°å¢žå›žæ¬¾æŽ¥å£è”调文档
## æ¦‚è¿°
在收付款台账页面(`src/views/salesManagement/receiptPaymentLedger/index.vue`)右侧客户明细表格中,每条数据新增了「新增回款」操作按钮。点击后弹出表单,填写回款信息并提交。
---
## æŽ¥å£ä¿¡æ¯
- **URL**: `/metricStatistics/saveReceiptPayment`
- **Method**: `POST`
- **Content-Type**: `application/json`
---
## è¯·æ±‚参数
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| `customerId` | String | æ˜¯ | å®¢æˆ·ID,来自主表(左侧)当前选中行的 `customerId` |
| `salesContractNo` | String | æ˜¯ | é”€å”®åˆåŒå·ï¼Œæ¥è‡ªæ˜Žç»†è¡Œï¼ˆå³ä¾§ï¼‰æ•°æ®çš„ `salesContractNo` |
| `paymentDate` | String | æ˜¯ | å›žæ¬¾æ—¥æœŸï¼Œæ ¼å¼ `YYYY-MM-DD` |
| `paymentAmount` | Number | æ˜¯ | å›žæ¬¾é‡‘额,保留两位小数,最小值 0.01 |
| `paymentMethod` | String | æ˜¯ | å›žæ¬¾æ–¹å¼ï¼Œå¯é€‰å€¼è§ä¸‹æ–¹æžšä¸¾è¡¨ |
| `remark` | String | å¦ | å¤‡æ³¨ä¿¡æ¯ |
### å›žæ¬¾æ–¹å¼æžšä¸¾å€¼
| å€¼ | è¯´æ˜Ž |
|------|------|
| `银行转账` | é“¶è¡Œè½¬è´¦ |
| `现金` | çް金 |
| `支票` | æ”¯ç¥¨ |
| `微信/支付宝` | å¾®ä¿¡/支付宝 |
| `其他` | å…¶ä»–方式 |
---
## è¯·æ±‚示例
```json
{
  "customerId": "CUST001",
  "salesContractNo": "XS-2024-0001",
  "paymentDate": "2024-08-12",
  "paymentAmount": 50000.00,
  "paymentMethod": "银行转账",
  "remark": "合同第一期回款"
}
```
---
## å“åº”格式
### æˆåŠŸ
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": null
}
```
### å¤±è´¥
```json
{
  "code": 500,
  "msg": "具体错误信息",
  "data": null
}
```
---
## å‰ç«¯è°ƒç”¨æµç¨‹
1. ç”¨æˆ·åœ¨å³ä¾§æ˜Žç»†è¡¨ç‚¹å‡»æŸè¡Œçš„「新增回款」按钮
2. å¼¹å‡ºå¯¹è¯æ¡†ï¼Œå±•示当前合同的销售合同号(只读)
3. ç”¨æˆ·å¡«å†™å›žæ¬¾æ—¥æœŸã€é‡‘额、方式、备注后点击确认
4. å‰ç«¯è°ƒç”¨ `POST /metricStatistics/saveReceiptPayment`
5. æˆåŠŸåŽå…³é—­å¼¹æ¡†ï¼Œè‡ªåŠ¨åˆ·æ–°å·¦ä¾§å®¢æˆ·æ±‡æ€»è¡¨å’Œå³ä¾§æ˜Žç»†è¡¨
6. å¤±è´¥åˆ™æç¤ºåŽç«¯è¿”回的 `msg` é”™è¯¯ä¿¡æ¯
---
## å‰ç«¯è¡¨å•校验规则
| å­—段 | è§„则 |
|------|------|
| `paymentDate` | å¿…填,未选择时提示"请选择回款日期" |
| `paymentAmount` | å¿…填,未输入时提示"请输入回款金额" |
| `paymentMethod` | å¿…填,未选择时提示"请选择回款方式" |
| `remark` | é€‰å¡«ï¼Œæ— æ ¡éªŒ |
src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java
@@ -7,22 +7,20 @@
import com.ruoyi.account.bean.vo.StatementAccountVo;
import com.ruoyi.account.pojo.AccountStatement;
import com.ruoyi.purchase.dto.VatDto;
import com.ruoyi.purchase.dto.VatSummaryDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
 *  Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-19 09:42:47
 */
import java.util.List;
@Mapper
public interface AccountStatementMapper extends BaseMapper<AccountStatement> {
    IPage<StatementAccountVo> listPageAccountStatement(Page page, @Param("req") StatementAccountDto statementAccountDto);
    IPage<VatDto> selectVatDtoPage(Page page, @Param("month") String month);
    IPage<VatDto> selectVatDtoPage(Page page, @Param("month") String month, @Param("type") String type);
    List<VatSummaryDto> selectVatSummary(@Param("year") String year);
    IPage<VatDto> listVatDetail(Page page, @Param("month") String month);
}
src/main/java/com/ruoyi/account/mapper/purchase/AccountPurchasePaymentMapper.java
@@ -26,4 +26,11 @@
    IPage<AccountPurchasePaymentVo> listPageAccountPurchasePayment(Page page, @Param("req") AccountPurchasePaymentDto accountPurchasePaymentDto);
    List<IncomeExpenseAnalysisDto> selectPayment(@Param("startStr") String startStr, @Param("endStr") String endStr, @Param("dateFormat") String dateFormat);
    /**
     * æŸ¥è¯¢æŸé‡‡è´­å°è´¦ä¸‹å·²å®¡æ‰¹çš„采购入库记录id列表
     * @param purchaseLedgerId é‡‡è´­å°è´¦id
     * @return å…¥åº“记录id列表
     */
    List<Long> selectStockInRecordIdsByPurchaseLedgerId(@Param("purchaseLedgerId") Long purchaseLedgerId);
}
src/main/java/com/ruoyi/account/mapper/sales/AccountSalesCollectionMapper.java
@@ -32,4 +32,11 @@
    List<SalesOutboundVo> getOutboundBatchesByCustomer(@Param("customerId") Integer customerId);
    List<IncomeExpenseAnalysisDto> selectIncomeStats(@Param("startStr") String startStr, @Param("endStr") String endStr, @Param("dateFormat") String dateFormat);
    /**
     * æŸ¥è¯¢æŸé”€å”®å°è´¦ä¸‹å·²å®¡æ‰¹çš„销售出库记录id列表
     * @param salesLedgerId é”€å”®å°è´¦id
     * @return å‡ºåº“记录id列表
     */
    List<Long> selectStockOutRecordIdsBySalesLedgerId(@Param("salesLedgerId") Long salesLedgerId);
}
src/main/java/com/ruoyi/account/service/impl/purchase/AccountPurchasePaymentServiceImpl.java
@@ -1,10 +1,11 @@
package com.ruoyi.account.service.impl.purchase;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
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.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.ruoyi.account.bean.dto.purchase.AccountPurchasePaymentDto;
import com.ruoyi.account.bean.vo.purchase.AccountPurchasePaymentVo;
import com.ruoyi.account.mapper.AccountStatementDetailsMapper;
@@ -17,15 +18,20 @@
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.purchase.dto.SavePaymentDto;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
/**
 * <p>
@@ -43,6 +49,7 @@
    private final AccountPurchasePaymentMapper accountPurchasePaymentMapper;
    private final AccountStatementDetailsMapper accountStatementDetailsMapper;
    private final AccountPaymentApplicationMapper accountPaymentApplicationMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    @Override
    public IPage<AccountPurchasePaymentVo> listPageAccountPurchasePayment(Page page, AccountPurchasePaymentDto accountPurchasePaymentDto) {
@@ -84,6 +91,63 @@
        return removeByIds(ids);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean savePayment(SavePaymentDto dto) {
        if (dto.getPaymentDate() == null) {
            throw new ServiceException("请选择付款日期");
        }
        if (dto.getPaymentAmount() == null || dto.getPaymentAmount().compareTo(BigDecimal.valueOf(0.01)) < 0) {
            throw new ServiceException("请输入付款金额");
        }
        if (StringUtils.isEmpty(dto.getPurchaseContractNumber())) {
            throw new ServiceException("采购合同号不能为空");
        }
        if (dto.getSupplierId() == null) {
            throw new ServiceException("供应商不能为空");
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                .eq(PurchaseLedger::getPurchaseContractNumber, dto.getPurchaseContractNumber()));
        if (purchaseLedger == null) {
            throw new ServiceException("采购合同不存在");
        }
        if (purchaseLedger.getSupplierId() == null || purchaseLedger.getSupplierId().longValue() != dto.getSupplierId().longValue()) {
            throw new ServiceException("供应商与合同不匹配");
        }
        List<Long> stockInRecordIds = accountPurchasePaymentMapper.selectStockInRecordIdsByPurchaseLedgerId(purchaseLedger.getId());
        if (CollectionUtils.isEmpty(stockInRecordIds)) {
            throw new ServiceException("该合同暂无可付款的采购入库记录");
        }
        AccountPaymentApplication application = new AccountPaymentApplication();
        application.setSupplierId(dto.getSupplierId().intValue());
        application.setStockInRecordIds(stockInRecordIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        application.setInvoiceApplicationNo(genPaymentApplicationNo());
        application.setPaymentMethod(dto.getPaymentMethod());
        application.setPaymentContent(dto.getPurchaseContractNumber());
        application.setApplyDate(dto.getPaymentDate());
        application.setRemark(dto.getRemark());
        application.setStatus(1);
        application.setPaymentAmount(dto.getPaymentAmount());
        accountPaymentApplicationMapper.insert(application);
        AccountPurchasePayment payment = new AccountPurchasePayment();
        payment.setAccountPaymentApplicationId(application.getId());
        payment.setSupplierId(dto.getSupplierId().intValue());
        payment.setPaymentDate(dto.getPaymentDate());
        payment.setPaymentMethod(dto.getPaymentMethod());
        payment.setPaymentAmount(dto.getPaymentAmount());
        payment.setPaymentNumber(genAccountPurchasePaymentNo());
        payment.setRemark(dto.getRemark());
        return save(payment);
    }
    private String genPaymentApplicationNo() {
        return "FK" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }
    private String genAccountPurchasePaymentNo() {
        return "SK" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }
src/main/java/com/ruoyi/account/service/impl/sales/AccountSalesCollectionServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.account.service.impl.sales;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -16,15 +17,20 @@
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sales.dto.SaveReceiptPaymentDto;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
/**
 * <p>
@@ -40,6 +46,7 @@
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    private final AccountStatementDetailsMapper accountStatementDetailsMapper;
    private final SalesLedgerMapper salesLedgerMapper;
    private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMddHHmmss");
@@ -90,6 +97,46 @@
        return accountSalesCollectionMapper.getOutboundBatchesByCustomer(customerId);
    }
    @Override
    public boolean saveReceiptPayment(SaveReceiptPaymentDto dto) {
        if (dto.getPaymentDate() == null) {
            throw new ServiceException("请选择回款日期");
        }
        if (dto.getPaymentAmount() == null || dto.getPaymentAmount().compareTo(BigDecimal.valueOf(0.01)) < 0) {
            throw new ServiceException("请输入回款金额");
        }
        if (StringUtils.isEmpty(dto.getSalesContractNo())) {
            throw new ServiceException("销售合同号不能为空");
        }
        if (dto.getCustomerId() == null) {
            throw new ServiceException("客户不能为空");
        }
        SalesLedger salesLedger = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                .eq(SalesLedger::getSalesContractNo, dto.getSalesContractNo()));
        if (salesLedger == null) {
            throw new ServiceException("销售合同不存在");
        }
        if (salesLedger.getCustomerId() == null || salesLedger.getCustomerId().longValue() != dto.getCustomerId().longValue()) {
            throw new ServiceException("客户与合同不匹配");
        }
        List<Long> stockOutRecordIds = accountSalesCollectionMapper.selectStockOutRecordIdsBySalesLedgerId(salesLedger.getId());
        if (CollectionUtils.isEmpty(stockOutRecordIds)) {
            throw new ServiceException("该合同暂无可回款的销售出库记录");
        }
        AccountSalesCollection collection = new AccountSalesCollection();
        collection.setCustomerId(dto.getCustomerId().intValue());
        collection.setStockOutRecordIds(stockOutRecordIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        collection.setCollectionDate(dto.getPaymentDate());
        collection.setCollectionAmount(dto.getPaymentAmount());
        collection.setCollectionMethod(dto.getPaymentMethod());
        collection.setCollectionNumber(genAccountSalesCollectionNo());
        collection.setRemark(dto.getRemark());
        return save(collection);
    }
    private String genAccountSalesCollectionNo() {
        return "SK" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }
src/main/java/com/ruoyi/account/service/purchase/AccountPurchasePaymentService.java
@@ -5,6 +5,7 @@
import com.ruoyi.account.bean.dto.purchase.AccountPurchasePaymentDto;
import com.ruoyi.account.bean.vo.purchase.AccountPurchasePaymentVo;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.purchase.dto.SavePaymentDto;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
@@ -27,4 +28,12 @@
    void exportAccountPurchasePayment(HttpServletResponse response, AccountPurchasePaymentDto accountPurchasePaymentDto);
    boolean deleteAccountPurchasePayment(List<Long> ids);
    /**
     * é‡‡è´­ç®¡ç†--供应商往来 æ–°å¢žä»˜æ¬¾
     *
     * @param dto ä»˜æ¬¾è¯·æ±‚参数
     * @return æ˜¯å¦æˆåŠŸ
     */
    boolean savePayment(SavePaymentDto dto);
}
src/main/java/com/ruoyi/account/service/sales/AccountSalesCollectionService.java
@@ -6,6 +6,7 @@
import com.ruoyi.account.bean.vo.sales.AccountSalesCollectionVo;
import com.ruoyi.account.bean.vo.sales.SalesOutboundVo;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.sales.dto.SaveReceiptPaymentDto;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
@@ -30,4 +31,12 @@
    boolean deleteAccountSalesCollection(List<Long> ids);
    List<SalesOutboundVo> getOutboundBatchesByCustomer(Integer customerId);
    /**
     * è¥é”€ç®¡ç†--客户往来 æ–°å¢žå›žæ¬¾
     *
     * @param dto å›žæ¬¾è¯·æ±‚参数
     * @return æ˜¯å¦æˆåŠŸ
     */
    boolean saveReceiptPayment(SaveReceiptPaymentDto dto);
}
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -111,7 +111,6 @@
                if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(vo.getBusinessType())) {
                    // é‡‡è´­å®¡æ‰¹ - æŸ¥è¯¢é‡‡è´­å•号
                    PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(vo.getBusinessId());
                    System.out.println("业务类型:" + purchaseLedger.getPurchaseContractNumber());
                    if (purchaseLedger != null) {
                        vo.setPurchaseContractNumber(purchaseLedger.getPurchaseContractNumber());
                    }
@@ -134,7 +133,7 @@
        List<Long> instanceIds = records.stream()
                .map(ApprovalInstanceVo::getId)
                .filter(id -> id != null)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java
@@ -5,7 +5,8 @@
@Getter
public enum SparePartsRequisitionRecordSourceTypeEnum implements BaseEnum<Integer> {
    SparePartsRequisitionRecordSourceTypeRepair(0, "ç»´ä¿®"),
    SparePartsRequisitionRecordSourceTypeMaintenance(1, "保养");
    SparePartsRequisitionRecordSourceTypeMaintenance(1, "保养"),
    SparePartsRequisitionRecordSourceTypeDirectOutbound(2, "直接出库");
    private final Integer code;
    private final String value;
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.device.dto.DeviceMaintenanceBatchCompleteDto;
import com.ruoyi.device.dto.DeviceMaintenanceDto;
import com.ruoyi.device.pojo.DeviceLedger;
import com.ruoyi.device.pojo.DeviceMaintenance;
@@ -12,6 +13,7 @@
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
@@ -79,5 +81,23 @@
        deviceMaintenanceService.export(response, ids);
    }
    @PostMapping("/batchComplete")
    @Operation(summary = "一键保养完成")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult batchComplete(@RequestBody DeviceMaintenanceBatchCompleteDto dto) {
        return deviceMaintenanceService.batchComplete(dto);
    }
    @PutMapping("/complete/{id}")
    @Operation(summary = "保养完成")
    public AjaxResult complete(@PathVariable Long id) {
        return deviceMaintenanceService.complete(id);
    }
    @PutMapping("/batchComplete")
    @Operation(summary = "一键保养完成")
    public AjaxResult batchComplete(String dateStr) {
        return deviceMaintenanceService.batchComplete(dateStr);
    }
}
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.device.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@Schema(name = "设备保养批量完成请求")
public class DeviceMaintenanceBatchCompleteDto {
    @Schema(description = "计划保养日期", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDate maintenancePlanDate;
    @Schema(description = "实际保养人")
    private String maintenanceActuallyName;
    @Schema(description = "实际保养时间")
    private LocalDateTime maintenanceActuallyTime;
    @Schema(description = "保养结果")
    private String maintenanceResult;
}
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java
@@ -53,5 +53,8 @@
    @Schema(description = "保养图片")
    private List<StorageBlobDTO> storageBlobDTOs;
    @Schema(description = "日期分组字段(yyyy-MM-dd)")
    private String dateStr;
}
src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.device.dto.DeviceMaintenanceBatchCompleteDto;
import com.ruoyi.device.dto.DeviceMaintenanceDto;
import com.ruoyi.device.pojo.DeviceMaintenance;
import com.ruoyi.device.vo.DeviceMaintenanceVo;
@@ -21,4 +22,10 @@
    void export(HttpServletResponse response, Long[] ids);
    DeviceMaintenanceVo detailById(Long id);
    AjaxResult batchComplete(DeviceMaintenanceBatchCompleteDto dto);
    AjaxResult complete(Long id);
    AjaxResult batchComplete(String dateStr);
}
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -1,13 +1,16 @@
package com.ruoyi.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.device.dto.DeviceMaintenanceBatchCompleteDto;
import com.ruoyi.device.dto.DeviceMaintenanceDto;
import com.ruoyi.device.execl.DeviceMaintenanceExeclDto;
import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
@@ -27,6 +30,9 @@
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
@@ -95,6 +101,11 @@
            }
        }
        // è‹¥è®¾ç½®äº†å®žé™…保养日期,则自动标记为完结
        if (deviceMaintenance.getMaintenanceActuallyTime() != null && deviceMaintenance.getStatus() == null) {
            deviceMaintenance.setStatus(1);
        }
        if (this.updateById(deviceMaintenance)) {
            // å¤„理图片上传
            fileUtil.saveStorageAttachmentByRecordTypeAndRecordId("file", RecordTypeEnum.DEVICE_MAINTENANCE, deviceMaintenance.getId(), deviceMaintenance.getStorageBlobDTOs());
@@ -104,6 +115,80 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult complete(Long id) {
        DeviceMaintenance record = this.getById(id);
        if (record == null) {
            return AjaxResult.error("保养记录不存在");
        }
        if (record.getStatus() != null && record.getStatus() == 1) {
            return AjaxResult.error("该保养记录已完成");
        }
        record.setStatus(1);
        record.setMaintenanceActuallyName(SecurityUtils.getLoginUser().getNickName());
        record.setMaintenanceActuallyTime(LocalDateTime.now());
        this.updateById(record);
        return AjaxResult.success();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult batchComplete(String dateStr) {
        if (StringUtils.isBlank(dateStr)) {
            return AjaxResult.error("日期参数不能为空");
        }
        LocalDate date = LocalDate.parse(dateStr);
        LocalDateTime startOfDay = date.atStartOfDay();
        LocalDateTime endOfDay = date.atTime(LocalTime.MAX);
        List<DeviceMaintenance> records = this.lambdaQuery()
                .eq(DeviceMaintenance::getStatus, 0)
                .ge(DeviceMaintenance::getCreateTime, startOfDay)
                .le(DeviceMaintenance::getCreateTime, endOfDay)
                .list();
        if (records.isEmpty()) {
            return AjaxResult.error("该日期下没有待完成的保养记录");
        }
        String nickName = SecurityUtils.getLoginUser().getNickName();
        LocalDateTime now = LocalDateTime.now();
        for (DeviceMaintenance record : records) {
            record.setStatus(1);
            record.setMaintenanceActuallyName(nickName);
            record.setMaintenanceActuallyTime(now);
        }
        this.updateBatchById(records);
        return AjaxResult.success("已完成 " + records.size() + " æ¡ä¿å…»è®°å½•");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult batchComplete(DeviceMaintenanceBatchCompleteDto dto) {
        if (dto.getMaintenancePlanDate() == null) {
            return AjaxResult.error("计划保养日期不能为空");
        }
        LambdaQueryWrapper<DeviceMaintenance> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeviceMaintenance::getStatus, 0)
               .apply("DATE(maintenance_plan_time) = {0}", dto.getMaintenancePlanDate().toString());
        List<DeviceMaintenance> list = this.list(wrapper);
        if (list.isEmpty()) {
            return AjaxResult.success("该日期下没有待保养的记录");
        }
        list.forEach(task -> {
            task.setStatus(1);
            if (StringUtils.isNotBlank(dto.getMaintenanceActuallyName())) {
                task.setMaintenanceActuallyName(dto.getMaintenanceActuallyName());
            }
            if (dto.getMaintenanceActuallyTime() != null) {
                task.setMaintenanceActuallyTime(dto.getMaintenanceActuallyTime());
            }
            if (StringUtils.isNotBlank(dto.getMaintenanceResult())) {
                task.setMaintenanceResult(dto.getMaintenanceResult());
            }
        });
        this.updateBatchById(list);
        return AjaxResult.success("共完成 " + list.size() + " æ¡ä¿å…»è®°å½•");
    }
    @Override
    public void export(HttpServletResponse response, Long[] ids) {
        List<DeviceMaintenance> supplierManageList = deviceMaintenanceMapper.selectList(null);
        ArrayList<DeviceMaintenanceExeclDto> deviceLedgerExeclDtos = new ArrayList<>();
src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -61,8 +61,7 @@
    private static final Integer ORDER_STATUS_WAIT = 1;
    private static final Integer ORDER_STATUS_RUNNING = 2;
    private static final Integer ORDER_STATUS_COMPLETED = 3;
    //private static final Integer ORDER_STATUS_END = 4;  // å·²å–消
    private static final Integer ORDER_STATUS_END = 5;
    private static final Integer ORDER_STATUS_PAUSED = 4;
    /********************************************************基础类*****************************************************/
    @GetMapping("/todos")
@@ -104,8 +103,10 @@
    /********************************************************营销采购类**************************************************/
    @GetMapping("/supplierPurchaseRanking")
    @Operation(summary = "供应商采购排名")
    public R supplierPurchaseRanking(@DefaultType Integer type) {
        List<SupplierPurchaseRankingDto> list = homeService.supplierPurchaseRanking(type);
    public R supplierPurchaseRanking(@DefaultType Integer type,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate) {
        List<SupplierPurchaseRankingDto> list = homeService.supplierPurchaseRanking(type, startDate, endDate);
        return R.ok(list);
    }
@@ -118,22 +119,28 @@
    @GetMapping("/customerContributionRanking")
    @Operation(summary = "客户金额贡献排名")
    public R customerContributionRanking(@DefaultType Integer type) {
        List<CustomerContributionRankingDto> list = homeService.customerContributionRanking(type);
    public R customerContributionRanking(@DefaultType Integer type,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate) {
        List<CustomerContributionRankingDto> list = homeService.customerContributionRanking(type, startDate, endDate);
        return R.ok(list);
    }
    @GetMapping("/productSalesAnalysis")
    @Operation(summary = "各产品销售金额分析")
    public R productSalesAnalysis() {
        List<MapDto> list = homeService.productSalesAnalysis();
    public R productSalesAnalysis(
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate) {
        List<MapDto> list = homeService.productSalesAnalysis(startDate, endDate);
        return R.ok(list);
    }
    @GetMapping("/rawMaterialPurchaseAmountRatio")
    @Operation(summary = "原材料采购金额占比")
    public R rawMaterialPurchaseAmountRatio(){
        List<MapDto> list = homeService.rawMaterialPurchaseAmountRatio();
    public R rawMaterialPurchaseAmountRatio(
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate){
        List<MapDto> list = homeService.rawMaterialPurchaseAmountRatio(startDate, endDate);
        return R.ok(list);
    }
@@ -261,7 +268,7 @@
        }
        Integer statusFromParam = parseOrderStatus(status);
        if (!isBlank(status) && statusFromParam == null && !"all".equalsIgnoreCase(status.trim())) {
            return R.fail("status参数不合法,可选值:all/waiting/inProgress/completed/end æˆ– 1/2/3/5");
            return R.fail("status参数不合法,可选值:all/waiting/inProgress/completed/paused æˆ– 1/2/3/4");
        }
        Integer queryStatus = resolveOrderStatus(status, tab);
@@ -283,7 +290,7 @@
        long waitingCount = 0L;
        long inProgressCount = 0L;
        long completedCount = 0L;
        long endCount = 0L;
        long pausedCount = 0L;
        List<Map<String, Object>> statusCountRows = productionOrderMapper.countHomeOrderProgressByStatus(startTime, endTime);
        if (statusCountRows != null) {
            for (Map<String, Object> countRow : statusCountRows) {
@@ -295,8 +302,8 @@
                    inProgressCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_COMPLETED)) {
                    completedCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_END)) {
                    endCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_PAUSED)) {
                    pausedCount = cnt;
                }
            }
        }
@@ -311,7 +318,7 @@
        result.put("waitingCount", waitingCount);
        result.put("inProgressCount", inProgressCount);
        result.put("completedCount", completedCount);
        result.put("endCount", endCount);
        result.put("pausedCount", pausedCount);
        result.put("records", records);
        return R.ok(result);
    }
@@ -474,22 +481,28 @@
    @GetMapping("/salesPurchaseStorageProductCount")
    @Operation(summary = "销售-采购-储存产品数")
    public R salesPurchaseStorageProductCount(){
        List<MapDto> list = homeService.salesPurchaseStorageProductCount();
    public R salesPurchaseStorageProductCount(
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate){
        List<MapDto> list = homeService.salesPurchaseStorageProductCount(startDate, endDate);
        return R.ok(list);
    }
    @GetMapping("/productInOutAnalysis")
    @Operation(summary = "产品出入库分析")
    public R productInOutAnalysis(@DefaultType Integer type){
        List<Map<String, Object>> result = homeService.productInOutAnalysis(type);
    public R productInOutAnalysis(@DefaultType Integer type,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate){
        List<Map<String, Object>> result = homeService.productInOutAnalysis(type, startDate, endDate);
        return R.ok(result);
    }
    @GetMapping("/productTurnoverDays")
    @Operation(summary = "产品周转天数")
    public R productTurnoverDays(){
        List<MapDto> list = homeService.productTurnoverDays();
    public R productTurnoverDays(
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate){
        List<MapDto> list = homeService.productTurnoverDays(startDate, endDate);
        return R.ok(list);
    }
@@ -531,8 +544,8 @@
        if ("3".equals(normalized) || "completed".equals(normalized)) {
            return ORDER_STATUS_COMPLETED;
        }
        if ("5".equals(normalized) || "end".equals(normalized)) {
            return ORDER_STATUS_END;
        if ("4".equals(normalized) || "paused".equals(normalized)) {
            return ORDER_STATUS_PAUSED;
        }
        return null;
    }
@@ -544,8 +557,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "completed";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "end";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "paused";
        }
        if (Objects.equals(status, ORDER_STATUS_WAIT)) {
            return "waiting";
@@ -563,8 +576,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "completed";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "end";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "paused";
        }
        return "all";
    }
@@ -579,8 +592,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "已完成";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "已结束";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "已暂停";
        }
        return "未知";
    }
@@ -637,7 +650,7 @@
                .select(ProductionOrder::getQuantity, ProductionOrder::getCompleteQuantity)
                .ge(ProductionOrder::getCreateTime, day.atStartOfDay())
                .lt(ProductionOrder::getCreateTime, day.plusDays(1).atStartOfDay())
                .ne(ProductionOrder::getStatus, ORDER_STATUS_END));
                .ne(ProductionOrder::getStatus, ORDER_STATUS_PAUSED));
        BigDecimal totalQuantity = BigDecimal.ZERO;
        BigDecimal totalCompleteQuantity = BigDecimal.ZERO;
src/main/java/com/ruoyi/home/mapper/HomeMapper.java
@@ -2,19 +2,24 @@
import com.ruoyi.dto.MapDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * é¦–页统计 Mapper
 *
 * @author antigravity
 */
@Mapper
public interface HomeMapper {
    /**
     * æŸ¥è¯¢äº§å“å‘¨è½¬å¤©æ•°
     *
     *
     * @return äº§å“å‘¨è½¬å¤©æ•°åˆ—表
     */
    List<MapDto> productTurnoverDays();
    List<MapDto> productTurnoverDaysByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * æŸ¥è¯¢é‡‡è´­å“å¤§ç±»åˆ†å¸ƒï¼ˆå„产品小类的采购数量/金额)
     *
     * @return é‡‡è´­åˆ†å¸ƒåˆ—表
     */
    List<MapDto> selectPurchaseCategoryDistribution();
}
src/main/java/com/ruoyi/home/service/HomeService.java
@@ -38,23 +38,23 @@
    HomeSummaryDto summaryStatistics();
    List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type);
    List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDate, String endDate);
    CustomerRevenueAnalysisDto customerRevenueAnalysis(Long customerId, Integer type);
    ProductCategoryDistributionDto productCategoryDistribution();
    List<CustomerContributionRankingDto> customerContributionRanking(Integer type);
    List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDate, String endDate);
    List<MapDto> productSalesAnalysis();
    List<MapDto> productSalesAnalysis(String startDate, String endDate);
    List<MapDto> rawMaterialPurchaseAmountRatio();
    List<MapDto> rawMaterialPurchaseAmountRatio(String startDate, String endDate);
    List<MapDto> salesPurchaseStorageProductCount();
    List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate);
    List<Map<String, Object>> productInOutAnalysis(Integer type);
    List<Map<String, Object>> productInOutAnalysis(Integer type, String startDate, String endDate);
    List<MapDto> productTurnoverDays();
    List<MapDto> productTurnoverDays(String startDate, String endDate);
    List<Map<String, Object>> incomeExpenseAnalysis(Integer type);
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -697,36 +697,39 @@
    }
    @Override
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type) {
        LocalDate today = LocalDate.now();
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        switch (type) {
            case 1: // å‘¨
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2: // æœˆ
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3: // å­£åº¦
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                return new ArrayList<>();
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            switch (type) {
                case 1: // å‘¨
                    startDate = today.with(DayOfWeek.MONDAY);
                    endDate = today.with(DayOfWeek.SUNDAY);
                    break;
                case 2: // æœˆ
                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                    break;
                case 3: // å­£åº¦
                    Month currentMonth = today.getMonth();
                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<PurchaseLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
                .ge("entry_date", startDate)
                .le("entry_date", endDate)
                // .ne("approval_status", 3)
                .groupBy("supplier_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
@@ -862,17 +865,81 @@
                majorDto.setRate("0.00");
            }
            // æŸ¥è¯¢å„产品小类的采购数量
            Map<String, BigDecimal> purchaseDataMap = new java.util.HashMap<>();
            List<com.ruoyi.dto.MapDto> purchaseDistribution = homeMapper.selectPurchaseCategoryDistribution();
            if (purchaseDistribution != null) {
                for (com.ruoyi.dto.MapDto item : purchaseDistribution) {
                    if (item.getName() != null && item.getValue() != null) {
                        purchaseDataMap.put(item.getName(), new BigDecimal(item.getValue()));
                    }
                }
            }
            BigDecimal majorTotalChildrenValue = BigDecimal.ZERO;
            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
            for (Product minor : children) {
                ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
                minorDto.setName(minor.getProductName());
                minorDto.setValue("0");
                BigDecimal minorValue = purchaseDataMap.getOrDefault(minor.getProductName(), BigDecimal.ZERO);
                minorDto.setValue(minorValue.toString());
                minorDto.setRate("0.00");
                minorDto.setChildren(new ArrayList<>());
                minorDtos.add(minorDto);
                majorTotalChildrenValue = majorTotalChildrenValue.add(minorValue);
            }
            majorDto.setChildren(minorDtos);
            // ä½¿ç”¨å®žé™…采购数据更新大类的 value
            if (majorTotalChildrenValue.compareTo(BigDecimal.ZERO) > 0) {
                majorDto.setValue(majorTotalChildrenValue.toString());
            }
            majorItems.add(majorDto);
        }
        // è®¡ç®—大类占比
        BigDecimal totalAllValue = BigDecimal.ZERO;
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            try {
                totalAllValue = totalAllValue.add(new BigDecimal(m.getValue()));
            } catch (NumberFormatException ignored) {
            }
        }
        if (totalAllValue.compareTo(BigDecimal.ZERO) > 0) {
            for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
                try {
                    BigDecimal val = new BigDecimal(m.getValue());
                    m.setRate(val.divide(totalAllValue, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP)
                            .toString());
                } catch (NumberFormatException ignored) {
                }
            }
        }
        // è®¡ç®—小类占比(在同大类内)
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            if (m.getChildren() == null || m.getChildren().isEmpty()) {
                continue;
            }
            BigDecimal parentVal = BigDecimal.ZERO;
            for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                try {
                    parentVal = parentVal.add(new BigDecimal(child.getValue()));
                } catch (NumberFormatException ignored) {
                }
            }
            if (parentVal.compareTo(BigDecimal.ZERO) > 0) {
                for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                    try {
                        BigDecimal childVal = new BigDecimal(child.getValue());
                        child.setRate(childVal.divide(parentVal, 4, RoundingMode.HALF_UP)
                                .multiply(new BigDecimal("100"))
                                .setScale(2, RoundingMode.HALF_UP)
                                .toString());
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
        }
        dto.setItems(majorItems);
@@ -880,38 +947,42 @@
    }
    @Override
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate = null;
        LocalDate endDate = null;
        switch (type) {
            case 1:
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2:
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3:
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            switch (type) {
                case 1:
                    startDate = today.with(DayOfWeek.MONDAY);
                    endDate = today.with(DayOfWeek.SUNDAY);
                    break;
                case 2:
                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                    break;
                case 3:
                    Month currentMonth = today.getMonth();
                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
                .isNotNull("customer_name")
                .between("entry_date", startDate, endDate)
                .groupBy("customer_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
        if (startDate != null && endDate != null) {
            queryWrapper.between("entry_date", startDate, endDate);
        }
        List<Map<String, Object>> maps = salesLedgerMapper.selectMaps(queryWrapper);
        List<CustomerContributionRankingDto> result = new ArrayList<>();
@@ -927,9 +998,13 @@
    }
    @Override
    public List<MapDto> productSalesAnalysis() {
        // èŽ·å–æ‰€æœ‰äº§å“å¤§ç±»çš„é”€å”®é¢
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
    public List<MapDto> productSalesAnalysis(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -964,9 +1039,13 @@
    }
    @Override
    public List<MapDto> rawMaterialPurchaseAmountRatio() {
        // èŽ·å–æ‰€æœ‰åŽŸææ–™çš„é‡‡è´­é¢
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
    public List<MapDto> rawMaterialPurchaseAmountRatio(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -1001,25 +1080,47 @@
    }
    @Override
    public List<MapDto> salesPurchaseStorageProductCount() {
    public List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonth = now.minusMonths(1);
        LocalDateTime lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        LocalDateTime currentMonthStart;
        LocalDateTime currentMonthEnd;
        LocalDateTime lastMonthStart;
        LocalDateTime lastMonthEnd;
        //  é”€å”®
        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, now);
        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
        //  é‡‡è´­
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, now);
        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
            LocalDate start = LocalDate.parse(startDate);
            LocalDate end = LocalDate.parse(endDate);
            currentMonthStart = start.atStartOfDay();
            currentMonthEnd = end.atTime(LocalTime.MAX);
            long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(start, end) + 1;
            LocalDate lastStart = start.minusDays(daysBetween);
            LocalDate lastEnd = start.minusDays(1);
            lastMonthStart = lastStart.atStartOfDay();
            lastMonthEnd = lastEnd.atTime(LocalTime.MAX);
        } else {
            currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            currentMonthEnd = now;
            LocalDateTime lastMonth = now.minusMonths(1);
            lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        }
        // é”€å”®è®¢å•数(统计sales_ledger表)
        int currentSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, currentMonthStart)
                .le(SalesLedger::getEntryDate, currentMonthEnd)));
        int lastSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, lastMonthStart)
                .le(SalesLedger::getEntryDate, lastMonthEnd)));
        // é‡‡è´­äº§å“æ•°
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, currentMonthEnd);
        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
        //  å‚¨å­˜
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, now);
        // å‚¨å­˜äº§å“æ•°
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthEnd);
        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
        List<MapDto> list = new ArrayList<>();
        list.add(createMapDto("销售产品数", currentSales, lastSales));
        list.add(createMapDto("销售订单数", currentSales, lastSales));
        list.add(createMapDto("采购产品数", currentPurchase, lastPurchase));
        list.add(createMapDto("储存产品数", currentStorage, lastStorage));
@@ -1048,7 +1149,7 @@
    }
    @Override
    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
    public List<Map<String, Object>> productInOutAnalysis(Integer type, String startDateStr, String endDateStr) {
        String targetName;
        if (type == 1) {
            targetName = "原材料";
@@ -1073,8 +1174,17 @@
        LocalDate now = LocalDate.now();
        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
        String startDate = now.minusDays(6).toString();
        String endDate = now.toString();
        LocalDate start;
        LocalDate end;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            start = LocalDate.parse(startDateStr);
            end = LocalDate.parse(endDateStr);
        } else {
            end = now;
            start = now.minusDays(6);
        }
        String startDate = start.toString();
        String endDate = end.toString();
        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
                endDate + " 23:59:59");
@@ -1096,8 +1206,9 @@
        List<Map<String, Object>> result = new ArrayList<>();
        for (int i = 6; i >= 0; i--) {
            LocalDate date = now.minusDays(i);
        long daysBetween = ChronoUnit.DAYS.between(start, end);
        for (int i = (int) daysBetween; i >= 0; i--) {
            LocalDate date = end.minusDays(i);
            Map<String, Object> dataPoint = new LinkedHashMap<>();
            dataPoint.put("date", date.format(displayDtf)); // 1/23
@@ -1111,7 +1222,10 @@
    }
    @Override
    public List<MapDto> productTurnoverDays() {
    public List<MapDto> productTurnoverDays(String startDateStr, String endDateStr) {
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            return homeMapper.productTurnoverDaysByDate(startDateStr, endDateStr);
        }
        return homeMapper.productTurnoverDays();
    }
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
@@ -15,12 +15,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
/**
 * @author :yys
 * @date : 2025/9/19 10:52
 */
@RestController
@Tag(name = "巡检任务记录")
@RequestMapping("/inspectionTask")
@@ -29,20 +23,13 @@
    private InspectionTaskService inspectionTaskService;
    /**
     * å·¡æ£€ä»»åŠ¡è¡¨è¡¨æŸ¥è¯¢
     */
    @GetMapping("/list")
    @Operation(summary = "巡检任务表表查询")
    @Operation(summary = "巡检任务表查询")
    public R<IPage<InspectionTaskDto>> list(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
        IPage<InspectionTaskDto> list = inspectionTaskService.selectInspectionTaskList(page, inspectionTaskDto);
        return R.ok(list);
    }
    /**
     * å¯¼å‡º
     */
    @PostMapping("/export")
    @Operation(summary = "导出定时任务记录")
    public void export(HttpServletResponse response) {
@@ -53,9 +40,6 @@
        util.exportExcel(response, list.getRecords(), "导出定时任务记录");
    }
    /**
     * å·¡æ£€ä»»åŠ¡è¡¨æ–°å¢žä¿®æ”¹
     */
    @PostMapping("/addOrEditInspectionTask")
    @Operation(summary = "巡检任务表新增修改")
    @Transactional(rollbackFor = Exception.class)
@@ -63,9 +47,6 @@
        return R.ok(inspectionTaskService.addOrEditInspectionTask(inspectionTaskDto));
    }
    /**
     * å·¡æ£€ä»»åŠ¡è¡¨åˆ é™¤
     */
    @DeleteMapping("/delInspectionTask")
    @Operation(summary = "巡检任务表删除")
    @Transactional(rollbackFor = Exception.class)
@@ -73,4 +54,18 @@
        return R.ok(inspectionTaskService.delByIds(ids));
    }
    @PutMapping("/complete/{id}")
    @Operation(summary = "巡检完成")
    @Transactional(rollbackFor = Exception.class)
    public R complete(@PathVariable Long id) {
        return R.ok(inspectionTaskService.complete(id));
    }
    @PutMapping("/batchComplete")
    @Operation(summary = "一键巡检完成")
    @Transactional(rollbackFor = Exception.class)
    public R batchComplete(String dateStr) {
        return R.ok(inspectionTaskService.batchComplete(dateStr));
    }
}
src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
@@ -82,6 +82,9 @@
    @Schema(description = "巡检地点详细描述")
    private String inspectionLocation;
    @Schema(description = "状态 0=待巡检, 1=已完成")
    private Integer status;
    @Schema(description = "软删除标志,0=未删除,1=已删除")
    private Integer deleted;
src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
@@ -6,10 +6,6 @@
import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
/**
 * @author :yys
 * @date : 2025/9/19 10:49
 */
public interface InspectionTaskService extends IService<InspectionTask> {
    IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto);
@@ -17,4 +13,8 @@
    int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto);
    int delByIds(Long[] ids);
    int complete(Long id);
    int batchComplete(String dateStr);
}
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -187,9 +187,63 @@
        // ä¿å­˜æ–‡ä»¶ï¼ˆå­—段不传则保留历史)
        saveInspectionAttachments(inspectionTask.getId(), inspectionTaskDto);
        // å·¡æ£€ç»“果设置后,自动标记为已完成
        if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionResult())) {
            InspectionTask statusUpdate = new InspectionTask();
            statusUpdate.setId(inspectionTask.getId());
            statusUpdate.setStatus(1);
            inspectionTaskMapper.updateById(statusUpdate);
        }
        return i;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int complete(Long id) {
        InspectionTask task = inspectionTaskMapper.selectById(id);
        if (task == null) {
            throw new IllegalArgumentException("巡检任务不存在");
        }
        if (task.getStatus() != null && task.getStatus() == 1) {
            throw new IllegalArgumentException("该巡检任务已完成");
        }
        task.setStatus(1);
        if (task.getInspectionResult() == null) {
            task.setInspectionResult("1");
        }
        return inspectionTaskMapper.updateById(task);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchComplete(String dateStr) {
        if (StringUtils.isBlank(dateStr)) {
            throw new IllegalArgumentException("日期参数不能为空");
        }
        java.time.LocalDate date = java.time.LocalDate.parse(dateStr);
        java.time.LocalDateTime startOfDay = date.atStartOfDay();
        java.time.LocalDateTime endOfDay = date.atTime(java.time.LocalTime.MAX);
        List<InspectionTask> tasks = inspectionTaskMapper.selectList(
                new LambdaQueryWrapper<InspectionTask>()
                        .eq(InspectionTask::getStatus, 0)
                        .ge(InspectionTask::getCreateTime, startOfDay)
                        .le(InspectionTask::getCreateTime, endOfDay));
        if (tasks.isEmpty()) {
            throw new IllegalArgumentException("该日期下没有待完成的巡检记录");
        }
        int count = 0;
        for (InspectionTask task : tasks) {
            task.setStatus(1);
            if (task.getInspectionResult() == null) {
                task.setInspectionResult("1");
            }
            inspectionTaskMapper.updateById(task);
            count++;
        }
        return count;
    }
    private void validateInspectionInput(InspectionTaskDto inspectionTaskDto, InspectionTask oldInspectionTask) {
        String inspectionResult = inspectionTaskDto.getInspectionResult();
        if (StringUtils.isBlank(inspectionResult) && oldInspectionTask != null) {
src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java
@@ -3,23 +3,18 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsOutboundDto;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 *  å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-02 03:59:56
 */
@RestController
@RequestMapping("/sparePartsRequisitionRecord")
@Tag(name = "备件领用记录接口")
@@ -28,9 +23,16 @@
    private SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
    @GetMapping("/listPage")
    @Operation(summary = "备件分类-分页查询")
    @Operation(summary = "备件领用记录-分页查询")
    public AjaxResult listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto){
        IPage<SparePartsRequisitionRecordDto> listPage = sparePartsRequisitionRecordService.listPage(page, sparePartsRequisitionRecordDto);
        return AjaxResult.success(listPage);
    }
    @PostMapping("/outbound")
    @Operation(summary = "备件独立出库")
    public AjaxResult outbound(@RequestBody SparePartsOutboundDto dto){
        sparePartsRequisitionRecordService.outbound(dto);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.measuringinstrumentledger.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "备件出库请求")
public class SparePartsOutboundDto {
    @Schema(description = "备件ID", requiredMode = Schema.RequiredMode.REQUIRED)
    private Long sparePartsId;
    @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED)
    private Integer quantity;
    @Schema(description = "设备ID(可选)")
    private Long deviceLedgerId;
    @Schema(description = "备注(可选)")
    private String remark;
}
src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java
@@ -3,17 +3,12 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsOutboundDto;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
/**
 * <p>
 *  æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-02 03:59:56
 */
public interface SparePartsRequisitionRecordService extends IService<SparePartsRequisitionRecord> {
    IPage<SparePartsRequisitionRecordDto> listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto);
    void outbound(SparePartsOutboundDto dto);
}
src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java
@@ -4,35 +4,58 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.SparePartsRequisitionRecordSourceTypeEnum;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsOutboundDto;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
import com.ruoyi.measuringinstrumentledger.mapper.SparePartsRequisitionRecordMapper;
import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
 * æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-02 03:59:56
 */
import java.math.BigDecimal;
@Service
@RequiredArgsConstructor
public class SparePartsRequisitionRecordServiceImpl extends ServiceImpl<SparePartsRequisitionRecordMapper, SparePartsRequisitionRecord> implements SparePartsRequisitionRecordService {
    private final SparePartsRequisitionRecordMapper sparePartsRequisitionRecordMapper;
    private final SparePartsMapper sparePartsMapper;
    @Override
    public IPage<SparePartsRequisitionRecordDto> listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto) {
        IPage<SparePartsRequisitionRecordDto> result = sparePartsRequisitionRecordMapper.listPage(page, sparePartsRequisitionRecordDto);
        // å¤„理来源类型文本
        result.getRecords().forEach(record -> {
            if (record.getSourceType() != null) {
                record.setSourceText(SparePartsRequisitionRecordSourceTypeEnum.getByCode(record.getSourceType()).getValue());
                SparePartsRequisitionRecordSourceTypeEnum typeEnum = SparePartsRequisitionRecordSourceTypeEnum.getByCode(record.getSourceType());
                if (typeEnum != null) {
                    record.setSourceText(typeEnum.getValue());
                }
            }
        });
        return result;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void outbound(SparePartsOutboundDto dto) {
        SpareParts spareParts = sparePartsMapper.selectById(dto.getSparePartsId());
        if (spareParts == null) {
            throw new IllegalArgumentException("备件不存在");
        }
        if (spareParts.getQuantity().compareTo(new BigDecimal(dto.getQuantity())) < 0) {
            throw new IllegalArgumentException("备件 " + spareParts.getName() + " åº“存不足");
        }
        // æ‰£å‡åº“å­˜
        spareParts.setQuantity(spareParts.getQuantity().subtract(new BigDecimal(dto.getQuantity())));
        sparePartsMapper.updateById(spareParts);
        // åˆ›å»ºé¢†ç”¨è®°å½•
        SparePartsRequisitionRecord record = new SparePartsRequisitionRecord();
        record.setSourceType(SparePartsRequisitionRecordSourceTypeEnum.SparePartsRequisitionRecordSourceTypeDirectOutbound.getCode());
        record.setSparePartsId(dto.getSparePartsId());
        record.setQuantity(dto.getQuantity());
        record.setDeviceLedgerId(dto.getDeviceLedgerId());
        this.save(record);
    }
}
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -20,8 +20,6 @@
import com.ruoyi.procurementrecord.service.ReturnManagementService;
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.ShippingInfoService;
import lombok.RequiredArgsConstructor;
@@ -46,7 +44,6 @@
    private final ReturnManagementMapper returnManagementMapper;
    private final ReturnSaleProductService returnSaleProductService;
    private final ShippingInfoService shippingInfoService;
    private final SalesLedgerMapper salesLedgerMapper;
    private final SalesRefundAmountOrderService salesRefundAmountOrderService;
    private final StockUtils stockUtils;
@@ -101,42 +98,26 @@
    public boolean handle(Long returnManagementId) {
        ReturnManagement byId = this.getById(returnManagementId);
        List<ReturnSaleProductDto> list = returnSaleProductService.listReturnSaleProduct(returnManagementId);
        // 1. è®¡ç®—正确的退款总额(金额=单价×数量)
        BigDecimal totalRefund = BigDecimal.ZERO;
        byId.setStatus(1);
        byId.setSettler(SecurityUtils.getLoginUser().getNickName());
        updateById(byId);
        SalesRefundAmountOrderDto salesRefundAmountOrder = new SalesRefundAmountOrderDto();
        salesRefundAmountOrder.setReturnManagementId(returnManagementId);
        salesRefundAmountOrder.setStatus(0);
        salesRefundAmountOrder.setCreateTime(byId.getCreateTime());
        salesRefundAmountOrder.setCreateUserId(SecurityUtils.getUserId());
        BigDecimal bigDecimal = new BigDecimal(0);
        for (ReturnSaleProductDto returnSaleProduct : list) {
            BigDecimal itemAmount = returnSaleProduct.getAmount() != null
                    ? returnSaleProduct.getAmount()
                    : BigDecimal.ZERO;
            totalRefund = totalRefund.add(itemAmount);
            // æ˜¯å¦æœ‰è´¨é‡é—®é¢˜
            bigDecimal = bigDecimal.add(returnSaleProduct.getPrice());
            salesRefundAmountOrder.setRefundedAmount(new BigDecimal(0));
            if (returnSaleProduct.getIsQuality() == 1) {
                stockUtils.addUnStockWithBatchNo(returnSaleProduct.getProductModelId(), returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(), returnSaleProduct.getId(), returnSaleProduct.getBatchNo());
            } else {
                stockUtils.addStockWithBatchNo(returnSaleProduct.getProductModelId(), returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(), returnSaleProduct.getId(), returnSaleProduct.getBatchNo());
            }
        }
        // 2. æ›´æ–°é€€è´§å•状态和退款总额
        byId.setStatus(1);
        byId.setSettler(SecurityUtils.getLoginUser().getNickName());
        byId.setRefundAmount(totalRefund);
        updateById(byId);
        // 3. æ›´æ–°é”€å”®å°è´¦çš„实际合同金额
        if (byId.getShippingId() != null) {
            ShippingInfo shippingInfo = shippingInfoService.getById(byId.getShippingId());
            if (shippingInfo != null && shippingInfo.getSalesLedgerId() != null) {
                SalesLedger salesLedger = salesLedgerMapper.selectById(shippingInfo.getSalesLedgerId());
                if (salesLedger != null && salesLedger.getContractAmount() != null) {
                    BigDecimal baseAmount = salesLedger.getNetContractAmount() != null
                            ? salesLedger.getNetContractAmount()
                            : salesLedger.getContractAmount();
                    salesLedger.setNetContractAmount(baseAmount.subtract(totalRefund));
                    salesLedgerMapper.updateById(salesLedger);
                }
            }
        }
        salesRefundAmountOrder.setRefundAmount(bigDecimal);
        salesRefundAmountOrder.setNotRefundedAmount(salesRefundAmountOrder.getRefundedAmount());
        return true;
    }
src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java
@@ -1,12 +1,17 @@
package com.ruoyi.purchase.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.service.purchase.AccountPurchasePaymentService;
import com.ruoyi.basic.service.ISupplierService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.purchase.dto.SavePaymentDto;
import com.ruoyi.purchase.dto.VatDto;
import com.ruoyi.purchase.dto.VatSummaryDto;
import com.ruoyi.purchase.service.PurchaseReportService;
import com.ruoyi.purchase.vo.PurchaseReportVo;
import io.swagger.v3.oas.annotations.Operation;
@@ -15,6 +20,7 @@
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -28,50 +34,71 @@
    private final ISupplierService supplierService;
    private final PurchaseReportService purchaseReportService;
    private final AccountPurchasePaymentService accountPurchasePaymentService;
    @GetMapping("/list")
    @Log(title = "采购报表-项目利润", businessType = BusinessType.OTHER)
    public R list(Page page, String customerName) {
        return R.ok(purchaseReportService.list(page,customerName));
        return R.ok(purchaseReportService.list(page, customerName));
    }
    @Log(title = "采购报表-项目利润导出", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @Operation(summary = "采购报表-项目利润导出")
    public void export(HttpServletResponse response, String customerName) {
        List<PurchaseReportVo> list = purchaseReportService.list(new Page(1,-1),customerName).getRecords();
        List<PurchaseReportVo> list = purchaseReportService.list(new Page(1, -1), customerName).getRecords();
        ExcelUtil<PurchaseReportVo> util = new ExcelUtil<>(PurchaseReportVo.class);
        util.exportExcel(response, list , "项目利润");
        util.exportExcel(response, list, "项目利润");
    }
    @Log(title = "采购报表-增值税比对", businessType = BusinessType.OTHER)
    @GetMapping("/listVat")
    public R listVat(Page page,String month) {
        return R.ok(purchaseReportService.listVat(page,month));
    public R listVat(Page page, String month, String type) {
        return R.ok(purchaseReportService.listVat(page, month, type));
    }
    @Log(title = "采购报表-增值税比对", businessType = BusinessType.EXPORT)
    @PostMapping("/exportTwo")
    @Operation(summary = "采购报表-增值税比对")
    public void exportTwo(HttpServletResponse response,String month) {
        List<VatDto> list = purchaseReportService.listVat(new Page(1,-1),month).getRecords();
    @Operation(summary = "采购报表-增值税比对导出")
    public void exportTwo(HttpServletResponse response, String month) {
        List<VatDto> list = purchaseReportService.listVat(new Page(1, -1), month, null).getRecords();
        ExcelUtil<VatDto> util = new ExcelUtil<>(VatDto.class);
        util.exportExcel(response, list , "增值税比对");
        util.exportExcel(response, list, "增值税比对");
    }
    @Log(title = "采购报表-增值税图表数据", businessType = BusinessType.OTHER)
    @GetMapping("/vatChart")
    @Operation(summary = "增值税进销项对比图表数据")
    public R vatChart(String year) {
        List<VatSummaryDto> list = purchaseReportService.vatSummary(year);
        return R.ok(list);
    }
    @GetMapping("/listVatDetail")
    @Operation(summary = "增值税明细-按发票/订单展示")
    public AjaxResult listVatDetail(Page page, String month) {
        IPage<VatDto> result = purchaseReportService.listVatDetail(page, month);
        return AjaxResult.success(result);
    }
    @GetMapping("/supplierTransactions")
    @Log(title = "供应商往来", businessType = BusinessType.OTHER)
    @Operation(summary = "供应商往来")
    public R supplierTransactions(Page page, String supplierName) {
        return R.ok(supplierService.supplierTransactions(page,supplierName));
        return R.ok(supplierService.supplierTransactions(page, supplierName));
    }
    @GetMapping("/supplierTransactionsDetails")
    @Log(title = "供应商往来明细", businessType = BusinessType.OTHER)
    @Operation(summary = "供应商往来明细")
    public R supplierTransactionsDetails(Page page, Long supplierId) {
        return R.ok(supplierService.supplierTransactionsDetails(page,supplierId));
        return R.ok(supplierService.supplierTransactionsDetails(page, supplierId));
    }
    @PostMapping("/savePayment")
    @Log(title = "新增付款", businessType = BusinessType.INSERT)
    @Operation(summary = "新增付款")
    public R savePayment(@RequestBody SavePaymentDto dto) {
        return R.ok(accountPurchasePaymentService.savePayment(dto));
    }
}
src/main/java/com/ruoyi/purchase/dto/SavePaymentDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.purchase.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * é‡‡è´­ç®¡ç†--供应商往来 æ–°å¢žä»˜æ¬¾è¯·æ±‚参数
 */
@Data
@Schema(name = "SavePaymentDto", description = "新增付款请求参数")
public class SavePaymentDto {
    @Schema(description = "供应商ID")
    private Long supplierId;
    @Schema(description = "采购合同号")
    private String purchaseContractNumber;
    @Schema(description = "付款日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate paymentDate;
    @Schema(description = "付款金额")
    private BigDecimal paymentAmount;
    @Schema(description = "付款方式")
    private String paymentMethod;
    @Schema(description = "备注")
    private String remark;
}
src/main/java/com/ruoyi/purchase/dto/VatDto.java
@@ -1,19 +1,59 @@
package com.ruoyi.purchase.dto;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@Schema(name = "VatDto", description = "管理驾驶舱--增值税比对参数")
@Schema(name = "VatDto", description = "增值税比对明细")
@ExcelIgnoreUnannotated
public class VatDto {
    @Excel(name = "ID")
    @Schema(description = "发票ID")
    private Long id;
    @Excel(name = "发票号码")
    @Schema(description = "发票号码")
    private String invoiceNumber;
    @Excel(name = "开票日期")
    @Schema(description = "开票日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate issueDate;
    @Excel(name = "类型")
    @Schema(description = "类型 sales-销项 purchase-进项")
    private String type;
    @Excel(name = "发票类型")
    @Schema(description = "发票类型")
    private String invoiceType;
    @Excel(name = "税额")
    @Schema(description = "税额")
    private BigDecimal taxAmount;
    @Excel(name = "价税合计")
    @Schema(description = "价税合计")
    private BigDecimal totalAmount;
    @Excel(name = "客户名称")
    @Schema(description = "客户名称")
    private String customerName;
    @Excel(name = "供应商名称")
    @Schema(description = "供应商名称")
    private String supplierName;
    @Excel(name = "月份")
    @Schema(description = "月份")
    private String month ;
    private String month;
    @Excel(name = "销项税额")
    @Schema(description = "销项税额")
@@ -23,8 +63,27 @@
    @Schema(description = "进项税额")
    private BigDecimal xTaxAmount;
    @Excel(name = "销-进")
    @Schema(description = "销-进")
    private BigDecimal taxAmount;
    // ========== æ˜Žç»†è§†å›¾å­—段 ==========
    @Excel(name = "发票号")
    @Schema(description = "发票号")
    private String invoiceNo;
    @Excel(name = "销售合同号")
    @Schema(description = "销售合同号")
    private String salesContractNo;
    @Excel(name = "订单类型")
    @Schema(description = "类型 è¿›é¡¹/销项")
    private String orderType;
    @Excel(name = "开票日期")
    @Schema(description = "开票日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate invoiceDate;
    @Excel(name = "税率")
    @Schema(description = "税率")
    private BigDecimal taxRate;
}
src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.purchase.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Schema(description = "增值税进销项按月汇总(图表用)")
public class VatSummaryDto {
    @Schema(description = "月份")
    private String month;
    @Schema(description = "销项税额合计")
    private BigDecimal jTaxAmount;
    @Schema(description = "进项税额合计")
    private BigDecimal xTaxAmount;
    @Schema(description = "差额(销-进)")
    private BigDecimal taxAmount;
}
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
@@ -119,12 +119,6 @@
     * åˆåŒé‡‘额(产品含税总价)
     */
    private BigDecimal contractAmount;
    /**
     * å®žé™…合同金额(扣减退货后)
     */
    @Schema(description = "实际合同金额")
    private BigDecimal netContractAmount;
//
//    /**
//     * ä¸šåŠ¡å‘˜
src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java
@@ -1,14 +1,20 @@
package com.ruoyi.purchase.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.purchase.dto.VatDto;
import com.ruoyi.purchase.dto.VatSummaryDto;
import com.ruoyi.purchase.vo.PurchaseReportVo;
import java.util.List;
public interface PurchaseReportService {
    IPage<PurchaseReportVo> list(Page page, String customerName);
    IPage<VatDto> listVat(Page page, String month);
    IPage<VatDto> listVat(Page page, String month, String type);
    List<VatSummaryDto> vatSummary(String year);
    IPage<VatDto> listVatDetail(Page page, String month);
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -162,10 +162,6 @@
        purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId());
        purchaseLedger.setApprovalStatus(1);
        // æ–°å¢žæ—¶åˆå§‹åŒ–实际合同金额
        if (purchaseLedger.getId() == null) {
            purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount());
        }
        // 3. æ–°å¢žæˆ–更新主表
        if (purchaseLedger.getId() == null) {
            if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.mapper.AccountStatementMapper;
import com.ruoyi.purchase.dto.VatDto;
import com.ruoyi.purchase.dto.VatSummaryDto;
import com.ruoyi.purchase.service.PurchaseReportService;
import com.ruoyi.purchase.vo.PurchaseReportVo;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
@@ -11,6 +12,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
@@ -26,7 +28,17 @@
    }
    @Override
    public IPage<VatDto> listVat(Page page, String month) {
        return accountStatementMapper.selectVatDtoPage(page, month);
    public IPage<VatDto> listVat(Page page, String month, String type) {
        return accountStatementMapper.selectVatDtoPage(page, month, type);
    }
    @Override
    public List<VatSummaryDto> vatSummary(String year) {
        return accountStatementMapper.selectVatSummary(year);
    }
    @Override
    public IPage<VatDto> listVatDetail(Page page, String month) {
        return accountStatementMapper.listVatDetail(page, month);
    }
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java
@@ -14,7 +14,6 @@
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.PurchaseReturnOrderProducts;
import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
@@ -29,7 +28,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -77,18 +75,6 @@
            }
        }else {
            throw new RuntimeException("请选择退货商品");
        }
        // æ›´æ–°é‡‡è´­å°è´¦çš„实际合同金额
        if (purchaseReturnOrderDto.getPurchaseLedgerId() != null && purchaseReturnOrderDto.getTotalAmount() != null) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseReturnOrderDto.getPurchaseLedgerId());
            if (purchaseLedger != null) {
                BigDecimal baseAmount = purchaseLedger.getNetContractAmount() != null
                        ? purchaseLedger.getNetContractAmount()
                        : purchaseLedger.getContractAmount();
                purchaseLedger.setNetContractAmount(baseAmount.subtract(purchaseReturnOrderDto.getTotalAmount()));
                purchaseLedgerMapper.updateById(purchaseLedger);
            }
        }
        return true;
    }
src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java
@@ -1,18 +1,22 @@
package com.ruoyi.sales.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.service.sales.AccountSalesCollectionService;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.sales.dto.SaveReceiptPaymentDto;
import com.ruoyi.sales.dto.StatisticsTableDto;
import com.ruoyi.sales.service.impl.MetricStatisticsServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -28,6 +32,7 @@
    private final MetricStatisticsServiceImpl metricStatisticsService;
    private final ICustomerService customerService;
    private final AccountSalesCollectionService accountSalesCollectionService;
    @Operation(summary = "头部总计")
    @GetMapping("/total")
@@ -55,4 +60,11 @@
        return R.ok(customerService.customewTransactionsDetails(page,customerId));
    }
    @PostMapping("/saveReceiptPayment")
    @Log(title = "新增回款", businessType = BusinessType.INSERT)
    @Operation(summary = "新增回款")
    public R saveReceiptPayment(@RequestBody SaveReceiptPaymentDto dto) {
        return R.ok(accountSalesCollectionService.saveReceiptPayment(dto));
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
@@ -7,13 +7,20 @@
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * äº§å“ä¿¡æ¯Controller
@@ -27,6 +34,9 @@
public class SalesLedgerProductController extends BaseController
{
    private final ISalesLedgerProductService salesLedgerProductService;
    private final ProcurementRecordService procurementRecordService;
    private final StockUtils stockUtils;
    private final PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper;
    /**
@@ -35,25 +45,26 @@
    @GetMapping("/list")
    public AjaxResult list(SalesLedgerProduct salesLedgerProduct) {
        List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(item -> {
                if (item.getApproveStatus() != 2) {
                    if (item.getHasSufficientStock() == 0) {
                        item.setApproveStatus(0);
                    } else {
                        item.setApproveStatus(1);
                    }
                }
                java.math.BigDecimal returnQuantity = item.getReturnQuantity() == null
                        ? java.math.BigDecimal.ZERO
                        : item.getReturnQuantity();
                java.math.BigDecimal quantity = item.getQuantity() == null
                        ? java.math.BigDecimal.ZERO
                        : item.getQuantity();
                item.setReturnQuantity(returnQuantity);
                item.setAvailableQuality(quantity.subtract(returnQuantity));
            });
        if (CollUtil.isEmpty(list)) {
            return AjaxResult.success(list);
        }
        List<Long> productIds = list.stream().map(SalesLedgerProduct::getProductModelId).collect(Collectors.toList());
        List<SimpleReturnOrderGroupDto> groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds);
        Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, item -> item.getSumReturnQuantity()));
        list.forEach(item -> {
            if (item.getApproveStatus() != 2) {
                if (item.getHasSufficientStock() == 0) {
                    item.setApproveStatus(0);
                } else {
                    item.setApproveStatus(1);
                }
            }
            // ç»Ÿè®¡é€€è´§æ•°é‡
            BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
            item.setReturnQuality(returnQuality);
            item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
        });
        return AjaxResult.success(list);
    }
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -19,7 +19,7 @@
    private String salesContractNo;
    private String customerContractNo;
    private String projectName;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date entryDate;
    private String entryDateStart;
src/main/java/com/ruoyi/sales/dto/SaveReceiptPaymentDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.sales.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * è¥é”€ç®¡ç†--客户往来 æ–°å¢žå›žæ¬¾è¯·æ±‚参数
 */
@Data
@Schema(name = "SaveReceiptPaymentDto", description = "新增回款请求参数")
public class SaveReceiptPaymentDto {
    @Schema(description = "客户ID")
    private Long customerId;
    @Schema(description = "销售合同号")
    private String salesContractNo;
    @Schema(description = "回款日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate paymentDate;
    @Schema(description = "回款金额")
    private BigDecimal paymentAmount;
    @Schema(description = "回款方式")
    private String paymentMethod;
    @Schema(description = "备注")
    private String remark;
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -32,8 +32,12 @@
    List<Map<String, Object>> selectProductSalesAnalysis();
    List<Map<String, Object>> selectProductSalesAnalysisByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
    List<Map<String, Object>> selectRawMaterialPurchaseAnalysis();
    List<Map<String, Object>> selectRawMaterialPurchaseAnalysisByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
    int selectProductCountByTypeAndDate(@Param("type") Integer type, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate);
    BigDecimal selectRawMaterialExpense();
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -50,7 +50,7 @@
    /**
     * å½•入日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date entryDate;
@@ -108,9 +108,6 @@
    @Schema(description = "付款方式")
    private String paymentMethod;
    @Schema(description = "实际合同金额")
    private BigDecimal netContractAmount;
    @TableField(exist = false)
    @Schema(description = "生产状态")
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -164,7 +164,11 @@
    @TableField(exist = false)
    private Integer hasSufficientStock;
    // å¯ç”¨æ•°é‡  quantity - returnQuantity
    // é€€è´§æ•°é‡
    @TableField(exist = false)
    private BigDecimal returnQuality;
    // å¯ç”¨æ•°é‡  quantity - returnQuality
    @TableField(exist = false)
    private BigDecimal availableQuality;
    @Schema(description = "创建用户")
@@ -181,10 +185,6 @@
    @TableField(exist = false)
    @Schema(description = "待发货数量")
    private BigDecimal noQuantity;
    @TableField(exist = false)
    @Schema(description = "退货数量")
    private BigDecimal returnQuantity;
    @TableField(exist = false)
    @Schema(description = "审批中数量")
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -98,27 +98,16 @@
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct);
        if(!CollectionUtils.isEmpty(salesLedgerProducts)){
            salesLedgerProducts.forEach(item -> {
                // å‘货信息(取最新一条)
                ShippingInfo latestShippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                // å‘货信息
                ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
                        .orderByDesc(ShippingInfo::getCreateTime)
                        .last("limit 1"));
                if(latestShippingInfo != null){
                    item.setShippingDate(latestShippingInfo.getShippingDate());
                    item.setExpressCompany(latestShippingInfo.getExpressCompany());
                    item.setExpressNumber(latestShippingInfo.getExpressNumber());
                }
                // è½¦ç‰Œå·ï¼šå–所有发货记录的车牌号,逗号拼接
                List<ShippingInfo> allShippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
                        .orderByAsc(ShippingInfo::getCreateTime));
                if(!CollectionUtils.isEmpty(allShippingInfos)){
                    String carNumbers = allShippingInfos.stream()
                            .map(ShippingInfo::getShippingCarNumber)
                            .filter(carNo -> carNo != null && !carNo.trim().isEmpty())
                            .distinct()
                            .collect(Collectors.joining(","));
                    item.setShippingCarNumber(carNumbers);
                if(shippingInfo != null){
                    item.setShippingDate(shippingInfo.getShippingDate());
                    item.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                    item.setExpressCompany(shippingInfo.getExpressCompany());
                    item.setExpressNumber(shippingInfo.getExpressNumber());
                }
            });
        }
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -38,6 +38,8 @@
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.sales.dto.*;
import com.ruoyi.sales.mapper.CommonFileMapper;
@@ -119,6 +121,9 @@
    @Autowired
    private ProductionProductMainService productionProductMainService;
    @Autowired
    private PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper;
    ;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
@@ -143,11 +148,17 @@
        productWrapper.eq(SalesLedgerProduct::getType, type.getCode());
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(productWrapper);
        if (type.equals(SaleEnum.PURCHASE)) {
            // æŸ¥è¯¢é€€è´§ä¿¡æ¯
            List<Long> productIds = salesLedgerProducts.stream().map(SalesLedgerProduct::getProductModelId).collect(Collectors.toList());
            List<SimpleReturnOrderGroupDto> groupListByProductIds = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(productIds)){
                groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds);
            }
            Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, SimpleReturnOrderGroupDto::getSumReturnQuantity));
            salesLedgerProducts.forEach(item -> {
                BigDecimal returnQuantity = item.getReturnQuantity() == null ? BigDecimal.ZERO : item.getReturnQuantity();
                item.setReturnQuantity(returnQuantity);
                BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity();
                item.setAvailableQuality(quantity.subtract(returnQuantity));
                BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
                item.setReturnQuality(returnQuality);
                item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
            });
        }
        return salesLedgerProducts;
@@ -590,18 +601,7 @@
            );
        }
        // 5. å…œåº•:确保实际合同金额有初始值等于合同金额
        if (salesLedger.getNetContractAmount() == null) {
            SalesLedger dbLedger = salesLedgerMapper.selectById(salesLedger.getId());
            if (dbLedger != null && dbLedger.getNetContractAmount() == null && dbLedger.getContractAmount() != null) {
                SalesLedger updateEntity = new SalesLedger();
                updateEntity.setId(salesLedger.getId());
                updateEntity.setNetContractAmount(dbLedger.getContractAmount());
                salesLedgerMapper.updateById(updateEntity);
            }
        }
        // 6. ä¿å­˜é”€å”®å°è´¦é™„ä»¶
        // 5. ä¿å­˜é”€å”®å°è´¦é™„ä»¶
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs());
        return 1;
@@ -738,15 +738,6 @@
            Field amountField = mainEntityClass.getDeclaredField("contractAmount");
            amountField.setAccessible(true);
            amountField.set(entity, totalAmount);
            // å®žé™…合同金额初始值等于合同金额
            try {
                Field netAmountField = mainEntityClass.getDeclaredField("netContractAmount");
                netAmountField.setAccessible(true);
                netAmountField.set(entity, totalAmount);
            } catch (NoSuchFieldException ignored) {
                // å­ç±»å¯èƒ½æ²¡æœ‰è¯¥å­—段,忽略
            }
            mainMapper.updateById(entity);
        } catch (Exception e) {
src/main/resources/application-bdsm.yml
ÎļþÒÑɾ³ý
src/main/resources/application-bhmy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-btw.yml
ÎļþÒÑɾ³ý
src/main/resources/application-cjny.yml
ÎļþÒÑɾ³ý
src/main/resources/application-ckgm.yml
ÎļþÒÑɾ³ý
src/main/resources/application-cmny.yml
ÎļþÒÑɾ³ý
src/main/resources/application-demo.yml
ÎļþÒÑɾ³ý
src/main/resources/application-dev.yml
@@ -74,7 +74,7 @@
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/product-inventory-management-bdpro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        url: jdbc:mysql://localhost:3306/product-inventory-management-new-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
      # ä»Žåº“数据源
src/main/resources/application-dhdc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hbkj.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hbtmblc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hbxm.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hckx.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hckxTest.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hcmy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hhkj.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hqjc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hsmy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hsxny.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hthb.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hxgy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hxsj.yml
ÎļþÒÑɾ³ý
src/main/resources/application-hysn.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jfhy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jjxm.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jlsn.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jsmy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jsyny.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jtwy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-jyhj.yml
ÎļþÒÑɾ³ý
src/main/resources/application-khl.yml
ÎļþÒÑɾ³ý
src/main/resources/application-khyy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-lsy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-mkzs.yml
ÎļþÒÑɾ³ý
src/main/resources/application-mqsp.yml
ÎļþÒÑɾ³ý
src/main/resources/application-mxsc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-myhb-pro.yml
ÎļþÒÑɾ³ý
src/main/resources/application-native.yml
ÎļþÒÑɾ³ý
src/main/resources/application-nhxcl.yml
ÎļþÒÑɾ³ý
src/main/resources/application-nydl.yml
ÎļþÒÑɾ³ý
src/main/resources/application-phmk.yml
ÎļþÒÑɾ³ý
src/main/resources/application-prod.yml
ÎļþÒÑɾ³ý
src/main/resources/application-qfsw.yml
ÎļþÒÑɾ³ý
src/main/resources/application-qlmc-pro.yml
ÎļþÒÑɾ³ý
src/main/resources/application-qlmc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-rfsy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-rzny.yml
ÎļþÒÑɾ³ý
src/main/resources/application-tjxm.yml
ÎļþÒÑɾ³ý
src/main/resources/application-tymk.yml
ÎļþÒÑɾ³ý
src/main/resources/application-wdsy.yml
ÎļþÒÑɾ³ý
src/main/resources/application-xhks.yml
ÎļþÒÑɾ³ý
src/main/resources/application-xyhb.yml
ÎļþÒÑɾ³ý
src/main/resources/application-ybkx-test.yml
ÎļþÃû´Ó src/main/resources/application-xlgs.yml ÐÞ¸Ä
@@ -73,7 +73,7 @@
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://172.17.0.1:3306/product-inventory-management-xlgs?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        url: jdbc:mysql://36.213.180.217:11101/product-inventory-management-ybkx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
@@ -145,14 +145,14 @@
  # redis é…ç½®
  data:
    mongodb:
      uri: mongodb://114.132.189.42:9028/chat_memory_db_xlgs
      uri: mongodb://114.132.189.42:9028/chat_memory_db_ybkx
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
@@ -260,7 +260,7 @@
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://1.15.17.182:9091 # åŸŸåå‰ç¼€
  domain: http://36.213.180.217:9522 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
src/main/resources/application-yss.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zdjc.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zjyj.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zqhx.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zxsq.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zyrq.yml
ÎļþÒÑɾ³ý
src/main/resources/application-zyrqCopy.yml
ÎļþÒÑɾ³ý
src/main/resources/mapper/account/AccountStatementMapper.xml
@@ -41,38 +41,129 @@
    ORDER BY lj.statement_month DESC
    </select>
    <select id="selectVatDtoPage" resultType="com.ruoyi.purchase.dto.VatDto">
    SELECT
        month,
        jTaxAmount,
        xTaxAmount,
        (jTaxAmount - xTaxAmount) AS taxAmount
    FROM (
        SELECT * FROM (
            SELECT
                asi.id,
                asi.invoice_number AS invoiceNumber,
                asi.issue_date AS issueDate,
                'sales' AS type,
                asi.invoice_type AS invoiceType,
                asi.tax_price AS taxAmount,
                asi.tax_inclusive_price AS totalAmount,
                c.customer_name AS customerName,
                NULL AS supplierName,
                DATE_FORMAT(asi.issue_date, '%Y-%m') AS month
            FROM account_sales_invoice asi
            LEFT JOIN customer c ON c.id = asi.customer_id
            WHERE (asi.status IS NULL OR asi.status = 0)
            UNION ALL
            SELECT
                api.id,
                api.invoice_number AS invoiceNumber,
                api.issue_date AS issueDate,
                'purchase' AS type,
                api.invoice_type AS invoiceType,
                api.tax_price AS taxAmount,
                api.tax_inclusive_price AS totalAmount,
                NULL AS customerName,
                s.supplier_name AS supplierName,
                DATE_FORMAT(api.issue_date, '%Y-%m') AS month
            FROM account_purchase_invoice api
            LEFT JOIN supplier_manage s ON s.id = api.supplier_id
            WHERE (api.status IS NULL OR api.status = 0)
        ) AS invoice_detail
        <where>
            <if test="month != null and month != ''">
                AND month = #{month}
            </if>
            <if test="type != null and type != ''">
                AND type = #{type}
            </if>
        </where>
        ORDER BY issueDate DESC, type
    </select>
    <select id="selectVatSummary" resultType="com.ruoyi.purchase.dto.VatSummaryDto">
        SELECT
            month,
            SUM(IF(type = 'purchase', tax_price, 0)) AS xTaxAmount,
            SUM(IF(type = 'sales', tax_price, 0)) AS jTaxAmount
            SUM(jTaxAmount) AS jTaxAmount,
            SUM(xTaxAmount) AS xTaxAmount,
            SUM(jTaxAmount) - SUM(xTaxAmount) AS taxAmount
        FROM (
            SELECT
                DATE_FORMAT(issue_date, '%Y-%m') AS month,
                tax_price,
                'sales' AS type
                SUM(tax_price) AS jTaxAmount,
                0 AS xTaxAmount
            FROM account_sales_invoice
            WHERE status != 1
            WHERE (status IS NULL OR status = 0)
            <if test="year != null and year != ''">
                AND YEAR(issue_date) = #{year}
            </if>
            GROUP BY DATE_FORMAT(issue_date, '%Y-%m')
            UNION ALL
            SELECT
                DATE_FORMAT(issue_date, '%Y-%m') AS month,
                tax_price,
                'purchase' AS type
                0 AS jTaxAmount,
                SUM(tax_price) AS xTaxAmount
            FROM account_purchase_invoice
            WHERE status != 1
        ) AS all_data
    GROUP BY month
    ) AS TT
     <where>
            <if test="month != null">
                and TT.month = #{month}
            WHERE (status IS NULL OR status = 0)
            <if test="year != null and year != ''">
                AND YEAR(issue_date) = #{year}
            </if>
     </where>
    ORDER BY TT.month
            GROUP BY DATE_FORMAT(issue_date, '%Y-%m')
        ) t
        GROUP BY month
        ORDER BY month
    </select>
    <select id="listVatDetail" resultType="com.ruoyi.purchase.dto.VatDto">
        SELECT *
        FROM (
            -- è¿›é¡¹ç¨Žæ˜Žç»†ï¼ˆé‡‡è´­è®¢å•)
            SELECT
                pl.purchase_contract_number AS invoiceNo,
                pl.sales_contract_no AS salesContractNo,
                pl.supplier_name AS supplierName,
                NULL AS customerName,
                '进项' AS orderType,
                pl.entry_date AS invoiceDate,
                slp.tax_rate AS taxRate,
                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS jTaxAmount,
                0 AS xTaxAmount,
                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS taxAmount
            FROM sales_ledger_product slp
            LEFT JOIN purchase_ledger pl ON pl.id = slp.sales_ledger_id
            WHERE slp.type = 2
              AND slp.tax_rate IS NOT NULL
              AND slp.tax_rate > 0
            GROUP BY pl.id
            UNION ALL
            -- é”€é¡¹ç¨Žæ˜Žç»†ï¼ˆé”€å”®è®¢å•)
            SELECT
                sl.sales_contract_no AS invoiceNo,
                sl.sales_contract_no AS salesContractNo,
                NULL AS supplierName,
                sl.customer_name AS customerName,
                '销项' AS orderType,
                sl.entry_date AS invoiceDate,
                slp.tax_rate AS taxRate,
                0 AS jTaxAmount,
                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS xTaxAmount,
                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS taxAmount
            FROM sales_ledger_product slp
            LEFT JOIN sales_ledger sl ON sl.id = slp.sales_ledger_id
            WHERE slp.type = 1
              AND slp.tax_rate IS NOT NULL
              AND slp.tax_rate > 0
            GROUP BY sl.id
        ) a
        <where>
            <if test="month != null and month != ''">
                AND DATE_FORMAT(a.invoiceDate, '%Y-%m') = #{month}
            </if>
        </where>
        ORDER BY a.invoiceDate DESC
    </select>
</mapper>
src/main/resources/mapper/account/purchase/AccountPurchasePaymentMapper.xml
@@ -54,5 +54,13 @@
        GROUP BY dateStr
        ORDER BY dateStr
    </select>
    <select id="selectStockInRecordIdsByPurchaseLedgerId" resultType="java.lang.Long">
        SELECT sir.id
        FROM stock_in_record sir
        LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
        WHERE sir.approval_status = 1
          AND sir.record_type IN ('7','10')
          AND IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) = #{purchaseLedgerId}
    </select>
</mapper>
src/main/resources/mapper/account/sales/AccountSalesCollectionMapper.xml
@@ -97,5 +97,13 @@
         GROUP BY dateStr
         ORDER BY dateStr
    </select>
    <select id="selectStockOutRecordIdsBySalesLedgerId" resultType="java.lang.Long">
        SELECT sor.id
        FROM stock_out_record sor
        JOIN shipping_info s ON sor.record_id = s.id
        WHERE s.sales_ledger_id = #{salesLedgerId}
          AND sor.record_type = '13'
          AND sor.approval_status = 1
    </select>
</mapper>
src/main/resources/mapper/basic/CustomerMapper.xml
@@ -174,14 +174,20 @@
        left join (
            select
                sl.id,
                sum(ascc.collection_amount) as receiptPaymentAmount
            from account_sales_collection ascc
            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
            WHERE sor.record_type='13'
              and sor.approval_status=1
            group by  sl.id
                sum(t.collection_amount) as receiptPaymentAmount
            from (
                select
                    ascc.collection_amount,
                    s.sales_ledger_id
                from account_sales_collection ascc
                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
                left join shipping_info s on sor.record_id = s.id
                where sor.record_type='13'
                  and sor.approval_status=1
                group by ascc.id, s.sales_ledger_id
            ) t
            left join sales_ledger sl on t.sales_ledger_id = sl.id
            group by sl.id
        )T1 on T1.id = sl.id
        left join (
            SELECT
src/main/resources/mapper/basic/SupplierManageMapper.xml
@@ -121,17 +121,25 @@
       left join (
           select
               pl.id,
               sum(app.payment_amount) as paymentAmount
           from account_purchase_payment app
           left join account_payment_application apa on app.account_payment_application_id = apa.id
           left join stock_in_record sir on FIND_IN_SET(sir.id, apa.stock_in_record_ids) > 0
               -- 10 ç±»åž‹æ‰å…³è”质检表
           LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
               -- åŠ¨æ€å…³è”é‡‡è´­ï¼ˆè‡ªåŠ¨é€‚é… 7 å’Œ 10)
           LEFT JOIN purchase_ledger pl
                     ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
           WHERE sir.approval_status = 1
             AND sir.record_type IN ('7','10')
               sum(t.payment_amount) as paymentAmount
           from (
               select
                   app.id,
                   app.payment_amount,
                   pl.id as purchase_ledger_id
               from account_purchase_payment app
               left join account_payment_application apa on app.account_payment_application_id = apa.id
               left join stock_in_record sir on FIND_IN_SET(sir.id, apa.stock_in_record_ids) > 0
                   -- 10 ç±»åž‹æ‰å…³è”质检表
               LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
                   -- åŠ¨æ€å…³è”é‡‡è´­ï¼ˆè‡ªåŠ¨é€‚é… 7 å’Œ 10)
               LEFT JOIN purchase_ledger pl
                         ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
               WHERE sir.approval_status = 1
                 AND sir.record_type IN ('7','10')
               group by app.id, pl.id
           ) t
           left join purchase_ledger pl on t.purchase_ledger_id = pl.id
           group by pl.id
       )T1 on T1.id = pl.id
       left join (
src/main/resources/mapper/home/HomeMapper.xml
@@ -13,4 +13,28 @@
        GROUP BY p.id, p.product_name
    </select>
    <select id="productTurnoverDaysByDate" resultType="com.ruoyi.dto.MapDto">
        SELECT p.product_name                                                      AS name,
               DATEDIFF(IFNULL(MAX(sor.create_time), NOW()), MAX(sir.create_time)) AS value
        FROM product p
                 JOIN product_model pm ON p.id = pm.product_id
                 JOIN stock_in_record sir ON pm.id = sir.product_model_id
                        AND sir.create_time &gt;= #{startDate}
                        AND sir.create_time &lt;= #{endDate}
                 LEFT JOIN stock_out_record sor ON pm.id = sor.product_model_id
        GROUP BY p.id, p.product_name
    </select>
    <select id="selectPurchaseCategoryDistribution" resultType="com.ruoyi.dto.MapDto">
        SELECT p2.product_name   AS name,
               COUNT(DISTINCT slp.id) AS value
        FROM sales_ledger_product slp
                 LEFT JOIN product p ON p.id = slp.product_category
                 LEFT JOIN product p2 ON p2.id = p.parent_id
        WHERE slp.type = 2
          AND p.parent_id IS NOT NULL
        GROUP BY p2.product_name
        ORDER BY value DESC
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -56,7 +56,6 @@
        T1.attachment_materials,
        T1.tenant_id,
        T1.contract_amount,
        T1.net_contract_amount,
        T1.contract_amount AS noInvoiceAmountTotal,
        T1.execution_date,
        T2.nick_name AS entry_person_name,
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -34,7 +34,6 @@
        ELSE 0
        END as has_sufficient_stock,
        (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0) - IFNULL(t5.pending_approval_quantity, 0)) as no_quantity,
        IFNULL(t6.return_quantity, 0) as return_quantity,
        IFNULL(t5.pending_approval_quantity, 0) as pending_approval_quantity,
        CASE
         WHEN IFNULL(t3.shipped_quantity, 0) = 0 AND IFNULL(t5.pending_approval_quantity, 0) = 0 THEN '待发货'
@@ -114,14 +113,6 @@
           ))
        GROUP BY si.sales_ledger_product_id
        ) t5 ON t5.sales_ledger_product_id = T1.id
        LEFT JOIN (
        SELECT si.sales_ledger_product_id, IFNULL(SUM(rsp.num), 0) AS return_quantity
        FROM return_management rm
        INNER JOIN return_sale_product rsp ON rm.id = rsp.return_management_id
        INNER JOIN shipping_info si ON rm.shipping_id = si.id
        WHERE rm.status = 1
        GROUP BY si.sales_ledger_product_id
        ) t6 ON t6.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>
@@ -228,7 +219,29 @@
        LIMIT 5
    </select>
    <select id="selectProductSalesAnalysisByDate" resultType="java.util.Map">
        SELECT
            product_category as name,
            SUM( tax_inclusive_total_price ) AS value
        FROM
            sales_ledger_product
        WHERE
            type = 1
            AND register_date &gt;= #{startDate}
            AND register_date &lt;= #{endDate}
        GROUP BY
            product_category
        ORDER BY
            value DESC
        LIMIT 5
    </select>
    <select id="selectRawMaterialPurchaseAnalysis" resultType="java.util.Map">
        WITH RECURSIVE raw_material_tree AS (
            SELECT id FROM product WHERE product_name = '原材料' AND parent_id IS NULL
            UNION ALL
            SELECT p.id FROM product p INNER JOIN raw_material_tree rmt ON p.parent_id = rmt.id
        )
        SELECT
            pr.product_name AS name,
            SUM( slp.tax_inclusive_total_price ) AS value
@@ -237,6 +250,26 @@
            JOIN product pr ON slp.product_id = pr.id
        WHERE
            slp.type = 2
            AND pr.id IN (SELECT id FROM raw_material_tree)
        GROUP BY
            pr.id,
            pr.product_name
        ORDER BY
            value DESC
        LIMIT 5
    </select>
    <select id="selectRawMaterialPurchaseAnalysisByDate" resultType="java.util.Map">
        SELECT
            pr.product_name AS name,
            SUM( slp.tax_inclusive_total_price ) AS value
        FROM
            sales_ledger_product slp
            JOIN product pr ON slp.product_id = pr.id
        WHERE
            slp.type = 2
            AND slp.register_date &gt;= #{startDate}
            AND slp.register_date &lt;= #{endDate}
            AND pr.parent_id = ( SELECT id FROM product WHERE product_name = '原材料' )
        GROUP BY
            pr.id,
@@ -261,7 +294,7 @@
    <select id="selectRawMaterialExpense" resultType="java.math.BigDecimal">
        WITH RECURSIVE product_tree AS (SELECT id
                                        FROM product
                                        WHERE product_name = '原材料'
                                        WHERE product_name = '原材料' AND parent_id IS NULL
                                        UNION ALL
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -101,9 +101,8 @@
        order by sor.id
    </select>
    <select id="getShippingInfoByCustomerName" resultType="com.ruoyi.sales.pojo.ShippingInfo">
        select distinct si.* from shipping_info si
        select * from shipping_info si
        left join sales_ledger sl on si.sales_ledger_id = sl.id
        inner join stock_out_record sor on sor.record_id = si.id and sor.record_type = '13' and sor.approval_status = 1
        where sl.customer_name = #{customerName} and si.status = '审核通过'
        where  sl.customer_name = #{customerName}
    </select>
</mapper>