From a5d7ef82a926d72651bb35779a59883528a9d641 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期一, 18 五月 2026 11:58:24 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
---
doc/20260515_设备巡检异常联动维修单_前端联调文档.md | 170 +
src/main/java/com/ruoyi/stock/service/StockInventoryService.java | 2
src/main/java/com/ruoyi/procurementrecord/bean/dto/InventoryInformationDto.java | 2
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java | 2
src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java | 4
src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java | 25
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java | 4
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java | 12
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java | 11
src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java | 12
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingInfoVo.java | 17
src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java | 15
src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java | 237 +
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java | 57
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java | 2
src/main/java/com/ruoyi/stock/execl/StockUnInventoryExportData.java | 2
src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java | 3
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml | 4
src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java | 99
src/main/java/com/ruoyi/purchase/pojo/SalesLedgerProductTemplate.java | 4
doc/20260512_AccountSubject树形改造前端修改文档.md | 185 +
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDtoCopy.java | 2
src/main/resources/application-dev.yml | 4
src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java | 7
src/main/java/com/ruoyi/device/controller/DeviceRepairController.java | 10
src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java | 6
src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml | 8
src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java | 43
src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java | 3
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java | 53
src/main/java/com/ruoyi/device/pojo/DeviceRepair.java | 12
src/main/resources/mapper/purchase/PurchaseReturnOrderProductsMapper.xml | 4
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java | 6
src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java | 85
src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrderProducts.java | 20
src/main/java/com/ruoyi/account/bean/dto/PurchaseReturnDto.java | 3
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java | 41
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java | 51
src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java | 28
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDto.java | 2
src/main/java/com/ruoyi/purchase/dto/SimpleReturnOrderGroupDto.java | 2
src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java | 4
src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java | 55
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementUpdateDto.java | 2
src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderProductsDto.java | 2
src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java | 13
src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java | 2
src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml | 24
src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java | 24
src/main/java/com/ruoyi/account/controller/AccountSubjectController.java | 8
doc/前端联调文档-设备报修保养财务模块改造.md | 233 +
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml | 14
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java | 236 +
src/main/java/com/ruoyi/collaborativeApproval/service/impl/SealApplicationManagementServiceImpl.java | 9
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java | 48
src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java | 8
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java | 38
src/main/resources/mapper/sales/SalesQuotationMapper.xml | 2
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java | 3
src/main/java/com/ruoyi/account/bean/dto/PurchaseInboundDto.java | 3
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java | 2
src/main/java/com/ruoyi/basic/dto/ProductModelDto.java | 2
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java | 6
src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java | 2
src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java | 35
src/main/java/com/ruoyi/projectManagement/service/impl/PlanServiceImpl.java | 5
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java | 6
src/main/java/com/ruoyi/account/bean/dto/SalesOutboundDto.java | 3
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java | 2
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml | 38
src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java | 27
src/main/java/com/ruoyi/purchase/vo/PurchaseReturnDetailsVo.java | 13
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java | 2
src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java | 22
src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java | 20
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java | 42
src/main/java/com/ruoyi/warehouse/mapper/DocumentationMapper.java | 1
src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java | 50
src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java | 2
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java | 2
doc/20260516_制造智能助手前端联调文档.md | 258 +
src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java | 26
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java | 2
src/main/resources/mapper/sales/ShippingInfoMapper.xml | 39
src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java | 125
src/main/java/com/ruoyi/account/mapper/AccountSubjectMapper.java | 5
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java | 598 ++++
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java | 3
src/main/java/com/ruoyi/ai/assistant/ManufacturingAgent.java | 21
src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java | 23
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementManagementUpdateDto.java | 2
src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java | 17
src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java | 83
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java | 1
src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml | 74
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java | 77
src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java | 12
src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java | 3
src/main/resources/mapper/basic/CustomerMapper.xml | 3
src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java | 3
src/main/java/com/ruoyi/purchase/mapper/PurchaseReturnOrdersMapper.java | 13
src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java | 101
src/main/java/com/ruoyi/project/system/controller/SysLoginController.java | 42
src/main/resources/logback.xml | 16
src/main/resources/manufacturing-agent-prompt.txt | 8
src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java | 63
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java | 15
src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java | 40
src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java | 307 ++
src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java | 12
src/main/java/com/ruoyi/procurementrecord/bean/dto/Details.java | 2
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java | 12
src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java | 250 +
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java | 1
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java | 107
src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java | 39
src/main/java/com/ruoyi/basic/pojo/ProductModel.java | 1
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java | 42
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java | 6
src/main/resources/mapper/stock/StockOutRecordMapper.xml | 6
src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java | 2
src/main/java/com/ruoyi/purchase/service/PurchaseReturnOrdersService.java | 6
src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java | 2
src/main/java/com/ruoyi/ai/tools/ManufacturingAgentTools.java | 1035 +++++++
src/main/java/com/ruoyi/production/pojo/ProductionAccount.java | 6
src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java | 362 ++
src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java | 32
src/main/resources/mapper/stock/StockInRecordMapper.xml | 10
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java | 10
src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java | 26
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java | 4
src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java | 2
src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java | 3
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml | 1
src/main/java/com/ruoyi/account/pojo/AccountSubject.java | 6
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java | 2
src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java | 37
src/main/java/com/ruoyi/projectManagement/service/impl/handle/InfoStageHandleService.java | 9
src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java | 4
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java | 2
src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java | 2
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java | 6
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java | 2
src/main/java/com/ruoyi/device/execl/DeviceRepairExeclDto.java | 12
src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java | 63
src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java | 231 +
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java | 2
src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java | 22
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementAddDto.java | 2
src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml | 3
src/main/java/com/ruoyi/framework/security/LoginUser.java | 5
.gitignore | 4
doc/20260512_财务管理模块前端联调文档.md | 288 ++
src/main/resources/mapper/account/AccountSubjectMapper.xml | 10
doc/20260512_add_parent_id_to_account_subject.sql | 5
src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml | 29
doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql | 22
src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java | 11
src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml | 82
src/main/java/com/ruoyi/account/service/AccountSubjectService.java | 8
src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml | 16
src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java | 13
src/main/java/com/ruoyi/collaborativeApproval/dto/SealApplicationManagementDTO.java | 8
src/main/java/com/ruoyi/device/service/IDeviceRepairService.java | 4
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java | 164
src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java | 17
src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java | 62
src/main/java/com/ruoyi/technology/service/TechnologyBomService.java | 2
src/main/java/com/ruoyi/account/bean/vo/AccountSubjectVo.java | 13
src/main/java/com/ruoyi/ai/config/ManufacturingAgentConfig.java | 20
src/main/java/com/ruoyi/ai/controller/ManufacturingAiController.java | 102
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java | 3
src/main/java/com/ruoyi/ai/assistant/ManufacturingIntentExecutor.java | 136
src/main/resources/mapper/stock/StockInventoryMapper.xml | 345 +
doc/20260512_create_financial_management_tables.sql | 104
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java | 20
src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java | 6
src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java | 13
/dev/null | 27
src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java | 98
src/main/java/com/ruoyi/account/bean/dto/SalesReturnDto.java | 3
src/main/resources/mapper/device/DeviceRepairMapper.xml | 14
src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java | 17
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java | 43
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java | 97
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java | 6
src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java | 69
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java | 10
188 files changed, 7,780 insertions(+), 749 deletions(-)
diff --git a/.gitignore b/.gitignore
index b60b03f..d3f800c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,7 @@
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
-
+claude.md
target/
!.mvn/wrapper/maven-wrapper.jar
@@ -43,4 +43,4 @@
!*/build/*.java
!*/build/*.html
-!*/build/*.xml
\ No newline at end of file
+!*/build/*.xml
diff --git "a/doc/20260512_AccountSubject\346\240\221\345\275\242\346\224\271\351\200\240\345\211\215\347\253\257\344\277\256\346\224\271\346\226\207\346\241\243.md" "b/doc/20260512_AccountSubject\346\240\221\345\275\242\346\224\271\351\200\240\345\211\215\347\253\257\344\277\256\346\224\271\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..c798c40
--- /dev/null
+++ "b/doc/20260512_AccountSubject\346\240\221\345\275\242\346\224\271\351\200\240\345\211\215\347\253\257\344\277\256\346\224\271\346\226\207\346\241\243.md"
@@ -0,0 +1,185 @@
+# AccountSubject 鏍戝舰鏀归�犲墠绔慨鏀规枃妗�
+
+鏇存柊鏃堕棿锛�2026-05-12
+
+## 1. 鍙樻洿鑳屾櫙
+
+`AccountSubjectController` 宸叉敼涓虹埗瀛愬眰绾ч�掑綊妯″紡锛宍/accountSubject/list` 鐜板湪杩斿洖鏍戝舰缁撴瀯锛坄children` 閫掑綊锛夛紝涓嶅啀鏄崟绾殑骞抽摵鍒楄〃銆�
+
+---
+
+## 2. 鍚庣鎺ュ彛鍙樺寲
+
+### 2.1 鏌ヨ鎺ュ彛锛堝凡鍙樻洿涓烘爲锛�
+
+- URL锛歚GET /accountSubject/list`
+- 鍏ュ弬锛氫繚鎸佷笉鍙橈紙`current,size,subjectCode,subjectName,subjectType,status`锛�
+- 鍑哄弬锛氫粛鏄垎椤靛3锛坄records,total`锛夛紝浣� `records` 鍙樹负鏍戣妭鐐规暟缁勶紙鏍硅妭鐐瑰垎椤碉紝瀛愯妭鐐归�掑綊鍐呭祵锛�
+
+绀轰緥锛�
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "records": [
+ {
+ "id": 1,
+ "parentId": null,
+ "subjectCode": "1002",
+ "subjectName": "閾惰瀛樻",
+ "subjectType": "璧勪骇绫�",
+ "balanceDirection": "鍊熸柟",
+ "status": 0,
+ "leaf": false,
+ "children": [
+ {
+ "id": 2,
+ "parentId": 1,
+ "subjectCode": "100201",
+ "subjectName": "宸ヨ瀛樻",
+ "subjectType": "璧勪骇绫�",
+ "balanceDirection": "鍊熸柟",
+ "status": 0,
+ "leaf": true,
+ "children": []
+ }
+ ]
+ }
+ ],
+ "total": 1
+ }
+}
+```
+
+### 2.2 鏂板/缂栬緫鎺ュ彛瀛楁鍙樺寲
+
+- `POST /accountSubject/add`
+- `PUT /accountSubject/edit`
+
+鏂板鏀寔瀛楁锛�
+
+- `parentId`锛氱埗鑺傜偣ID锛堜负绌鸿〃绀烘牴鑺傜偣锛�
+
+绀轰緥锛�
+
+```json
+{
+ "id": 2,
+ "parentId": 1,
+ "subjectCode": "100201",
+ "subjectName": "宸ヨ瀛樻",
+ "subjectType": "璧勪骇绫�",
+ "balanceDirection": "鍊熸柟",
+ "status": 0,
+ "remark": ""
+}
+```
+
+### 2.3 鍒犻櫎鎺ュ彛琛屼负鍙樺寲
+
+- `DELETE /accountSubject/remove/{ids}`
+
+琛屼负锛�
+
+1. 鍒犻櫎鐖惰妭鐐规椂浼氶�掑綊鍒犻櫎鎵�鏈夊瓙瀛欒妭鐐广��
+2. 鑻ヤ换鎰忓緟鍒犻櫎鑺傜偣锛堝惈瀛愬瓩锛夊凡琚� `fin_voucher_entry.subject_code` 寮曠敤锛屽垯鏁翠綋鍒犻櫎澶辫触銆�
+
+---
+
+## 3. 鍓嶇鏀归�犳竻鍗�
+
+## 3.1 鎬昏处绉戠洰绠$悊椤�
+
+鏂囦欢锛歚src/views/financialManagement/generalLedger/index.vue`
+
+### 蹇呮敼椤�
+
+1. 鏂板鈥滅埗绉戠洰鈥濋�夋嫨鎺т欢锛坄el-cascader` 鎴� `el-tree-select`锛夛紝淇濆瓨鏃跺甫 `parentId`銆�
+2. 鍒楄〃鏀逛负鏍戣〃灞曠ず锛堟帹鑽� `el-table` + `row-key` + `tree-props`锛夈��
+3. 鎼滅储閫昏緫淇濇寔涓嶅彉锛屼絾瑕佸吋瀹� `records` 涓烘爲缁撴瀯銆�
+
+---
+
+## 3.2 鍑瘉椤电鐩笅鎷�
+
+鏂囦欢锛歚src/views/financialManagement/voucher/index.vue`
+
+褰撳墠鍑瘉鍒嗗綍浣跨敤 `el-select`锛堝钩閾猴級銆�
+鍚庣宸茶繑鍥炴爲锛岄渶瑕佸墠绔墎骞冲寲鍚庡啀缁戝畾涓嬫媺銆�
+
+绀轰緥锛堝彲澶嶇敤锛夛細
+
+```js
+const flattenSubjectTree = (nodes, result = []) => {
+ (nodes || []).forEach(node => {
+ result.push({
+ code: node.subjectCode,
+ name: node.subjectName,
+ id: node.id,
+ parentId: node.parentId
+ });
+ if (node.children?.length) {
+ flattenSubjectTree(node.children, result);
+ }
+ });
+ return result;
+};
+
+// list 鎺ュ彛杩斿洖鍚庯細
+const treeRecords = response.data?.records || [];
+subjectList.value = flattenSubjectTree(treeRecords);
+```
+
+---
+
+## 3.3 绉戠洰鎬昏处/鏄庣粏璐﹂〉绾ц仈
+
+鏂囦欢锛�
+
+- `src/views/financialManagement/voucher/generalLedger.vue`
+- `src/views/financialManagement/voucher/detailLedger.vue`
+
+褰撳墠閫昏緫鏄妸 `records` 寮哄埗鏄犲皠鎴� `children: []`锛岄渶瑕佸垹闄よ繖娈碘�滃钩閾烘敼閫犫�濓紝鐩存帴浣跨敤鍚庣鏍戙��
+
+绀轰緥锛堝彲澶嶇敤锛夛細
+
+```js
+const toCascaderTree = (nodes = []) =>
+ nodes
+ .filter(item => item.subjectCode && item.subjectName)
+ .map(item => ({
+ code: item.subjectCode,
+ name: item.subjectName,
+ children: toCascaderTree(item.children || [])
+ }));
+
+subjectOptions.value = toCascaderTree(response.data?.records || []);
+```
+
+---
+
+## 4. 寤鸿鐨勫墠绔瓧娈电害瀹�
+
+寤鸿鍦ㄥ墠绔� `form` 澧炲姞锛�
+
+- `parentId: null`
+
+骞跺湪缂栬緫鍥炲~鏃朵繚鎸� `parentId`銆�
+
+---
+
+## 5. 鑱旇皟娉ㄦ剰浜嬮」
+
+1. `/accountSubject/list` 鐨� `total` 鏄牴鑺傜偣鏁伴噺锛屼笉鏄叏閲忚妭鐐规暟銆�
+2. 鑻ラ〉闈粛鎸夊钩閾� `records.map(...)` 澶勭悊锛屼細涓㈠け瀛愯妭鐐广��
+3. 鍒犻櫎绉戠洰澶辫触鏃讹紝浼樺厛妫�鏌ユ槸鍚﹁鍑瘉鍒嗗綍寮曠敤銆�
+4. 淇濆瓨澶辫触鍑虹幇鈥滅埗绉戠洰涓嶈兘鏄綋鍓嶇鐩垨鍏跺瓙绉戠洰鈥濇椂锛岄渶瑕佸墠绔檺鍒剁埗鑺傜偣鍙�夎寖鍥淬��
+
+---
+
+## 6. 鏁版嵁搴撳瓧娈佃姹�
+
+`account_subject` 闇�瑕佸寘鍚� `parent_id` 瀛楁锛坄bigint`锛屽彲绌猴級銆�
+鑻ョ嚎涓婂簱灏氭湭娣诲姞锛岃鍏堟墽琛� DDL 鍐嶈仈璋冦��
diff --git a/doc/20260512_add_parent_id_to_account_subject.sql b/doc/20260512_add_parent_id_to_account_subject.sql
new file mode 100644
index 0000000..cf33a33
--- /dev/null
+++ b/doc/20260512_add_parent_id_to_account_subject.sql
@@ -0,0 +1,5 @@
+-- account_subject 澧炲姞鐖剁骇绉戠洰瀛楁锛堟爲褰㈢粨鏋勶級
+ALTER TABLE `account_subject`
+ADD COLUMN `parent_id` bigint NULL COMMENT '鐖剁鐩甀D锛堜负绌鸿〃绀烘牴鑺傜偣锛�' AFTER `id`;
+
+CREATE INDEX `idx_account_subject_parent_id` ON `account_subject` (`parent_id`);
diff --git a/doc/20260512_create_financial_management_tables.sql b/doc/20260512_create_financial_management_tables.sql
new file mode 100644
index 0000000..db3caad
--- /dev/null
+++ b/doc/20260512_create_financial_management_tables.sql
@@ -0,0 +1,104 @@
+-- 璐㈠姟绠$悊妯″潡寤鸿〃鑴氭湰锛堝浐瀹氳祫浜�/鏃犲舰璧勪骇/鍑瘉/绉戠洰璐︼級
+-- 璇存槑锛�
+-- 1) 鎬昏处绉戠洰缁х画澶嶇敤宸叉湁琛� account_subject锛屼笉閲嶅鍒涘缓 fin_account_subject銆�
+-- 2) 閲戦瀛楁缁熶竴 decimal(18,2)銆�
+
+CREATE TABLE IF NOT EXISTS `fin_fixed_asset` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+ `asset_code` varchar(64) NOT NULL COMMENT '璧勪骇缂栧彿',
+ `asset_name` varchar(128) NOT NULL COMMENT '璧勪骇鍚嶇О',
+ `category` varchar(64) NOT NULL COMMENT '璧勪骇绫诲埆',
+ `specification` varchar(255) DEFAULT NULL COMMENT '瑙勬牸鍨嬪彿',
+ `purchase_date` date NOT NULL COMMENT '璐疆鏃ユ湡',
+ `original_value` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '璧勪骇鍘熷��',
+ `useful_life` int NOT NULL DEFAULT '1' COMMENT '浣跨敤骞撮檺(骞�)',
+ `residual_rate` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '娈嬪�肩巼(%)',
+ `accumulated_depreciation` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '绱鎶樻棫',
+ `net_value` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '鍑�鍊�',
+ `location` varchar(255) DEFAULT NULL COMMENT '瀛樻斁鍦扮偣',
+ `department` varchar(128) DEFAULT NULL COMMENT '浣跨敤閮ㄩ棬',
+ `keeper` varchar(64) DEFAULT NULL COMMENT '淇濈浜�',
+ `status` varchar(32) NOT NULL DEFAULT 'in_use' COMMENT '鐘舵��: in_use/idle/repair/scrapped',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `create_user` varchar(64) DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `update_user` varchar(64) DEFAULT NULL COMMENT '淇敼浜�',
+ `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_fin_fixed_asset_code` (`asset_code`),
+ KEY `idx_fin_fixed_asset_status` (`status`),
+ KEY `idx_fin_fixed_asset_category` (`category`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='鍥哄畾璧勪骇';
+
+CREATE TABLE IF NOT EXISTS `fin_intangible_asset` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+ `asset_code` varchar(64) NOT NULL COMMENT '璧勪骇缂栧彿',
+ `asset_name` varchar(128) NOT NULL COMMENT '璧勪骇鍚嶇О',
+ `category` varchar(64) NOT NULL COMMENT '璧勪骇绫诲埆',
+ `certificate_no` varchar(128) DEFAULT NULL COMMENT '璇佷功缂栧彿',
+ `acquisition_date` date NOT NULL COMMENT '鍙栧緱鏃ユ湡',
+ `original_value` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '璧勪骇鍘熷��',
+ `amortization_period` int NOT NULL DEFAULT '1' COMMENT '鎽婇攢骞撮檺(骞�)',
+ `residual_rate` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '娈嬪�肩巼(%)',
+ `accumulated_amortization` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '绱鎽婇攢',
+ `net_value` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '鍑�鍊�',
+ `validity_date` date DEFAULT NULL COMMENT '鏈夋晥鏈熻嚦',
+ `status` varchar(32) NOT NULL DEFAULT 'in_use' COMMENT '鐘舵��: in_use/expired/amortized',
+ `description` varchar(1000) DEFAULT NULL COMMENT '璧勪骇鎻忚堪',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `create_user` varchar(64) DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `update_user` varchar(64) DEFAULT NULL COMMENT '淇敼浜�',
+ `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_fin_intangible_asset_code` (`asset_code`),
+ KEY `idx_fin_intangible_asset_status` (`status`),
+ KEY `idx_fin_intangible_asset_category` (`category`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='鏃犲舰璧勪骇';
+
+CREATE TABLE IF NOT EXISTS `fin_voucher` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+ `voucher_no` varchar(64) NOT NULL COMMENT '鍑瘉瀛楀彿',
+ `voucher_date` date NOT NULL COMMENT '鍑瘉鏃ユ湡',
+ `summary` varchar(500) DEFAULT NULL COMMENT '鎽樿',
+ `debit` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '鍊熸柟鍚堣',
+ `credit` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '璐锋柟鍚堣',
+ `creator` varchar(64) DEFAULT NULL COMMENT '鍒跺崟浜�',
+ `status` varchar(32) NOT NULL DEFAULT 'unposted' COMMENT '鐘舵��: unposted/posted/cancelled',
+ `attachment_count` int NOT NULL DEFAULT '0' COMMENT '闄勪欢寮犳暟',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `create_user` varchar(64) DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `update_user` varchar(64) DEFAULT NULL COMMENT '淇敼浜�',
+ `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_fin_voucher_no` (`voucher_no`),
+ KEY `idx_fin_voucher_date` (`voucher_date`),
+ KEY `idx_fin_voucher_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='鍑瘉涓昏〃';
+
+CREATE TABLE IF NOT EXISTS `fin_voucher_entry` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+ `voucher_id` bigint NOT NULL COMMENT '鍑瘉ID',
+ `row_no` int NOT NULL DEFAULT '1' COMMENT '琛屽彿',
+ `subject_code` varchar(64) NOT NULL COMMENT '绉戠洰缂栫爜',
+ `subject_name` varchar(128) DEFAULT NULL COMMENT '绉戠洰鍚嶇О',
+ `summary` varchar(500) DEFAULT NULL COMMENT '鎽樿',
+ `debit` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '鍊熸柟閲戦',
+ `credit` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '璐锋柟閲戦',
+ `auxiliary_type` varchar(32) DEFAULT NULL COMMENT '杈呭姪鏍哥畻绫诲瀷',
+ `auxiliary_id` varchar(64) DEFAULT NULL COMMENT '杈呭姪鏍哥畻瀵硅薄ID',
+ `auxiliary_name` varchar(128) DEFAULT NULL COMMENT '杈呭姪鏍哥畻瀵硅薄鍚嶇О',
+ `create_user` varchar(64) DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `update_user` varchar(64) DEFAULT NULL COMMENT '淇敼浜�',
+ `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ PRIMARY KEY (`id`),
+ KEY `idx_fin_voucher_entry_voucher` (`voucher_id`),
+ KEY `idx_fin_voucher_entry_subject` (`subject_code`),
+ KEY `idx_fin_voucher_entry_aux` (`auxiliary_type`, `auxiliary_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='鍑瘉鍒嗗綍';
diff --git "a/doc/20260512_\350\264\242\345\212\241\347\256\241\347\220\206\346\250\241\345\235\227\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260512_\350\264\242\345\212\241\347\256\241\347\220\206\346\250\241\345\235\227\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..1804d82
--- /dev/null
+++ "b/doc/20260512_\350\264\242\345\212\241\347\256\241\347\220\206\346\250\241\345\235\227\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,288 @@
+# 璐㈠姟绠$悊妯″潡鍓嶇鑱旇皟鏂囨。锛坅ccount 妯″潡锛�
+
+鏇存柊鏃堕棿锛�2026-05-12
+
+## 1. 閫氱敤璇存槑
+
+### 1.1 鍝嶅簲缁撴瀯
+鎴愬姛鍝嶅簲锛�
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {}
+}
+```
+
+涓氬姟鏍¢獙澶辫触锛堜緥濡傚�熻捶涓嶅钩琛°�佸繀濉己澶憋級鐢卞叏灞�寮傚父杩斿洖锛�
+
+```json
+{
+ "code": 500,
+ "msg": "閿欒淇℃伅",
+ "data": null
+}
+```
+
+### 1.2 鍒嗛〉缁撴瀯
+鍒嗛〉鎺ュ彛缁熶竴浣跨敤 MyBatis-Plus `Page`锛�
+- 璇锋眰鍙傛暟锛歚current`銆乣size`
+- 杩斿洖锛歚data.records`銆乣data.total`
+
+---
+
+## 2. 鎬昏处绉戠洰锛堝凡鍦ㄥ師鎺ュ彛涓婂寮烘牎楠岋級
+
+鎺ュ彛淇濇寔涓嶅彉锛�
+- `GET /accountSubject/list`
+- `POST /accountSubject/add`
+- `PUT /accountSubject/edit`
+- `DELETE /accountSubject/remove/{ids}`
+- `POST /accountSubject/export`
+
+鏂板瑙勫垯锛�
+1. `subjectCode`銆乣subjectName`銆乣subjectType` 蹇呭~銆�
+2. `subjectCode` 鍞竴鏍¢獙銆�
+3. 鍒犻櫎鍓嶅仛寮曠敤鏍¢獙锛氳嫢琚嚟璇佸垎褰�(`fin_voucher_entry.subject_code`)寮曠敤锛岀姝㈠垹闄ゃ��
+
+---
+
+## 3. 鍥哄畾璧勪骇
+
+Base URL锛歚/financial/fixedAsset`
+
+### 3.1 鍒嗛〉鏌ヨ
+- `GET /page`
+- Query锛歚current,size,assetCode,assetName,category,status`
+
+### 3.2 鏂板
+- `POST /add`
+- Body锛圝SON锛夛細
+
+```json
+{
+ "assetCode": "GD20260512001",
+ "assetName": "鍔炲叕鐢佃剳",
+ "category": "electronic",
+ "specification": "ThinkPad X1",
+ "purchaseDate": "2026-05-01",
+ "originalValue": 8000.00,
+ "usefulLife": 5,
+ "residualRate": 5.00,
+ "location": "璐㈠姟閮�",
+ "department": "璐㈠姟閮�",
+ "keeper": "寮犱笁",
+ "status": "in_use",
+ "remark": "绀轰緥"
+}
+```
+
+### 3.3 淇敼
+- `PUT /update`
+- Body锛氬悓鏂板锛岄渶鍖呭惈 `id`
+
+### 3.4 鍒犻櫎
+- `DELETE /delete?ids=1&ids=2`
+
+### 3.5 鎶樻棫璁℃彁锛堟寜鏈堬級
+- `POST /depreciate`
+- Body 鍙�夛細
+ - 鍏ㄩ儴鍦ㄧ敤璧勪骇锛歚{}`
+ - 鎸囧畾璧勪骇锛歚{"ids":[1,2,3]}`
+
+鏍稿績鍏紡锛�
+- `monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)`
+- `accumulatedDepreciation += monthlyDepreciation`
+- `netValue = originalValue - accumulatedDepreciation`
+
+鐘舵�佸缓璁�硷細
+- `in_use`銆乣idle`銆乣repair`銆乣scrapped`
+
+---
+
+## 4. 鏃犲舰璧勪骇
+
+Base URL锛歚/financial/intangibleAsset`
+
+### 4.1 鍒嗛〉鏌ヨ
+- `GET /page`
+- Query锛歚current,size,assetCode,assetName,category,status`
+
+### 4.2 鏂板
+- `POST /add`
+
+```json
+{
+ "assetCode": "WX20260512001",
+ "assetName": "ERP杞欢璁稿彲",
+ "category": "software",
+ "certificateNo": "SW-001",
+ "acquisitionDate": "2026-05-01",
+ "originalValue": 50000.00,
+ "amortizationPeriod": 10,
+ "residualRate": 0.00,
+ "validityDate": "2036-05-01",
+ "status": "in_use",
+ "description": "绀轰緥",
+ "remark": ""
+}
+```
+
+### 4.3 淇敼
+- `PUT /update`
+- Body锛氬悓鏂板锛岄渶鍖呭惈 `id`
+
+### 4.4 鍒犻櫎
+- `DELETE /delete?ids=1&ids=2`
+
+### 4.5 鎽婇攢璁℃彁锛堟寜鏈堬級
+- `POST /amortize`
+- Body 鍙�夛細
+ - 鍏ㄩ儴鍦ㄧ敤璧勪骇锛歚{}`
+ - 鎸囧畾璧勪骇锛歚{"ids":[1,2,3]}`
+
+鏍稿績鍏紡锛�
+- `monthlyAmortization = originalValue * (1 - residualRate/100) / (amortizationPeriod*12)`
+- `accumulatedAmortization += monthlyAmortization`
+- `netValue = originalValue - accumulatedAmortization`
+- 褰� `netValue <= 0` 鏃讹細`netValue=0` 涓� `status=amortized`
+
+鐘舵�佸缓璁�硷細
+- `in_use`銆乣expired`銆乣amortized`
+
+---
+
+## 5. 鍑瘉
+
+Base URL锛歚/financial/voucher`
+
+### 5.1 鍒嗛〉鏌ヨ
+- `GET /page`
+- Query锛歚current,size,voucherNo,creator,status,startDate,endDate`
+
+### 5.2 鏂板
+- `POST /add`
+
+```json
+{
+ "voucherNo": "璁�-0001",
+ "voucherDate": "2026-05-12",
+ "summary": "閿�鍞敹鍏�",
+ "creator": "寮犱笁",
+ "attachmentCount": 0,
+ "remark": "",
+ "entries": [
+ {
+ "subjectCode": "1002",
+ "subjectName": "閾惰瀛樻",
+ "summary": "鏀跺埌璐ф",
+ "debit": 1000.00,
+ "credit": 0
+ },
+ {
+ "subjectCode": "6001",
+ "subjectName": "涓昏惀涓氬姟鏀跺叆",
+ "summary": "纭鏀跺叆",
+ "debit": 0,
+ "credit": 1000.00
+ }
+ ]
+}
+```
+
+### 5.3 淇敼
+- `PUT /update`
+- Body锛氬悓鏂板锛岄渶鍖呭惈 `id`
+- 浠� `unposted` 鐘舵�佸厑璁镐慨鏀�
+
+### 5.4 杩囪处
+- `POST /post`
+
+```json
+{
+ "id": 1
+}
+```
+
+### 5.5 浣滃簾
+- `POST /cancel`
+
+```json
+{
+ "id": 1
+}
+```
+
+### 5.6 璇︽儏
+- `GET /detail/{id}`
+
+鍏抽敭鏍¢獙锛�
+1. 鍒嗗綍鑷冲皯涓�鏉℃湁鏁堣锛堢鐩笉绌猴紝涓斿�熸柟鎴栬捶鏂� > 0锛夈��
+2. 姣忔潯鏈夋晥鍒嗗綍涓嶈兘鍊熻捶鍚屾椂澶т簬 0銆�
+3. 鍊熻捶骞宠 锛歚sum(debit) == sum(credit)` 涓旈兘 > 0銆�
+4. `subjectCode` 蹇呴』瀛樺湪浜� `account_subject`銆�
+
+鐘舵�佹祦杞細
+- `unposted -> posted`
+- `unposted -> cancelled`
+
+---
+
+## 6. 绉戠洰鎬昏处
+
+### 6.1 鏌ヨ鎺ュ彛
+- `GET /financial/ledger/general`
+
+### 6.2 Query 鍙傛暟
+- `subjectCode`锛堝繀濉級
+- `startMonth`锛圷YYY-MM锛�
+- `endMonth`锛圷YYY-MM锛�
+
+### 6.3 杩斿洖瀛楁
+- `rowType`锛歚opening` / `entry` / `monthly_total` / `yearly_total`
+- `date`
+- `voucherNo`
+- `summary`
+- `debit`
+- `credit`
+- `direction`锛堝��/璐凤級
+- `balance`锛堝�熸璐疯礋锛�
+
+璇存槑锛�
+- 绉戠洰鏀寔鈥滄寚瀹氱鐩強鍏朵笅绾э紙鍓嶇紑鍖归厤锛夆�濇煡璇€��
+- 鑷姩杈撳嚭鈥滄湡鍒濅綑棰� / 鏈湀鍚堣 / 鏈勾绱鈥濄��
+
+---
+
+## 7. 绉戠洰鏄庣粏璐�
+
+### 7.1 鏌ヨ鎺ュ彛
+- `GET /financial/ledger/detail`
+
+### 7.2 Query 鍙傛暟
+- `subjectCode`锛堝繀濉級
+- `auxiliaryType`锛堝彲閫夛細`customer/supplier/department/employee/project`锛�
+- `auxiliaryId`锛堝彲閫夛級
+- `startMonth`锛圷YYY-MM锛�
+- `endMonth`锛圷YYY-MM锛�
+
+### 7.3 杩斿洖瀛楁
+鍚岀鐩�昏处锛�
+- `rowType,date,voucherNo,summary,debit,credit,direction,balance`
+
+---
+
+## 8. 鏁版嵁搴撹剼鏈�
+
+宸叉彁渚涜剼鏈細
+
+- `doc/20260512_create_financial_management_tables.sql`
+
+鍖呭惈锛�
+- `fin_fixed_asset`
+- `fin_intangible_asset`
+- `fin_voucher`
+- `fin_voucher_entry`
+
+鎬昏处绉戠洰澶嶇敤鐜版湁 `account_subject`銆�
diff --git a/doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql b/doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql
new file mode 100644
index 0000000..7f34fe0
--- /dev/null
+++ b/doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql
@@ -0,0 +1,22 @@
+ALTER TABLE `inspection_task`
+ ADD COLUMN `inspection_project` VARCHAR(100) NULL COMMENT '宸℃椤圭洰' AFTER `task_name`;
+
+ALTER TABLE `inspection_task`
+ ADD COLUMN `inspection_result` VARCHAR(1) NULL COMMENT '宸℃缁撴灉 0寮傚父 1姝e父' AFTER `remarks`,
+ ADD COLUMN `abnormal_description` VARCHAR(500) NULL COMMENT '寮傚父鎻忚堪' AFTER `inspection_result`,
+ ADD COLUMN `device_repair_id` BIGINT NULL COMMENT '鍏宠仈缁翠慨鍗旾D' AFTER `abnormal_description`,
+ ADD COLUMN `acceptance_user_id` BIGINT NULL COMMENT '楠屾敹浜篒D' AFTER `device_repair_id`,
+ ADD COLUMN `acceptance_name` VARCHAR(100) NULL COMMENT '楠屾敹浜�' AFTER `acceptance_user_id`;
+
+ALTER TABLE `timing_task`
+ ADD COLUMN `inspection_project` VARCHAR(100) NULL COMMENT '宸℃椤圭洰' AFTER `task_name`,
+ ADD COLUMN `is_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '鏄惁鍚敤 0鍚� 1鏄�' AFTER `is_active`;
+
+CREATE INDEX `idx_inspection_task_device_repair_id`
+ ON `inspection_task` (`device_repair_id`);
+
+CREATE INDEX `idx_inspection_task_inspection_result`
+ ON `inspection_task` (`inspection_result`);
+
+CREATE INDEX `idx_timing_task_is_enabled`
+ ON `timing_task` (`is_enabled`);
diff --git "a/doc/20260515_\350\256\276\345\244\207\345\267\241\346\243\200\345\274\202\345\270\270\350\201\224\345\212\250\347\273\264\344\277\256\345\215\225_\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260515_\350\256\276\345\244\207\345\267\241\346\243\200\345\274\202\345\270\270\350\201\224\345\212\250\347\273\264\344\277\256\345\215\225_\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..0df5fcc
--- /dev/null
+++ "b/doc/20260515_\350\256\276\345\244\207\345\267\241\346\243\200\345\274\202\345\270\270\350\201\224\345\212\250\347\273\264\344\277\256\345\215\225_\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,170 @@
+# 璁惧宸℃涓庡畾鏃跺贰妫�鍓嶇鑱旇皟鏂囨。锛坕nspectiontask锛�
+
+> 鏇存柊鏃ユ湡锛�2026-05-15
+> 閫傜敤妯″潡锛氳澶囧贰妫�浠诲姟 `inspectiontask`锛坄/inspectionTask`锛変笌瀹氭椂宸℃浠诲姟锛坄/timingTask`锛�
+
+## 1. 鏈鏀瑰姩
+
+1. 宸℃浠诲姟鏂板瀛楁锛�
+ - `inspectionProject`锛堝贰妫�椤圭洰锛�
+ - `inspectionResult`锛堝贰妫�缁撴灉锛宍0`寮傚父 / `1`姝e父锛屽繀濉級
+ - `abnormalDescription`锛堝紓甯告弿杩帮級
+ - `deviceRepairId`锛堝叧鑱旂淮淇崟ID锛屽紓甯告椂鍚庣鑷姩鍥炲~锛�
+ - `acceptanceUserId`锛堥獙鏀朵汉ID锛�
+ - `acceptanceName`锛堥獙鏀朵汉锛�
+2. 寮傚父鏍¢獙瑙勫垯锛�
+ - `inspectionResult=1`锛堟甯革級锛氱収鐗囬潪蹇呭~銆�
+ - `inspectionResult=0`锛堝紓甯革級锛氬繀椤绘湁鐓х墖锛屼笖蹇呴』濉啓 `abnormalDescription`銆�
+3. 寮傚父鑱斿姩瑙勫垯锛�
+ - 寮傚父淇濆瓨鍚庤嚜鍔ㄧ敓鎴� `device_repair` 骞跺洖濉� `deviceRepairId`銆�
+4. 瀹氭椂浠诲姟鏂板瀛楁锛�
+ - `inspectionProject`锛堝贰妫�椤圭洰锛�
+ - `isEnabled`锛堟槸鍚﹀惎鐢紝`0`鍚� / `1`鏄級
+5. 澶囨敞甯﹀叆瑙勫垯锛�
+ - 瀹氭椂浠诲姟鑷姩鐢熸垚宸℃璁板綍鏃讹紝鑻ュ畾鏃朵换鍔� `remarks` 鏈夊�硷紝浼氭嫾鎺ュ埌宸℃璁板綍澶囨敞涓��
+
+## 2. 鏁版嵁搴撳彉鏇�
+
+鑱旇皟鍓嶆墽琛� SQL锛�
+
+- [doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql](/D:/鐗涢┈/鍗楅��/鍚庣/product-inventory-management-after-jdk25/doc/20260515_device_maintenance_inspection_abnormal_acceptance.sql)
+
+> 璇存槑锛氳鑴氭湰褰撳墠浣滅敤浜� `inspection_task` 涓� `timing_task` 涓ゅ紶琛紝鏂囦欢鍚嶅巻鍙蹭繚鐣欐湭鏀广��
+
+## 3. 宸℃浠诲姟鎺ュ彛
+
+### 3.1 淇濆瓨鎺ュ彛
+
+`POST /inspectionTask/addOrEditInspectionTask`
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| id | long | 鍚� | 鏈夊��=淇敼锛屾棤鍊�=鏂板 |
+| taskId | int | 寤鸿蹇呭~ | 璁惧ID锛堢敤浜庡紓甯歌嚜鍔ㄥ缓缁翠慨鍗曪級 |
+| taskName | string | 寤鸿蹇呭~ | 璁惧鍚嶇О |
+| inspectionProject | string | 鍚� | 宸℃椤圭洰 |
+| inspectorId | string | 鍚� | 宸℃浜篒D锛屾敮鎸侀�楀彿鍒嗛殧 |
+| inspectionResult | string | 鏄� | `0`=寮傚父锛宍1`=姝e父 |
+| abnormalDescription | string | 鏉′欢蹇呭~ | 寮傚父鏃跺繀濉� |
+| acceptanceUserId | long | 鍚� | 楠屾敹浜篒D |
+| acceptanceName | string | 鍚� | 楠屾敹浜哄鍚� |
+| commonFileListDTO | array | 鏉′欢蹇呭~ | 闄勪欢缁�1锛堝紓甯告椂涓夌粍鑷冲皯涓�缁勬湁鍥撅級 |
+| commonFileListAfterDTO | array | 鏉′欢蹇呭~ | 闄勪欢缁�2锛堝紓甯告椂涓夌粍鑷冲皯涓�缁勬湁鍥撅級 |
+| commonFileListBeforeDTO | array | 鏉′欢蹇呭~ | 闄勪欢缁�3锛堝紓甯告椂涓夌粍鑷冲皯涓�缁勬湁鍥撅級 |
+
+寮傚父绀轰緥锛�
+
+```json
+{
+ "taskId": 1001,
+ "taskName": "绌哄帇鏈篈-01",
+ "inspectionProject": "娑︽粦绯荤粺",
+ "inspectorId": "12",
+ "inspectionResult": "0",
+ "abnormalDescription": "鐢垫満寮傚搷锛屾俯鍗囧亸楂�",
+ "acceptanceUserId": 20,
+ "commonFileListDTO": [
+ {
+ "id": 90001,
+ "application": "file"
+ }
+ ]
+}
+```
+
+姝e父绀轰緥锛�
+
+```json
+{
+ "taskId": 1001,
+ "taskName": "绌哄帇鏈篈-01",
+ "inspectionProject": "鐐规",
+ "inspectorId": "12",
+ "inspectionResult": "1",
+ "acceptanceUserId": 20
+}
+```
+
+### 3.2 鍒楄〃鎺ュ彛
+
+`GET /inspectionTask/list`
+
+杩斿洖鍖呭惈鏂板瀛楁锛�
+
+- `inspectionProject`
+- `inspectionResult`
+- `abnormalDescription`
+- `deviceRepairId`
+- `acceptanceUserId`
+- `acceptanceName`
+
+## 4. 瀹氭椂浠诲姟鎺ュ彛
+
+### 4.1 淇濆瓨鎺ュ彛
+
+`POST /timingTask/addOrEditTimingTask`
+
+鏂板/鏇存柊瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| inspectionProject | string | 鍚� | 宸℃椤圭洰 |
+| remarks | string | 鍚� | 澶囨敞 |
+| isEnabled | int | 鍚� | `0`=绂佺敤锛宍1`=鍚敤锛涗笉浼犻粯璁ゅ惎鐢� |
+
+绀轰緥锛�
+
+```json
+{
+ "taskName": "绌哄帇鏈篈-01瀹氭椂宸℃",
+ "inspectionProject": "鏈堝害宸℃",
+ "taskId": 1001,
+ "inspectorIds": "12,13",
+ "frequencyType": "DAILY",
+ "frequencyDetail": "09:00",
+ "remarks": "閲嶇偣妫�鏌ヨ酱鎵挎俯搴�",
+ "isEnabled": 1
+}
+```
+
+### 4.2 鍚敤鐘舵�佽涓�
+
+1. `isEnabled=1`锛氫换鍔¤繘鍏ヨ皟搴︼紝鎸夐娆¤嚜鍔ㄧ敓鎴愬贰妫�璁板綍銆�
+2. `isEnabled=0`锛氫换鍔′笉璋冨害锛涘凡瀛樺湪璋冨害浼氳绉婚櫎銆�
+
+### 4.3 澶囨敞甯﹀叆瑙勫垯
+
+瀹氭椂浠诲姟鑷姩鐢熸垚宸℃璁板綍鏃讹細
+
+1. 宸℃璁板綍澶囨敞鍥哄畾鍓嶇紑锛歚鑷姩鐢熸垚鑷畾鏃朵换鍔D: {id}`
+2. 褰撳畾鏃朵换鍔� `remarks` 闈炵┖鏃讹紝鎷兼帴涓猴細
+ `鑷姩鐢熸垚鑷畾鏃朵换鍔D: {id}锛泏remarks}`
+
+## 5. 寮傚父鑷姩寤虹淮淇崟瑙勫垯
+
+褰撳贰妫�璁板綍 `inspectionResult=0` 鏃讹細
+
+1. 鑻� `deviceRepairId` 涓虹┖锛屽悗绔嚜鍔ㄥ垱寤� `device_repair`锛�
+ - `deviceLedgerId`锛氭潵鑷� `taskId`
+ - `deviceName`锛氫紭鍏� `taskName`锛屽惁鍒欏彇璁惧鍙拌处鍚嶇О
+ - `remark`锛氬紓甯告弿杩�
+ - `status`锛歚0`锛堝緟缁翠慨锛�
+2. 鑻ュ凡鏈夊叧鑱旂淮淇崟锛屼粎鍚屾鏇存柊缁翠慨鍗� `remark`銆�
+
+## 6. 鍓嶇鏀归�犲缓璁�
+
+1. 宸℃琛ㄥ崟鏂板 `inspectionProject` 杈撳叆妗嗐��
+2. 宸℃琛ㄥ崟淇濈暀鈥滄甯�/寮傚父鈥濊仈鍔ㄦ牎楠岋細
+ - 寮傚父鏃跺己鍒跺紓甯告弿杩� + 鑷冲皯涓�缁勫浘鐗囥��
+3. 瀹氭椂浠诲姟琛ㄥ崟鏂板鈥滄槸鍚﹀惎鐢ㄢ�濆紑鍏冲苟鏄犲皠 `isEnabled`銆�
+4. 瀹氭椂浠诲姟琛ㄥ崟鏂板 `inspectionProject` 涓� `remarks` 杈撳叆椤广��
+5. 宸℃鍒楄〃灞曠ず `inspectionProject` 鍜� `deviceRepairId`锛堟敮鎸佽烦杞淮淇崟璇︽儏锛夈��
+
+## 7. 鑱旇皟楠屾敹娓呭崟
+
+1. 宸℃鏂板/淇敼鍙纭彁浜� `inspectionProject` 骞跺湪鍒楄〃鍥炴樉銆�
+2. 寮傚父宸℃锛堟湁鎻忚堪+鏈夊浘锛変繚瀛樻垚鍔熷苟鍥炲~ `deviceRepairId`銆�
+3. 寮傚父宸℃缂烘弿杩版垨缂哄浘鐗囨椂琚嫤鎴��
+4. 瀹氭椂浠诲姟 `isEnabled=0` 鏃朵笉鍐嶈Е鍙戣嚜鍔ㄥ贰妫�璁板綍銆�
+5. 瀹氭椂浠诲姟 `isEnabled=1` 鏃舵寜棰戞鐢熸垚宸℃璁板綍銆�
+6. 瀹氭椂浠诲姟鏈� `remarks` 鏃讹紝鑷姩宸℃璁板綍澶囨敞甯︿笂璇ュ唴瀹广��
diff --git "a/doc/20260516_\345\210\266\351\200\240\346\231\272\350\203\275\345\212\251\346\211\213\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260516_\345\210\266\351\200\240\346\231\272\350\203\275\345\212\251\346\211\213\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..d4b9a1f
--- /dev/null
+++ "b/doc/20260516_\345\210\266\351\200\240\346\231\272\350\203\275\345\212\251\346\211\213\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,258 @@
+# 鍒堕�犳櫤鑳藉姪鎵嬪墠绔仈璋冩枃妗o紙`manufacturing-ai`锛�
+
+> 鏇存柊鏃ユ湡锛�2026-05-16
+> 閫傜敤妯″潡锛氱敓浜х幇鍦恒�佽鍒掋�佸伐鍗曘�佽澶囥�佽川閲忋�佺墿鏂欍�佸紓甯稿鐞�
+> 鑳藉姏鑼冨洿锛氭煡銆侀棶銆佸姙銆侀璀︺�佸垎鏋�
+
+## 1. 鎺ュ彛鎬昏
+
+1. 娴佸紡瀵硅瘽锛歚POST /manufacturing-ai/chat`
+2. 浼氳瘽鍒楄〃锛歚GET /manufacturing-ai/history/sessions`
+3. 浼氳瘽娑堟伅锛歚GET /manufacturing-ai/history/messages/{memoryId}`
+4. 鍒犻櫎浼氳瘽锛歚DELETE /manufacturing-ai/history/{memoryId}`
+
+璇存槑锛�
+- `/chat` 涓� **SSE/娴佸紡鏂囨湰** 杩斿洖锛坄text/stream;charset=utf-8`锛夈��
+- 鍛戒腑鈥滄煡/棰勮/鍒嗘瀽/鍔炩�濆伐鍏锋椂锛屾祦寮忔渶缁堝唴瀹规槸 **JSON 瀛楃涓�**锛堜笉鏄� `AjaxResult`锛夈��
+- 鏈懡涓伐鍏锋椂锛岃繑鍥炴櫘閫氳嚜鐒惰瑷�鏂囨湰銆�
+
+## 2. 閴存潈涓庤姹傚ご
+
+- 缁熶竴浣跨敤绯荤粺鐧诲綍鎬侊紙`Authorization` 涓庣幇鏈夋帴鍙d竴鑷达級銆�
+- `POST /manufacturing-ai/chat` 璇锋眰澶达細`Content-Type: application/json`銆�
+
+## 3. 瀵硅瘽鎺ュ彛
+
+### 3.1 璇锋眰
+
+```http
+POST /manufacturing-ai/chat
+Content-Type: application/json
+```
+
+```json
+{
+ "memoryId": "mfg-ai-001",
+ "message": "鏌ヨ澶囪タ闂ㄥ瓙鍙橀鍣ㄧ殑缁翠慨鎯呭喌"
+}
+```
+
+瀛楁璇存槑锛�
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+| --- | --- | --- | --- |
+| memoryId | string | 鏄� | 浼氳瘽 ID锛屽墠绔敓鎴愬苟澶嶇敤 |
+| message | string | 鏄� | 鐢ㄦ埛杈撳叆 |
+
+### 3.2 杩斿洖锛堟祦寮忥級
+
+```http
+Content-Type: text/stream;charset=utf-8
+```
+
+鍓嶇澶勭悊寤鸿锛�
+1. 鎸夋祦鎷兼帴瀹屾暣鏂囨湰銆�
+2. 灏濊瘯 `JSON.parse(fullText)`锛�
+ - 鎴愬姛锛氭寜缁撴瀯鍖栫粨鏋滄覆鏌撱��
+ - 澶辫触锛氭寜鏅�氳亰澶╂枃鏈覆鏌撱��
+
+## 4. 缁撴瀯鍖栧搷搴斿崗璁�
+
+### 4.1 閫氱敤缁撴瀯
+
+```json
+{
+ "success": true,
+ "type": "manufacturing_device_repair_list",
+ "description": "宸茶繑鍥炶澶囩淮淇褰曘��",
+ "summary": {},
+ "data": {},
+ "charts": {}
+}
+```
+
+### 4.2 `type` 鏋氫妇
+
+| type | 鍦烘櫙 |
+| --- | --- |
+| manufacturing_site_snapshot | 鐢熶骇鐜板満姒傝 |
+| manufacturing_plan_list | 鐢熶骇璁″垝鏌ヨ |
+| manufacturing_workorder_list | 宸ュ崟鏌ヨ |
+| manufacturing_device_list | 璁惧鍙拌处鏌ヨ |
+| manufacturing_device_repair_list | 璁惧缁翠慨璁板綍鏌ヨ |
+| manufacturing_quality_list | 璐ㄩ噺鏌ヨ |
+| manufacturing_material_list | 鐗╂枡搴撳瓨鏌ヨ |
+| manufacturing_exception_list | 寮傚父澶勭悊鏌ヨ |
+| manufacturing_warning | 棰勮鐪嬫澘 |
+| manufacturing_analysis | 缁忚惀鍒嗘瀽 |
+| manufacturing_action_plan | 鍔炵悊寤鸿锛堝姩浣滃崱锛� |
+
+## 5. 鈥滄煡鈥濊兘鍔涜仈璋冭鐐�
+
+### 5.1 璁惧鐩稿叧璺敱瑙勫垯锛堝叧閿級
+
+- 褰撶敤鎴疯緭鍏ュ寘鍚� `缁翠慨/鎶ヤ慨/妫�淇�/缁存姢`锛岃澶囧煙浼氳繑鍥� `manufacturing_device_repair_list`锛堟煡 `device_repair`锛夈��
+- 鏈寘鍚互涓婅瘝鏃讹紝杩斿洖 `manufacturing_device_list`锛堟煡璁惧鍙拌处锛夈��
+
+绀轰緥锛�
+- `鏌ヨ澶嘇-01` -> `manufacturing_device_list`
+- `鏌ヨ澶嘇-01缁翠慨鎯呭喌` -> `manufacturing_device_repair_list`
+
+### 5.2 缁翠慨璁板綍鏃堕棿杩囨护瑙勫垯锛堝叧閿級
+
+- 鐢ㄦ埛鏄庣‘甯︽椂闂存潯浠讹紙濡傗�滄湰鏈�/涓婂懆/杩�7澶�/2026-05-01 鍒� 2026-05-16鈥濓級鎵嶆寜鏃堕棿杩囨护缁翠慨璁板綍銆�
+- 鏈甫鏃堕棿鏉′欢鏃讹紝涓嶉粯璁ゆ寜杩� 30 澶╂埅鏂紝閬垮厤鍘嗗彶缁翠慨璁板綍琚杩囨护銆�
+
+### 5.3 鍏抽敭璇嶅鐞嗚鍒欙紙璁惧/缁翠慨锛�
+
+- 绯荤粺浼氭竻娲楀櫔闊宠瘝锛歚鏌ヨ/鏌ョ湅/璇�/璁惧/缁翠慨鎯呭喌/璁板綍/淇℃伅` 绛夈��
+- 鍚屾椂浼氶�氳繃璁惧鍙拌处鍖归厤 `deviceLedgerId` 鍏滃簳锛屽啀鍥炴煡缁翠慨璁板綍锛岄檷浣庘�滄湁鏁版嵁浣嗘煡涓嶅埌鈥濈殑姒傜巼銆�
+
+### 5.4 鍒楄〃缁撴灉绾﹀畾
+
+- 鍒楄〃鏁版嵁缁熶竴鍦� `data.items`
+- 缁熻鎽樿鍦� `summary`
+
+甯哥敤瀛楁锛�
+
+| type | 甯哥敤瀛楁 |
+| --- | --- |
+| manufacturing_plan_list | `mpsNo`, `requiredDate`, `status` |
+| manufacturing_workorder_list | `workOrderNo`, `planStartTime`, `planEndTime`, `status` |
+| manufacturing_device_list | `deviceName`, `deviceModel`, `pendingRepairCount` |
+| manufacturing_device_repair_list | `deviceName`, `deviceModel`, `repairTime`, `repairName`, `maintenanceName`, `status`, `createTime` |
+
+## 6. 鈥滈璀︹�濊仈璋冭鐐�
+
+- `type = manufacturing_warning`
+- 棰勮鏄庣粏鍦� `data.items`锛屾瘡椤瑰寘鍚細
+ - `level`锛歚high` / `medium`
+ - `title`
+ - `count`
+ - `detail`
+
+鐘舵�佸彛寰勶細
+- 璁惧鈥滃緟缁翠慨鈥濈粺璁℃寜 `status = 0` 璁$畻锛堜笉鍐嶆妸鍏朵粬鐘舵�佽鍏ュ緟缁翠慨锛夈��
+
+## 7. 鈥滃垎鏋愨�濊仈璋冭鐐�
+
+- `type = manufacturing_analysis`
+- 鍏抽敭鎸囨爣鍦� `summary`
+- 鎸囨爣鍗″湪 `data.coreMetrics`
+- 鍥捐〃閰嶇疆鍦� `charts`锛�
+ - `charts.domainBarOption`
+ - `charts.qualityPieOption`
+
+鍥捐〃閰嶇疆鍙洿鎺ョ粰 ECharts 浣跨敤銆�
+
+## 8. 鈥滃姙鈥濊兘鍔涜仈璋冭鐐�
+
+褰撳墠鈥滃姙鈥濅负 **鍔炵悊寤鸿妯″紡**锛圓I 杈撳嚭鍔ㄤ綔鍗★紝鍓嶇纭鍚庤皟鐢ㄧ洰鏍囦笟鍔℃帴鍙o級銆�
+
+- `type = manufacturing_action_plan`
+- 鍔ㄤ綔鍗℃暟缁勶細`data.actionCards`
+
+鍔ㄤ綔鍗″瓧娈碉細
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| code | 鍔ㄤ綔缂栫爜 |
+| name | 鍔ㄤ綔鍚嶇О |
+| method | 璇锋眰鏂规硶 |
+| targetApi | 鐩爣涓氬姟鎺ュ彛 |
+| requiredFields | 蹇呭~瀛楁 |
+| examplePayload | 绀轰緥鍙傛暟 |
+| description | 璇存槑 |
+
+鍐呯疆鍔ㄤ綔绀轰緥锛�
+1. `POST /productionOperationTask/assign`
+2. `POST /device/repair`
+3. `POST /quality/qualityUnqualified/deal`
+4. `POST /stockInventory/addstockInventory`
+5. `POST /procurementExceptionRecord/add`
+
+## 9. 浼氳瘽绠$悊鎺ュ彛
+
+### 9.1 浼氳瘽鍒楄〃
+
+```http
+GET /manufacturing-ai/history/sessions
+```
+
+`AjaxResult.data` 瀛楁锛�
+- `memoryId`
+- `title`
+- `lastMessage`
+- `messageCount`
+- `lastChatTime`
+
+### 9.2 浼氳瘽娑堟伅
+
+```http
+GET /manufacturing-ai/history/messages/{memoryId}
+```
+
+`AjaxResult.data` 瀛楁锛�
+- `role`锛歚user` / `assistant` / `system` / `tool`
+- `content`
+- `filePaths`
+
+### 9.3 鍒犻櫎浼氳瘽
+
+```http
+DELETE /manufacturing-ai/history/{memoryId}
+```
+
+杩斿洖鏍囧噯 `AjaxResult`銆�
+
+## 10. 閿欒涓庤竟鐣�
+
+`/chat` 甯歌杩斿洖鏂囨湰锛�
+- `memoryId涓嶈兘涓虹┖`
+- `message涓嶈兘涓虹┖`
+
+寤鸿鍓嶇鍙戦�佸墠鍏堝仛蹇呭~鏍¢獙銆�
+
+## 11. 鍓嶇鑱旇皟娴佺▼寤鸿
+
+1. 鐧诲綍鍚庡垱寤哄苟澶嶇敤 `memoryId`銆�
+2. 璋冪敤 `/manufacturing-ai/chat`锛屾寜 SSE 鎷兼帴瀹屾暣鏂囨湰銆�
+3. 鍏堝皾璇� JSON 瑙f瀽锛�
+ - 鎴愬姛锛氭寜 `type` 璺敱鍒板搴� UI锛堝垪琛�/棰勮/鍒嗘瀽/鍔ㄤ綔鍗★級銆�
+ - 澶辫触锛氭寜鏅�氳亰澶╂秷鎭睍绀恒��
+4. 鈥滃姙鈥濆満鏅敱鐢ㄦ埛纭鍔ㄤ綔鍗″悗锛屽墠绔皟鐢� `targetApi` 瀹屾垚涓氬姟鎻愪氦銆�
+5. 閫氳繃鍘嗗彶鎺ュ彛鍋氫細璇濆洖鏄句笌鍒犻櫎銆�
+
+## 12. 鍓嶇闆嗘垚绾︽潫锛堟湰娆¤ˉ鍏咃級
+
+### 12.1 鏅鸿兘浣撴柊澧炰笌寮圭獥鍚屾瑙勫垯锛堝己鍒讹級
+
+1. 褰� `src/views/aiIndustrialBrain/index.vue` 鏂板鏅鸿兘浣擄紙`agents`锛夐�昏緫鏃讹紝蹇呴』鍚屾纭寮圭獥鍔╂墜鍙敤鎬с��
+2. 寮圭獥鍔╂墜缁熶竴鐢� `src/components/AIChatSidebar/assistants/index.js` 鐨� `assistantRegistry` 娉ㄥ唽銆�
+3. 鏂板鏅鸿兘浣撶殑 `key` 鑻ヨ鍦ㄥ脊绐椾腑鍙敤锛屽繀椤诲湪 `assistantRegistry` 涓彁渚涘悓鍚嶉厤缃��
+4. 鏈湪 `assistantRegistry` 娉ㄥ唽鐨勬櫤鑳戒綋锛屽脊绐楁樉绀轰负 `pending`锛堝紑鍙戜腑锛夋�併��
+
+### 12.2 鐢熶骇鍔╂墜鎺ュ叆绾﹀畾
+
+1. 鐢熶骇鍔╂墜閰嶇疆浣嶄簬 `src/components/AIChatSidebar/assistants/productionAssistant.js`锛宍apiBase = /manufacturing-ai`銆�
+2. AI 宸ヤ笟澶ц剳涓敓浜ф櫤鑳戒綋杩涘叆寮圭獥鍚庯紝榛樿浣跨敤 `production` 鍔╂墜銆�
+3. 鍏ㄥ眬鍙充晶瀵硅瘽妗嗗姪鎵嬪垏鎹㈠垪琛ㄥ凡鍖呭惈锛�
+ - `general`锛堝緟鍔炲姪鐞嗭級
+ - `purchase`锛堥噰璐姪鐞嗭級
+ - `production`锛堢敓浜у姪鐞嗭級
+
+### 12.3 瀛楁涓枃鍖栧睍绀鸿鍒�
+
+1. 闈㈠悜涓氬姟鐢ㄦ埛鐨勫瓧娈靛悕銆佹爣绛俱�佸繀濉彁绀轰笉鐩存帴灞曠ず鑻辨枃 key銆�
+2. `requiredFields`銆乣missingFields` 鎻愮ず闇�杞崲涓轰腑鏂囪矾寰勬爣绛撅紙绀轰緥锛歚缂哄皯蹇呭~瀛楁锛氬伐鍗曞彿銆佽鍒掔粨鏉熸椂闂碻锛夈��
+3. 缁撴瀯鍖栧垪琛ㄥ垪鍚嶃�佹憳瑕佹寚鏍囥�佸姩浣滃崱瀛楁浼樺厛鏄剧ず涓枃锛涜嫳鏂� key 浠呯敤浜庢帴鍙i�氫俊涓庤皟璇曘��
+
+## 13. 鏈鏇存柊璁板綍锛�2026-05-16锛�
+
+1. 鏂板璁惧缁翠慨璁板綍杩斿洖绫诲瀷锛歚manufacturing_device_repair_list`銆�
+2. 淇璁惧鍩熸剰鍥惧垎娴侊細`缁翠慨/鎶ヤ慨/妫�淇�/缁存姢` 璧扮淮淇褰曪紝涓嶅啀璇蛋璁惧鍒楄〃銆�
+3. 淇缁翠慨璁板綍鏃堕棿杩囨护锛氫粎鍦ㄧ敤鎴锋槑纭椂闂存潯浠舵椂鐢熸晥銆�
+4. 淇寰呯淮淇粺璁″彛寰勶細鎸� `status = 0` 缁熻銆�
+5. 鏂板 AI 宸ヤ笟澶ц剳鏅鸿兘浣撲笌寮圭獥鍚屾缁存姢瑙勫垯锛氭柊澧炴櫤鑳戒綋蹇呴』鍚屾娉ㄥ唽寮圭獥鍔╂墜銆�
+6. 鐢熶骇鍔╂墜宸叉帴鍏ュ伐涓氬ぇ鑴戝脊绐椾笌鍏ㄥ眬鍙充晶瀵硅瘽妗嗗姪鎵嬪垏鎹€��
+7. 澧炲姞瀛楁涓枃鍖栧睍绀虹害鏉燂細閬垮厤鑻辨枃瀛楁瀵逛笟鍔$敤鎴风洿鍑恒��
diff --git "a/doc/\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243-\350\256\276\345\244\207\346\212\245\344\277\256\344\277\235\345\205\273\350\264\242\345\212\241\346\250\241\345\235\227\346\224\271\351\200\240.md" "b/doc/\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243-\350\256\276\345\244\207\346\212\245\344\277\256\344\277\235\345\205\273\350\264\242\345\212\241\346\250\241\345\235\227\346\224\271\351\200\240.md"
new file mode 100644
index 0000000..43c468c
--- /dev/null
+++ "b/doc/\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243-\350\256\276\345\244\207\346\212\245\344\277\256\344\277\235\345\205\273\350\264\242\345\212\241\346\250\241\345\235\227\346\224\271\351\200\240.md"
@@ -0,0 +1,233 @@
+# 鍓嶇鑱旇皟鏂囨。锛堣澶囨姤淇� / 璁惧淇濆吇瀹氭椂浠诲姟 / 璐㈠姟绉戠洰鎬昏处锛�
+
+## 1. 鍙樻洿鑼冨洿
+
+鏈鑱旇皟娑夊強 3 涓ā鍧楋細
+
+1. 璐㈠姟妯″潡锛氱鐩�昏处鍘绘帀鍑瘉瀛楀彿銆佹憳瑕侊紝鍙繑鍥� 1 鏉″悎璁℃暟鎹��
+2. 璁惧淇濆吇瀹氭椂浠诲姟锛氭柊澧� `淇濆吇浜篳 瀛楁锛屽畾鏃朵换鍔$敓鎴愪繚鍏昏褰曟椂甯﹀叆銆�
+3. 璁惧鎶ヤ慨锛氱‘璁ゆ姤淇悗鏂板楠屾敹瀹℃壒锛岄獙鏀堕�氳繃鍚庢墠绠楀畬缁撱��
+
+---
+
+## 2. 鎺ュ彛娓呭崟
+
+### 2.1 璐㈠姟-绉戠洰鎬昏处
+
+- **GET** `/financial/ledger/general`
+- 璇存槑锛氳繑鍥炵鐩�昏处鍚堣锛屼粎 1 鏉¤褰曘��
+
+#### 璇锋眰鍙傛暟锛圦uery锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| `subjectCode` | string | 鏄� | 绉戠洰缂栫爜 |
+| `startMonth` | string | 鏄� | 寮�濮嬫湀浠斤紝鏍煎紡 `YYYY-MM` |
+| `endMonth` | string | 鏄� | 缁撴潫鏈堜唤锛屾牸寮� `YYYY-MM` |
+
+#### 杩斿洖缁撴瀯
+
+`R<List<FinLedgerRowVo>>`
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": [
+ {
+ "rowType": "yearly_total",
+ "date": "2026-05-31",
+ "debit": 12000.00,
+ "credit": 8000.00,
+ "direction": "鍊�",
+ "balance": 4000.00
+ }
+ ]
+}
+```
+
+#### 鑱旇皟娉ㄦ剰
+
+1. `data` 鍥哄畾鍙湁 1 鏉★紙鍚堣锛夈��
+2. `voucherNo`銆乣summary` 涓嶈繑鍥烇紙涓嶅啀灞曠ず鍑瘉瀛楀彿銆佹憳瑕侊級銆�
+
+---
+
+### 2.2 璁惧淇濆吇瀹氭椂浠诲姟锛堟柊澧炰繚鍏讳汉锛�
+
+- 鍩虹璺緞锛歚/deviceMaintenanceTask`
+- 鐩稿叧鎺ュ彛锛�
+ - **POST** `/add`
+ - **POST** `/update`
+ - **GET** `/listPage`
+
+#### 鏂板瀛楁
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| `maintenancePerson` | string | 淇濆吇浜� |
+
+#### 鏂板/鏇存柊璇锋眰绀轰緥
+
+```json
+{
+ "id": 1,
+ "taskName": "绌哄帇鏈轰繚鍏讳换鍔�",
+ "taskId": 1001,
+ "maintenancePerson": "寮犱笁",
+ "frequencyType": "MONTHLY",
+ "frequencyDetail": "10,09:00",
+ "remarks": "姣忔湀渚嬭淇濆吇"
+}
+```
+
+#### 瀹氭椂浠诲姟涓嬪彂琛屼负
+
+瀹氭椂浠诲姟鎵ц鍚庯紝绯荤粺鑷姩鍒涘缓淇濆吇璁板綍锛坄device_maintenance`锛夋椂浼氬啓鍏ワ細
+
+- `maintenanceActuallyName = maintenancePerson`
+
+鍗冲墠绔湪瀹氭椂浠诲姟閲岀淮鎶ょ殑淇濆吇浜猴紝浼氳嚜鍔ㄥ甫鍏ュ埌淇濆吇璁板綍銆�
+
+---
+
+### 2.3 璁惧鎶ヤ慨锛堢‘璁ゅ悗楠屾敹瀹℃壒锛�
+
+- 鍩虹璺緞锛歚/device/repair`
+
+#### 鐘舵�佸畾涔�
+
+| 鐘舵�佸�� | 鍚箟 |
+|---|---|
+| `0` | 寰呯淮淇� |
+| `3` | 寰呴獙鏀� |
+| `1` | 瀹岀粨 |
+| `2` | 澶辫触 |
+
+#### 2.3.1 缁翠慨纭锛堝師纭鎶ヤ慨锛�
+
+- **POST** `/device/repair/repair`
+- 璇存槑锛氭彁浜ゅ悗鐘舵�佷粠 `寰呯淮淇�(0)` 杩涘叆 `寰呴獙鏀�(3)`锛屼笉鍐嶇洿鎺ュ畬缁撱��
+
+璇锋眰绀轰緥锛�
+
+```json
+{
+ "id": 10001,
+ "maintenanceName": "鏉庡洓",
+ "maintenanceTime": "2026-05-14 10:30:00",
+ "maintenanceResult": "鏇存崲杞存壙骞惰瘯杩愯姝e父",
+ "sparePartsUseList": [
+ {
+ "id": 501,
+ "quantity": 2
+ }
+ ]
+}
+```
+
+甯歌澶辫触鎻愮ず锛堢敤浜庡墠绔脊绐楋級锛�
+
+- `鎶ヤ慨璁板綍涓嶅瓨鍦╜
+- `璇ユ姤淇凡瀹岀粨锛屼笉鑳介噸澶嶇‘璁ょ淮淇甡
+- `璇ユ姤淇凡鎻愪氦楠屾敹瀹℃壒`
+- `澶囦欢 xxx 鏁伴噺涓嶈冻`
+
+#### 2.3.2 楠屾敹瀹℃壒锛堟柊澧烇級
+
+- **POST** `/device/repair/acceptance`
+- 璇存槑锛氫粎 `寰呴獙鏀�(3)` 鍙鎵癸紱瀹℃壒閫氳繃鍚庣姸鎬佹敼涓� `瀹岀粨(1)`銆�
+
+璇锋眰鍙傛暟锛圔ody锛夛細
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| `id` | long | 鏄� | 鎶ヤ慨璁板綍ID |
+| `acceptanceName` | string | 鏄� | 楠屾敹浜� |
+| `acceptanceTime` | string | 鏄� | 楠屾敹鏃堕棿锛屾牸寮� `yyyy-MM-dd HH:mm:ss` |
+| `acceptanceRemark` | string | 鏄� | 楠屾敹澶囨敞 |
+
+璇锋眰绀轰緥锛�
+
+```json
+{
+ "id": 10001,
+ "acceptanceName": "鐜嬩簲",
+ "acceptanceTime": "2026-05-14 11:00:00",
+ "acceptanceRemark": "缁翠慨椤规牳楠岄�氳繃锛岃澶囪繍琛屾甯�"
+}
+```
+
+甯歌澶辫触鎻愮ず锛�
+
+- `鎶ヤ慨璁板綍id涓嶈兘涓虹┖`
+- `鎶ヤ慨璁板綍涓嶅瓨鍦╜
+- `璇ユ姤淇湭杩涘叆寰呴獙鏀剁姸鎬侊紝涓嶈兘瀹℃壒`
+- `楠屾敹浜轰笉鑳戒负绌篳
+- `楠屾敹鏃堕棿涓嶈兘涓虹┖`
+- `楠屾敹澶囨敞涓嶈兘涓虹┖`
+
+#### 2.3.3 鏅�氭洿鏂版帴鍙i檺鍒�
+
+- **PUT** `/device/repair`
+- 闄愬埗锛氫笉鑳介�氳繃鏅�氭洿鏂扮洿鎺ユ妸鐘舵�佹敼鎴� `瀹岀粨(1)`锛堝繀椤昏蛋楠屾敹瀹℃壒鎺ュ彛锛夈��
+- 澶辫触鎻愮ず锛歚璇峰厛鎻愪氦楠屾敹瀹℃壒锛岄獙鏀堕�氳繃鍚庢墠鍙畬缁揱
+
+---
+
+## 3. 杩斿洖瀛楁鍙樻洿锛堟姤淇垪琛�/璇︽儏锛�
+
+浠ヤ笅鎺ュ彛杩斿洖宸叉柊澧為獙鏀跺瓧娈碉細
+
+- **GET** `/device/repair/page`
+- **GET** `/device/repair/{id}`
+
+鏂板杩斿洖瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| `acceptanceName` | string | 楠屾敹浜� |
+| `acceptanceTime` | string | 楠屾敹鏃堕棿 |
+| `acceptanceRemark` | string | 楠屾敹澶囨敞 |
+
+---
+
+## 4. 鍓嶇鏀归�犲缓璁�
+
+1. 鎶ヤ慨鍒楄〃澧炲姞鐘舵�佸�� `3=寰呴獙鏀禶 鐨勫睍绀烘枃妗堜笌绛涢�夐」銆�
+2. 鈥滅‘璁ょ淮淇�濇寜閽皟鐢� `/device/repair/repair`锛屾垚鍔熷悗鍒锋柊涓哄緟楠屾敹鐘舵�併��
+3. 鏂板鈥滈獙鏀跺鎵光�濆脊绐楋紝蹇呭~锛�
+ - 楠屾敹浜�
+ - 楠屾敹鏃堕棿
+ - 楠屾敹澶囨敞
+4. 绂佹鍦ㄦ櫘閫氱紪杈戦〉鐩存帴灏嗙姸鎬佺疆涓哄畬缁撱��
+5. 璁惧淇濆吇瀹氭椂浠诲姟鏂板鈥滀繚鍏讳汉鈥濊緭鍏ラ」锛屽苟鍦ㄥ垪琛�/璇︽儏灞曠ず銆�
+6. 绉戠洰鎬昏处椤甸潰鎸夊崟琛屽悎璁℃覆鏌擄紝涓嶅啀鏄剧ず鍑瘉瀛楀彿銆佹憳瑕佸垪銆�
+
+---
+
+## 5. 鑱旇皟妫�鏌ユ竻鍗�
+
+1. 绉戠洰鎬昏处鏌ヨ杩斿洖 `data.length === 1`锛屼笖鏃� `voucherNo/summary`銆�
+2. 鏂板淇濆吇瀹氭椂浠诲姟鏃朵紶 `maintenancePerson`锛屽垪琛ㄨ兘鍥炴樉銆�
+3. 瀹氭椂浠诲姟瑙﹀彂鍚庯紝鐢熸垚鐨勪繚鍏昏褰� `maintenanceActuallyName` 涓庡畾鏃朵换鍔′繚鍏讳汉涓�鑷淬��
+4. 鎶ヤ慨鍗曟祦绋嬶細`0寰呯淮淇� -> 3寰呴獙鏀� -> 1瀹岀粨`銆�
+5. 寰呴獙鏀跺崟鎹湭濉獙鏀朵汉/楠屾敹鏃堕棿/楠屾敹澶囨敞鏃讹紝鍚庣杩斿洖瀵瑰簲閿欒鎻愮ず銆�
+6. 灏濊瘯閫氳繃 `PUT /device/repair` 鐩存帴璁句负瀹岀粨鏃讹紝鍚庣杩斿洖鎷︽埅鎻愮ず銆�
+
+---
+
+## 6. 鏁版嵁搴撳彉鏇达紙鑱旇皟鍓嶇‘璁わ級
+
+```sql
+ALTER TABLE maintenance_task
+ ADD COLUMN maintenance_person VARCHAR(100) NULL COMMENT '淇濆吇浜�';
+
+ALTER TABLE device_repair
+ ADD COLUMN acceptance_name VARCHAR(100) NULL COMMENT '楠屾敹浜�',
+ ADD COLUMN acceptance_time DATETIME NULL COMMENT '楠屾敹鏃堕棿',
+ ADD COLUMN acceptance_remark VARCHAR(500) NULL COMMENT '楠屾敹澶囨敞';
+```
+
+> 鑻ユ湭鎵ц浠ヤ笂 SQL锛岀浉鍏虫帴鍙d細鍑虹幇瀛楁涓嶅瓨鍦ㄥ紓甯搞��
+
diff --git a/src/main/java/com/ruoyi/account/bean/dto/PurchaseInboundDto.java b/src/main/java/com/ruoyi/account/bean/dto/PurchaseInboundDto.java
index 6ca07a4..757e6b4 100644
--- a/src/main/java/com/ruoyi/account/bean/dto/PurchaseInboundDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/PurchaseInboundDto.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@@ -18,9 +19,11 @@
@Schema(description = "寮�濮嬫棩鏈�")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;
@Schema(description = "缁撴潫鏃ユ湡")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/PurchaseReturnDto.java b/src/main/java/com/ruoyi/account/bean/dto/PurchaseReturnDto.java
index d521422..c238990 100644
--- a/src/main/java/com/ruoyi/account/bean/dto/PurchaseReturnDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/PurchaseReturnDto.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@@ -18,9 +19,11 @@
@Schema(description = "寮�濮嬫棩鏈�")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;
@Schema(description = "缁撴潫鏃ユ湡")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/SalesOutboundDto.java b/src/main/java/com/ruoyi/account/bean/dto/SalesOutboundDto.java
index a30e035..33bc1b9 100644
--- a/src/main/java/com/ruoyi/account/bean/dto/SalesOutboundDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/SalesOutboundDto.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@@ -18,9 +19,11 @@
@Schema(description = "寮�濮嬫棩鏈�")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;
@Schema(description = "缁撴潫鏃ユ湡")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/SalesReturnDto.java b/src/main/java/com/ruoyi/account/bean/dto/SalesReturnDto.java
index d635408..b7ebae2 100644
--- a/src/main/java/com/ruoyi/account/bean/dto/SalesReturnDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/SalesReturnDto.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@@ -18,9 +19,11 @@
@Schema(description = "寮�濮嬫棩鏈�")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;
@Schema(description = "缁撴潫鏃ユ湡")
@JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java
new file mode 100644
index 0000000..84a3676
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java
@@ -0,0 +1,22 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 绉戠洰鏄庣粏璐︽煡璇㈠弬鏁帮紙鍚緟鍔╂牳绠楁潯浠讹級銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinDetailLedgerQueryDto extends FinLedgerQueryDto {
+
+ /**
+ * 杈呭姪鏍哥畻绫诲瀷锛歝ustomer/supplier/department/employee/project銆�
+ */
+ private String auxiliaryType;
+
+ /**
+ * 杈呭姪鏍哥畻瀵硅薄ID銆�
+ */
+ private String auxiliaryId;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java
new file mode 100644
index 0000000..c6baeca
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鍥哄畾璧勪骇鏌ヨ涓庝繚瀛� DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinFixedAssetDto extends FinFixedAsset {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java
new file mode 100644
index 0000000..e3023ed
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java
@@ -0,0 +1,17 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鎵归噺ID璇锋眰鍙傛暟銆�
+ */
+@Data
+public class FinIdBatchDto {
+
+ /**
+ * ID闆嗗悎銆�
+ */
+ private List<Long> ids;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java
new file mode 100644
index 0000000..60dd50e
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鏃犲舰璧勪骇鏌ヨ涓庝繚瀛� DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinIntangibleAssetDto extends FinIntangibleAsset {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java
new file mode 100644
index 0000000..cfc0553
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java
@@ -0,0 +1,25 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+/**
+ * 绉戠洰璐︽煡璇㈠弬鏁般��
+ */
+@Data
+public class FinLedgerQueryDto {
+
+ /**
+ * 绉戠洰缂栫爜锛堟敮鎸佹湯绾ф垨鎸囧畾绉戠洰锛夈��
+ */
+ private String subjectCode;
+
+ /**
+ * 寮�濮嬫湀浠斤紝鏍煎紡锛歒YYY-MM銆�
+ */
+ private String startMonth;
+
+ /**
+ * 缁撴潫鏈堜唤锛屾牸寮忥細YYYY-MM銆�
+ */
+ private String endMonth;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java
new file mode 100644
index 0000000..3f90ea2
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java
@@ -0,0 +1,23 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉淇濆瓨 DTO锛堜富琛� + 鍒嗗綍锛夈��
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDto extends FinVoucher {
+
+ /**
+ * 鍑瘉鏄庣粏鍒嗗綍銆�
+ */
+ private List<FinVoucherEntryDto> entries;
+
+ private List<StorageBlobDTO> storageBlobDTOs;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java
new file mode 100644
index 0000000..f722d79
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鍑瘉鍒嗗綍 DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherEntryDto extends FinVoucherEntry {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java
new file mode 100644
index 0000000..9955bcc
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java
@@ -0,0 +1,37 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 鍑瘉鍒嗛〉鏌ヨ鍙傛暟銆�
+ */
+@Data
+public class FinVoucherPageDto {
+
+ /**
+ * 鍑瘉瀛楀彿锛堟ā绯婂尮閰嶏級銆�
+ */
+ private String voucherNo;
+
+ /**
+ * 鍒跺崟浜恒��
+ */
+ private String creator;
+
+ /**
+ * 鍑瘉鐘舵�併��
+ */
+ private String status;
+
+ /**
+ * 寮�濮嬫棩鏈燂紙鍚級銆�
+ */
+ private LocalDate startDate;
+
+ /**
+ * 缁撴潫鏃ユ湡锛堝惈锛夈��
+ */
+ private LocalDate endDate;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java
new file mode 100644
index 0000000..47c0900
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java
@@ -0,0 +1,15 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+/**
+ * 鍑瘉鐘舵�佸彉鏇村弬鏁般��
+ */
+@Data
+public class FinVoucherStatusDto {
+
+ /**
+ * 鍑瘉ID銆�
+ */
+ private Long id;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/AccountSubjectVo.java b/src/main/java/com/ruoyi/account/bean/vo/AccountSubjectVo.java
index 3154d1c..c6bb078 100644
--- a/src/main/java/com/ruoyi/account/bean/vo/AccountSubjectVo.java
+++ b/src/main/java/com/ruoyi/account/bean/vo/AccountSubjectVo.java
@@ -3,6 +3,19 @@
import com.ruoyi.account.pojo.AccountSubject;
import lombok.Data;
+import java.util.ArrayList;
+import java.util.List;
+
@Data
public class AccountSubjectVo extends AccountSubject {
+
+ /**
+ * 瀛愮鐩垪琛紙閫掑綊缁撴瀯锛夈��
+ */
+ private List<AccountSubjectVo> children = new ArrayList<>();
+
+ /**
+ * 鏄惁鍙跺瓙鑺傜偣銆�
+ */
+ private Boolean leaf;
}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java
new file mode 100644
index 0000000..846b350
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java
@@ -0,0 +1,43 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 绉戠洰璐﹀熀纭�鍒嗗綍鏌ヨ瀵硅薄锛圫QL鏄犲皠浣跨敤锛夈��
+ */
+@Data
+public class FinLedgerEntryRecordVo {
+
+ /**
+ * 鍑瘉鏃ユ湡銆�
+ */
+ private LocalDate voucherDate;
+
+ /**
+ * 鍑瘉瀛楀彿銆�
+ */
+ private String voucherNo;
+
+ /**
+ * 鎽樿銆�
+ */
+ private String summary;
+
+ /**
+ * 鍊熸柟閲戦銆�
+ */
+ private BigDecimal debit;
+
+ /**
+ * 璐锋柟閲戦銆�
+ */
+ private BigDecimal credit;
+
+ /**
+ * 琛屽彿锛堟帓搴忓瓧娈碉級銆�
+ */
+ private Integer rowNo;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java
new file mode 100644
index 0000000..da4131f
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java
@@ -0,0 +1,55 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 绉戠洰璐﹁鏁版嵁杩斿洖瀵硅薄銆�
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class FinLedgerRowVo {
+
+ /**
+ * 琛岀被鍨嬶細opening/entry/monthly_total/yearly_total銆�
+ */
+ private String rowType;
+
+ /**
+ * 鏃ユ湡銆�
+ */
+ private LocalDate date;
+
+ /**
+ * 鍑瘉瀛楀彿銆�
+ */
+ private String voucherNo;
+
+ /**
+ * 鎽樿銆�
+ */
+ private String summary;
+
+ /**
+ * 鍊熸柟閲戦銆�
+ */
+ private BigDecimal debit;
+
+ /**
+ * 璐锋柟閲戦銆�
+ */
+ private BigDecimal credit;
+
+ /**
+ * 浣欓鏂瑰悜锛氬��/璐枫��
+ */
+ private String direction;
+
+ /**
+ * 浣欓锛堝�熸璐疯礋锛夈��
+ */
+ private BigDecimal balance;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java
new file mode 100644
index 0000000..4b23545
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java
@@ -0,0 +1,24 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉璇︽儏杩斿洖瀵硅薄銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDetailVo extends FinVoucher {
+
+ /**
+ * 鍑瘉鍒嗗綍鍒楄〃銆�
+ */
+ private List<FinVoucherEntry> entries;
+ private List<StorageBlobVO> storageBlobVOList;
+}
diff --git a/src/main/java/com/ruoyi/account/controller/AccountSubjectController.java b/src/main/java/com/ruoyi/account/controller/AccountSubjectController.java
index 8282883..38dd0ce 100644
--- a/src/main/java/com/ruoyi/account/controller/AccountSubjectController.java
+++ b/src/main/java/com/ruoyi/account/controller/AccountSubjectController.java
@@ -33,7 +33,7 @@
@GetMapping("/list")
@Log(title = "鎬昏处绉戠洰鏁版嵁闆嗗悎", businessType = BusinessType.OTHER)
- @Operation(summary = "鎬昏处绉戠洰鍒嗛〉鏌ヨ")
+ @Operation(summary = "鎬昏处绉戠洰鏍戝舰鏌ヨ锛堥�掑綊锛�")
public R<IPage<AccountSubjectVo>> AccountSubjectDtoList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) {
IPage<AccountSubjectVo> paramList = accountSubjectService.baseList(page, accountSubjectDto);
return R.ok(paramList);
@@ -43,21 +43,21 @@
@Log(title = "鏂板鎬昏处绉戠洰", businessType = BusinessType.INSERT)
@Operation(summary = "鏂板鎬昏处绉戠洰")
public R AccountSubjectDtoAdd(@RequestBody AccountSubjectDto accountSubjectDto) {
- return R.ok(accountSubjectService.save(accountSubjectDto));
+ return R.ok(accountSubjectService.saveAccountSubject(accountSubjectDto));
}
@PutMapping("/edit")
@Log(title = "淇敼鎬昏处绉戠洰", businessType = BusinessType.UPDATE)
@Operation(summary = "淇敼鎬昏处绉戠洰")
public R AccountSubjectDtoEdit(@RequestBody AccountSubjectDto accountSubjectDto) {
- return R.ok(accountSubjectService.updateById(accountSubjectDto));
+ return R.ok(accountSubjectService.updateAccountSubject(accountSubjectDto));
}
@DeleteMapping("/remove/{ids}")
@Log(title = "鍒犻櫎鎬昏处绉戠洰", businessType = BusinessType.DELETE)
@Operation(summary = "鍒犻櫎鎬昏处绉戠洰")
public R AccountSubjectDtooRemove(@PathVariable Long[] ids) {
- return R.ok(accountSubjectService.removeBatchByIds(Arrays.asList(ids)));
+ return R.ok(accountSubjectService.removeAccountSubjectByIds(Arrays.asList(ids)));
}
@PostMapping("/export")
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java b/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java
new file mode 100644
index 0000000..a18c9da
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java
@@ -0,0 +1,63 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.bean.dto.financial.FinIdBatchDto;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import com.ruoyi.account.service.financial.FinFixedAssetService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 鍥哄畾璧勪骇鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/fixedAsset")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鍥哄畾璧勪骇")
+public class FinFixedAssetController {
+
+ private final FinFixedAssetService finFixedAssetService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鍥哄畾璧勪骇鍒嗛〉鏌ヨ")
+ public R<IPage<FinFixedAsset>> page(Page<FinFixedAsset> page, FinFixedAssetDto queryDto) {
+ return R.ok(finFixedAssetService.pageList(page, queryDto));
+ }
+
+ @PostMapping("/add")
+ @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.INSERT)
+ @Operation(summary = "鏂板鍥哄畾璧勪骇")
+ public R<Boolean> add(@RequestBody FinFixedAssetDto dto) {
+ return R.ok(finFixedAssetService.add(dto));
+ }
+
+ @PutMapping("/update")
+ @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.UPDATE)
+ @Operation(summary = "淇敼鍥哄畾璧勪骇")
+ public R<Boolean> update(@RequestBody FinFixedAssetDto dto) {
+ return R.ok(finFixedAssetService.update(dto));
+ }
+
+ @DeleteMapping("/delete")
+ @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.DELETE)
+ @Operation(summary = "鍒犻櫎鍥哄畾璧勪骇")
+ public R<Boolean> delete(@RequestParam("ids") Long[] ids) {
+ return R.ok(finFixedAssetService.deleteByIds(Arrays.asList(ids)));
+ }
+
+ @PostMapping("/depreciate")
+ @Log(title = "鍥哄畾璧勪骇鎶樻棫璁℃彁", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鍥哄畾璧勪骇鎸夋湀璁℃彁鎶樻棫")
+ public R depreciate(@RequestBody(required = false) FinIdBatchDto dto) {
+ return R.ok(finFixedAssetService.depreciate(dto == null ? null : dto.getIds()));
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java b/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java
new file mode 100644
index 0000000..cb12f8c
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java
@@ -0,0 +1,63 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinIdBatchDto;
+import com.ruoyi.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import com.ruoyi.account.service.financial.FinIntangibleAssetService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 鏃犲舰璧勪骇鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/intangibleAsset")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鏃犲舰璧勪骇")
+public class FinIntangibleAssetController {
+
+ private final FinIntangibleAssetService finIntangibleAssetService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鏃犲舰璧勪骇鍒嗛〉鏌ヨ")
+ public R<IPage<FinIntangibleAsset>> page(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto) {
+ return R.ok(finIntangibleAssetService.pageList(page, queryDto));
+ }
+
+ @PostMapping("/add")
+ @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.INSERT)
+ @Operation(summary = "鏂板鏃犲舰璧勪骇")
+ public R<Boolean> add(@RequestBody FinIntangibleAssetDto dto) {
+ return R.ok(finIntangibleAssetService.add(dto));
+ }
+
+ @PutMapping("/update")
+ @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.UPDATE)
+ @Operation(summary = "淇敼鏃犲舰璧勪骇")
+ public R<Boolean> update(@RequestBody FinIntangibleAssetDto dto) {
+ return R.ok(finIntangibleAssetService.update(dto));
+ }
+
+ @DeleteMapping("/delete")
+ @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.DELETE)
+ @Operation(summary = "鍒犻櫎鏃犲舰璧勪骇")
+ public R<Boolean> delete(@RequestParam("ids") Long[] ids) {
+ return R.ok(finIntangibleAssetService.deleteByIds(Arrays.asList(ids)));
+ }
+
+ @PostMapping("/amortize")
+ @Log(title = "鏃犲舰璧勪骇鎽婇攢璁℃彁", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鏃犲舰璧勪骇鎸夋湀璁℃彁鎽婇攢")
+ public R amortize(@RequestBody(required = false) FinIdBatchDto dto) {
+ return R.ok(finIntangibleAssetService.amortize(dto == null ? null : dto.getIds()));
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java b/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java
new file mode 100644
index 0000000..423030a
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java
@@ -0,0 +1,39 @@
+package com.ruoyi.account.controller.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+import com.ruoyi.account.service.financial.FinLedgerService;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 绉戠洰鎬昏处/鏄庣粏璐︽帶鍒跺櫒銆�
+ */
+@RestController
+@RequestMapping("/financial/ledger")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-绉戠洰璐�")
+public class FinLedgerController {
+
+ private final FinLedgerService finLedgerService;
+
+ @GetMapping("/general")
+ @Operation(summary = "绉戠洰鎬昏处鏌ヨ")
+ public R<List<FinLedgerRowVo>> general(FinLedgerQueryDto queryDto) {
+ return R.ok(finLedgerService.queryGeneralLedger(queryDto));
+ }
+
+ @GetMapping("/detail")
+ @Operation(summary = "绉戠洰鏄庣粏璐︽煡璇�")
+ public R<List<FinLedgerRowVo>> detail(FinDetailLedgerQueryDto queryDto) {
+ return R.ok(finLedgerService.queryDetailLedger(queryDto));
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java b/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java
new file mode 100644
index 0000000..beeaafa
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherStatusDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.service.financial.FinVoucherService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 鍑瘉鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/voucher")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鍑瘉")
+public class FinVoucherController {
+
+ private final FinVoucherService finVoucherService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鍑瘉鍒嗛〉鏌ヨ")
+ public R<IPage<FinVoucher>> page(Page<FinVoucher> page, FinVoucherPageDto queryDto) {
+ return R.ok(finVoucherService.pageList(page, queryDto));
+ }
+
+ @PostMapping("/add")
+ @Log(title = "鍑瘉", businessType = BusinessType.INSERT)
+ @Operation(summary = "鏂板鍑瘉")
+ public R<Boolean> add(@RequestBody FinVoucherDto dto) {
+ return R.ok(finVoucherService.addVoucher(dto));
+ }
+
+ @PutMapping("/update")
+ @Log(title = "鍑瘉", businessType = BusinessType.UPDATE)
+ @Operation(summary = "淇敼鍑瘉")
+ public R<Boolean> update(@RequestBody FinVoucherDto dto) {
+ return R.ok(finVoucherService.updateVoucher(dto));
+ }
+
+ @PostMapping("/post")
+ @Log(title = "鍑瘉杩囪处", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鍑瘉杩囪处")
+ public R<Boolean> post(@RequestBody FinVoucherStatusDto dto) {
+ return R.ok(finVoucherService.postVoucher(dto.getId()));
+ }
+
+ @PostMapping("/cancel")
+ @Log(title = "鍑瘉浣滃簾", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鍑瘉浣滃簾")
+ public R<Boolean> cancel(@RequestBody FinVoucherStatusDto dto) {
+ return R.ok(finVoucherService.cancelVoucher(dto.getId()));
+ }
+
+ @GetMapping("/detail/{id}")
+ @Operation(summary = "鍑瘉璇︽儏")
+ public R<FinVoucherDetailVo> detail(@PathVariable("id") Long id) {
+ return R.ok(finVoucherService.detail(id));
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/AccountSubjectMapper.java b/src/main/java/com/ruoyi/account/mapper/AccountSubjectMapper.java
index 1fface5..46a4968 100644
--- a/src/main/java/com/ruoyi/account/mapper/AccountSubjectMapper.java
+++ b/src/main/java/com/ruoyi/account/mapper/AccountSubjectMapper.java
@@ -3,6 +3,9 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.account.pojo.AccountSubject;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
/**
* <p>
@@ -15,4 +18,6 @@
@Mapper
public interface AccountSubjectMapper extends BaseMapper<AccountSubject> {
+ Long countReferencedBySubjectCodes(@Param("subjectCodes") List<String> subjectCodes);
+
}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java
new file mode 100644
index 0000000..da2ae49
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鍥哄畾璧勪骇 Mapper銆�
+ */
+@Mapper
+public interface FinFixedAssetMapper extends BaseMapper<FinFixedAsset> {
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java
new file mode 100644
index 0000000..8a7bbb2
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鏃犲舰璧勪骇 Mapper銆�
+ */
+@Mapper
+public interface FinIntangibleAssetMapper extends BaseMapper<FinIntangibleAsset> {
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java
new file mode 100644
index 0000000..2fa2b73
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 鍑瘉鍒嗗綍 Mapper銆�
+ */
+@Mapper
+public interface FinVoucherEntryMapper extends BaseMapper<FinVoucherEntry> {
+
+ List<FinLedgerEntryRecordVo> listPostedEntries(@Param("subjectCode") String subjectCode,
+ @Param("startDate") LocalDate startDate,
+ @Param("endDate") LocalDate endDate,
+ @Param("auxiliaryType") String auxiliaryType,
+ @Param("auxiliaryId") String auxiliaryId);
+
+ List<FinLedgerEntryRecordVo> listPostedEntriesBefore(@Param("subjectCode") String subjectCode,
+ @Param("beforeDate") LocalDate beforeDate,
+ @Param("auxiliaryType") String auxiliaryType,
+ @Param("auxiliaryId") String auxiliaryId);
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java
new file mode 100644
index 0000000..b528b7c
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鍑瘉涓昏〃 Mapper銆�
+ */
+@Mapper
+public interface FinVoucherMapper extends BaseMapper<FinVoucher> {
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/AccountSubject.java b/src/main/java/com/ruoyi/account/pojo/AccountSubject.java
index 49ba3da..9616324 100644
--- a/src/main/java/com/ruoyi/account/pojo/AccountSubject.java
+++ b/src/main/java/com/ruoyi/account/pojo/AccountSubject.java
@@ -39,6 +39,12 @@
private Long id;
/**
+ * 鐖剁鐩甀D锛堜负绌鸿〃绀烘牴鑺傜偣锛�
+ */
+ @ApiModelProperty("鐖剁鐩甀D锛堜负绌鸿〃绀烘牴鑺傜偣锛�")
+ private Long parentId;
+
+ /**
* 绉戠洰缂栫爜(鍞竴鏍囪瘑)
*/
@ApiModelProperty("绉戠洰缂栫爜(鍞竴鏍囪瘑)")
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java b/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java
new file mode 100644
index 0000000..f11f700
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java
@@ -0,0 +1,101 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鍥哄畾璧勪骇瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_fixed_asset")
+@ApiModel(value = "FinFixedAsset瀵硅薄", description = "鍥哄畾璧勪骇")
+public class FinFixedAsset implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("涓婚敭ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty("璧勪骇缂栧彿")
+ private String assetCode;
+
+ @ApiModelProperty("璧勪骇鍚嶇О")
+ private String assetName;
+
+ @ApiModelProperty("璧勪骇绫诲埆")
+ private String category;
+
+ @ApiModelProperty("瑙勬牸鍨嬪彿")
+ private String specification;
+
+ @ApiModelProperty("璐疆鏃ユ湡")
+ private LocalDate purchaseDate;
+
+ @ApiModelProperty("璧勪骇鍘熷��")
+ private BigDecimal originalValue;
+
+ @ApiModelProperty("浣跨敤骞撮檺(骞�)")
+ private Integer usefulLife;
+
+ @ApiModelProperty("娈嬪�肩巼(%)")
+ private BigDecimal residualRate;
+
+ @ApiModelProperty("绱鎶樻棫")
+ private BigDecimal accumulatedDepreciation;
+
+ @ApiModelProperty("鍑�鍊�")
+ private BigDecimal netValue;
+
+ @ApiModelProperty("瀛樻斁鍦扮偣")
+ private String location;
+
+ @ApiModelProperty("浣跨敤閮ㄩ棬")
+ private String department;
+
+ @ApiModelProperty("淇濈浜�")
+ private String keeper;
+
+ @ApiModelProperty("鐘舵��")
+ private String status;
+
+ @ApiModelProperty("澶囨敞")
+ private String remark;
+
+ @ApiModelProperty("鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ @ApiModelProperty("鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ @ApiModelProperty("淇敼浜�")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String updateUser;
+
+ @ApiModelProperty("淇敼鏃堕棿")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+
+ @ApiModelProperty("閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java b/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java
new file mode 100644
index 0000000..e8ab4d3
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java
@@ -0,0 +1,98 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鏃犲舰璧勪骇瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_intangible_asset")
+@ApiModel(value = "FinIntangibleAsset瀵硅薄", description = "鏃犲舰璧勪骇")
+public class FinIntangibleAsset implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("涓婚敭ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty("璧勪骇缂栧彿")
+ private String assetCode;
+
+ @ApiModelProperty("璧勪骇鍚嶇О")
+ private String assetName;
+
+ @ApiModelProperty("璧勪骇绫诲埆")
+ private String category;
+
+ @ApiModelProperty("璇佷功缂栧彿")
+ private String certificateNo;
+
+ @ApiModelProperty("鍙栧緱鏃ユ湡")
+ private LocalDate acquisitionDate;
+
+ @ApiModelProperty("璧勪骇鍘熷��")
+ private BigDecimal originalValue;
+
+ @ApiModelProperty("鎽婇攢骞撮檺(骞�)")
+ private Integer amortizationPeriod;
+
+ @ApiModelProperty("娈嬪�肩巼(%)")
+ private BigDecimal residualRate;
+
+ @ApiModelProperty("绱鎽婇攢")
+ private BigDecimal accumulatedAmortization;
+
+ @ApiModelProperty("鍑�鍊�")
+ private BigDecimal netValue;
+
+ @ApiModelProperty("鏈夋晥鏈熻嚦")
+ private LocalDate validityDate;
+
+ @ApiModelProperty("鐘舵��")
+ private String status;
+
+ @ApiModelProperty("璧勪骇鎻忚堪")
+ private String description;
+
+ @ApiModelProperty("澶囨敞")
+ private String remark;
+
+ @ApiModelProperty("鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ @ApiModelProperty("鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ @ApiModelProperty("淇敼浜�")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String updateUser;
+
+ @ApiModelProperty("淇敼鏃堕棿")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+
+ @ApiModelProperty("閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java
new file mode 100644
index 0000000..0a2918c
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java
@@ -0,0 +1,83 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鍑瘉涓昏〃瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_voucher")
+@ApiModel(value = "FinVoucher瀵硅薄", description = "鍑瘉涓昏〃")
+public class FinVoucher implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("涓婚敭ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty("鍑瘉瀛楀彿")
+ private String voucherNo;
+
+ @ApiModelProperty("鍑瘉鏃ユ湡")
+ private LocalDate voucherDate;
+
+ @ApiModelProperty("鎽樿")
+ private String summary;
+
+ @ApiModelProperty("鍊熸柟鍚堣")
+ private BigDecimal debit;
+
+ @ApiModelProperty("璐锋柟鍚堣")
+ private BigDecimal credit;
+
+ @ApiModelProperty("鍒跺崟浜�")
+ private String creator;
+
+ @ApiModelProperty("鐘舵��: unposted/posted/cancelled")
+ private String status;
+
+ @ApiModelProperty("闄勪欢鏁伴噺")
+ private Integer attachmentCount;
+
+ @ApiModelProperty("澶囨敞")
+ private String remark;
+
+ @ApiModelProperty("鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ @ApiModelProperty("鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ @ApiModelProperty("淇敼浜�")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String updateUser;
+
+ @ApiModelProperty("淇敼鏃堕棿")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+
+ @ApiModelProperty("閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java
new file mode 100644
index 0000000..44ac56e
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java
@@ -0,0 +1,85 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 鍑瘉鍒嗗綍瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_voucher_entry")
+@ApiModel(value = "FinVoucherEntry瀵硅薄", description = "鍑瘉鍒嗗綍")
+public class FinVoucherEntry implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("涓婚敭ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty("鍑瘉ID")
+ private Long voucherId;
+
+ @ApiModelProperty("琛屽彿")
+ private Integer rowNo;
+
+ @ApiModelProperty("绉戠洰缂栫爜")
+ private String subjectCode;
+
+ @ApiModelProperty("绉戠洰鍚嶇О")
+ private String subjectName;
+
+ @ApiModelProperty("鎽樿")
+ private String summary;
+
+ @ApiModelProperty("鍊熸柟閲戦")
+ private BigDecimal debit;
+
+ @ApiModelProperty("璐锋柟閲戦")
+ private BigDecimal credit;
+
+ @ApiModelProperty("杈呭姪鏍哥畻绫诲瀷")
+ private String auxiliaryType;
+
+ @ApiModelProperty("杈呭姪鏍哥畻瀵硅薄ID")
+ private String auxiliaryId;
+
+ @ApiModelProperty("杈呭姪鏍哥畻瀵硅薄鍚嶇О")
+ private String auxiliaryName;
+
+ @ApiModelProperty("鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private String createUser;
+
+ @ApiModelProperty("鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ @ApiModelProperty("淇敼浜�")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String updateUser;
+
+ @ApiModelProperty("淇敼鏃堕棿")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+
+ @ApiModelProperty("閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/service/AccountSubjectService.java b/src/main/java/com/ruoyi/account/service/AccountSubjectService.java
index a7dd670..bcbc57c 100644
--- a/src/main/java/com/ruoyi/account/service/AccountSubjectService.java
+++ b/src/main/java/com/ruoyi/account/service/AccountSubjectService.java
@@ -8,6 +8,8 @@
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
+
/**
* <p>
* 鎬昏处绉戠洰琛� 鏈嶅姟绫�
@@ -20,5 +22,11 @@
IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto);
+ Boolean saveAccountSubject(AccountSubjectDto accountSubjectDto);
+
+ Boolean updateAccountSubject(AccountSubjectDto accountSubjectDto);
+
+ Boolean removeAccountSubjectByIds(List<Long> ids);
+
void exportAccountSubject(HttpServletResponse response);
}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java b/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java
new file mode 100644
index 0000000..0b2c1cc
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java
@@ -0,0 +1,26 @@
+package com.ruoyi.account.service.financial;
+
+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.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍥哄畾璧勪骇鏈嶅姟銆�
+ */
+public interface FinFixedAssetService extends IService<FinFixedAsset> {
+
+ IPage<FinFixedAsset> pageList(Page<FinFixedAsset> page, FinFixedAssetDto queryDto);
+
+ Boolean add(FinFixedAssetDto dto);
+
+ Boolean update(FinFixedAssetDto dto);
+
+ Boolean deleteByIds(List<Long> ids);
+
+ Map<String, Object> depreciate(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java b/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java
new file mode 100644
index 0000000..46d946a
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java
@@ -0,0 +1,26 @@
+package com.ruoyi.account.service.financial;
+
+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.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏃犲舰璧勪骇鏈嶅姟銆�
+ */
+public interface FinIntangibleAssetService extends IService<FinIntangibleAsset> {
+
+ IPage<FinIntangibleAsset> pageList(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto);
+
+ Boolean add(FinIntangibleAssetDto dto);
+
+ Boolean update(FinIntangibleAssetDto dto);
+
+ Boolean deleteByIds(List<Long> ids);
+
+ Map<String, Object> amortize(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java b/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java
new file mode 100644
index 0000000..c4e9fa4
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java
@@ -0,0 +1,17 @@
+package com.ruoyi.account.service.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+
+import java.util.List;
+
+/**
+ * 绉戠洰璐︽湇鍔°��
+ */
+public interface FinLedgerService {
+
+ List<FinLedgerRowVo> queryGeneralLedger(FinLedgerQueryDto queryDto);
+
+ List<FinLedgerRowVo> queryDetailLedger(FinDetailLedgerQueryDto queryDto);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java b/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java
new file mode 100644
index 0000000..078bac4
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java
@@ -0,0 +1,27 @@
+package com.ruoyi.account.service.financial;
+
+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.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+
+/**
+ * 鍑瘉鏈嶅姟銆�
+ */
+public interface FinVoucherService extends IService<FinVoucher> {
+
+ IPage<FinVoucher> pageList(Page<FinVoucher> page, FinVoucherPageDto queryDto);
+
+ Boolean addVoucher(FinVoucherDto dto);
+
+ Boolean updateVoucher(FinVoucherDto dto);
+
+ Boolean postVoucher(Long id);
+
+ Boolean cancelVoucher(Long id);
+
+ FinVoucherDetailVo detail(Long id);
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
index 152966a..37bf64b 100644
--- a/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
@@ -10,6 +10,7 @@
import com.ruoyi.account.mapper.AccountSubjectMapper;
import com.ruoyi.account.pojo.AccountSubject;
import com.ruoyi.account.service.AccountSubjectService;
+import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import jakarta.servlet.http.HttpServletResponse;
@@ -18,7 +19,16 @@
import org.springframework.stereotype.Service;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -37,30 +47,78 @@
@Override
public IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) {
- LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>();
- if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectCode())) {
- queryWrapper.like(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode());
- }
- if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectName())) {
- queryWrapper.like(AccountSubject::getSubjectName, accountSubjectDto.getSubjectName());
- }
- if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectType())) {
- queryWrapper.eq(AccountSubject::getSubjectType, accountSubjectDto.getSubjectType());
- }
- queryWrapper.orderByDesc(AccountSubject::getId);
+ Page<AccountSubjectDto> requestPage = page == null ? new Page<>(1, 10) : page;
+ List<AccountSubject> allSubjects = list(loadBaseQueryWrapper(accountSubjectDto));
+ List<AccountSubject> filteredSubjects = applyTreeFilter(allSubjects, accountSubjectDto);
+ List<AccountSubjectVo> fullTree = buildTree(filteredSubjects);
- Page<AccountSubject> entityPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
- Page<AccountSubject> paramPage = page(entityPage, queryWrapper);
+ long current = requestPage.getCurrent() <= 0 ? 1 : requestPage.getCurrent();
+ long size = requestPage.getSize() <= 0 ? 10 : requestPage.getSize();
+ int fromIndex = (int) Math.min((current - 1) * size, fullTree.size());
+ int toIndex = (int) Math.min(fromIndex + size, fullTree.size());
+ List<AccountSubjectVo> pagedRoots = fromIndex >= toIndex
+ ? Collections.emptyList()
+ : fullTree.subList(fromIndex, toIndex);
- Page<AccountSubjectVo> resultPage = new Page<>(paramPage.getCurrent(), paramPage.getSize(), paramPage.getTotal());
- List<AccountSubjectVo> records = new ArrayList<>(paramPage.getRecords().size());
- for (AccountSubject item : paramPage.getRecords()) {
- AccountSubjectVo vo = new AccountSubjectVo();
- BeanUtils.copyProperties(item, vo);
- records.add(vo);
- }
- resultPage.setRecords(records);
+ Page<AccountSubjectVo> resultPage = new Page<>(current, size, fullTree.size());
+ resultPage.setRecords(pagedRoots);
return resultPage;
+ }
+
+ @Override
+ public Boolean saveAccountSubject(AccountSubjectDto accountSubjectDto) {
+ validateSubjectRequiredFields(accountSubjectDto);
+ validateSubjectCodeUnique(accountSubjectDto, false);
+ validateParent(accountSubjectDto.getParentId(), null);
+ if (accountSubjectDto.getStatus() == null) {
+ accountSubjectDto.setStatus(0);
+ }
+ return save(accountSubjectDto);
+ }
+
+ @Override
+ public Boolean updateAccountSubject(AccountSubjectDto accountSubjectDto) {
+ if (accountSubjectDto == null || accountSubjectDto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛岀鐩甀D涓嶈兘涓虹┖");
+ }
+ if (getById(accountSubjectDto.getId()) == null) {
+ throw new ServiceException("淇敼澶辫触锛屾湭鎵惧埌瀵瑰簲绉戠洰");
+ }
+ validateParent(accountSubjectDto.getParentId(), accountSubjectDto.getId());
+ validateSubjectRequiredFields(accountSubjectDto);
+ validateSubjectCodeUnique(accountSubjectDto, true);
+ return updateById(accountSubjectDto);
+ }
+
+ @Override
+ public Boolean removeAccountSubjectByIds(List<Long> ids) {
+ if (ids == null || ids.isEmpty()) {
+ return true;
+ }
+ List<AccountSubject> allSubjects = list();
+ if (allSubjects == null || allSubjects.isEmpty()) {
+ return true;
+ }
+ Map<Long, List<Long>> childrenIdMap = buildChildrenIdMap(allSubjects);
+ Set<Long> removeIds = new LinkedHashSet<>();
+ for (Long id : ids) {
+ collectDescendantIds(id, childrenIdMap, removeIds);
+ }
+ if (removeIds.isEmpty()) {
+ return true;
+ }
+ List<String> subjectCodes = allSubjects.stream()
+ .filter(subject -> removeIds.contains(subject.getId()))
+ .map(AccountSubject::getSubjectCode)
+ .filter(StringUtils::isNotEmpty)
+ .collect(Collectors.toList());
+ if (!subjectCodes.isEmpty()) {
+ Long referencedCount = accountSubjectMapper.countReferencedBySubjectCodes(subjectCodes);
+ if (referencedCount != null && referencedCount > 0) {
+ throw new ServiceException("鍒犻櫎澶辫触锛岀鐩凡琚嚟璇佸垎褰曞紩鐢�");
+ }
+ }
+ return removeByIds(removeIds);
}
@Override
@@ -74,4 +132,266 @@
ExcelUtil<AccountSubjectImportDto> util = new ExcelUtil<>(AccountSubjectImportDto.class);
util.exportExcel(response, importDtos , "鎬昏处绉戠洰");
}
+
+ /**
+ * 鏍¢獙绉戠洰蹇呭~瀛楁锛岄伩鍏嶈剰鏁版嵁鍐欏叆銆�
+ */
+ private void validateSubjectRequiredFields(AccountSubjectDto accountSubjectDto) {
+ if (accountSubjectDto == null) {
+ throw new ServiceException("鎬昏处绉戠洰鏁版嵁涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(accountSubjectDto.getSubjectCode())) {
+ throw new ServiceException("绉戠洰缂栫爜涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(accountSubjectDto.getSubjectName())) {
+ throw new ServiceException("绉戠洰鍚嶇О涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(accountSubjectDto.getSubjectType())) {
+ throw new ServiceException("绉戠洰绫诲瀷涓嶈兘涓虹┖");
+ }
+ }
+
+ /**
+ * 鏍¢獙绉戠洰缂栫爜鍞竴锛屾柊澧炲拰淇敼閮借鎵ц銆�
+ */
+ private void validateSubjectCodeUnique(AccountSubjectDto accountSubjectDto, boolean isUpdate) {
+ LambdaQueryWrapper<AccountSubject> codeQueryWrapper = new LambdaQueryWrapper<>();
+ codeQueryWrapper.eq(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode());
+ if (isUpdate) {
+ codeQueryWrapper.ne(AccountSubject::getId, accountSubjectDto.getId());
+ }
+ AccountSubject exists = getOne(codeQueryWrapper, false);
+ if (Objects.nonNull(exists)) {
+ throw new ServiceException("绉戠洰缂栫爜宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+ }
+ }
+
+ /**
+ * 浠呮寜閫氱敤杩囨护鏉′欢鏌ヨ鍩虹鏁版嵁锛堟爲褰㈣繃婊ゅ悗缁啀鍋氾級銆�
+ */
+ private LambdaQueryWrapper<AccountSubject> loadBaseQueryWrapper(AccountSubjectDto accountSubjectDto) {
+ LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>();
+ if (accountSubjectDto != null && accountSubjectDto.getStatus() != null) {
+ queryWrapper.eq(AccountSubject::getStatus, accountSubjectDto.getStatus());
+ }
+ queryWrapper.orderByAsc(AccountSubject::getSubjectCode).orderByAsc(AccountSubject::getId);
+ return queryWrapper;
+ }
+
+ /**
+ * 鏍戝舰杩囨护锛氬懡涓妭鐐瑰悗淇濈暀鍏剁埗閾句笌瀛愭爲锛屼繚璇侀�掑綊缁撴瀯瀹屾暣銆�
+ */
+ private List<AccountSubject> applyTreeFilter(List<AccountSubject> allSubjects, AccountSubjectDto queryDto) {
+ if (allSubjects == null || allSubjects.isEmpty()) {
+ return Collections.emptyList();
+ }
+ boolean hasFilter = queryDto != null && (
+ StringUtils.isNotEmpty(queryDto.getSubjectCode())
+ || StringUtils.isNotEmpty(queryDto.getSubjectName())
+ || StringUtils.isNotEmpty(queryDto.getSubjectType())
+ );
+ if (!hasFilter) {
+ return allSubjects;
+ }
+
+ Map<Long, AccountSubject> subjectMap = allSubjects.stream()
+ .filter(item -> item.getId() != null)
+ .collect(Collectors.toMap(AccountSubject::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
+ Map<Long, List<AccountSubject>> childrenMap = buildChildrenMap(allSubjects);
+
+ Set<Long> matchedIds = new LinkedHashSet<>();
+ for (AccountSubject subject : allSubjects) {
+ if (subject.getId() == null) {
+ continue;
+ }
+ if (matchesFilter(subject, queryDto)) {
+ matchedIds.add(subject.getId());
+ }
+ }
+ if (matchedIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ Set<Long> resultIds = new LinkedHashSet<>(matchedIds);
+ for (Long matchedId : matchedIds) {
+ addAncestors(matchedId, subjectMap, resultIds);
+ addDescendants(matchedId, childrenMap, resultIds);
+ }
+
+ return allSubjects.stream()
+ .filter(item -> item.getId() != null && resultIds.contains(item.getId()))
+ .collect(Collectors.toList());
+ }
+
+ private boolean matchesFilter(AccountSubject subject, AccountSubjectDto queryDto) {
+ if (queryDto == null) {
+ return true;
+ }
+ if (StringUtils.isNotEmpty(queryDto.getSubjectCode())
+ && (subject.getSubjectCode() == null || !subject.getSubjectCode().contains(queryDto.getSubjectCode()))) {
+ return false;
+ }
+ if (StringUtils.isNotEmpty(queryDto.getSubjectName())
+ && (subject.getSubjectName() == null || !subject.getSubjectName().contains(queryDto.getSubjectName()))) {
+ return false;
+ }
+ if (StringUtils.isNotEmpty(queryDto.getSubjectType())
+ && !queryDto.getSubjectType().equals(subject.getSubjectType())) {
+ return false;
+ }
+ return true;
+ }
+
+ private void addAncestors(Long subjectId, Map<Long, AccountSubject> subjectMap, Set<Long> resultIds) {
+ AccountSubject current = subjectMap.get(subjectId);
+ if (current == null) {
+ return;
+ }
+ Long parentId = current.getParentId();
+ while (parentId != null && parentId > 0) {
+ AccountSubject parent = subjectMap.get(parentId);
+ if (parent == null) {
+ break;
+ }
+ if (!resultIds.add(parent.getId())) {
+ break;
+ }
+ parentId = parent.getParentId();
+ }
+ }
+
+ private void addDescendants(Long subjectId, Map<Long, List<AccountSubject>> childrenMap, Set<Long> resultIds) {
+ List<AccountSubject> children = childrenMap.getOrDefault(subjectId, Collections.emptyList());
+ for (AccountSubject child : children) {
+ if (child.getId() == null) {
+ continue;
+ }
+ if (resultIds.add(child.getId())) {
+ addDescendants(child.getId(), childrenMap, resultIds);
+ }
+ }
+ }
+
+ private Map<Long, List<AccountSubject>> buildChildrenMap(List<AccountSubject> subjects) {
+ Map<Long, List<AccountSubject>> childrenMap = new HashMap<>();
+ for (AccountSubject subject : subjects) {
+ if (subject.getId() == null) {
+ continue;
+ }
+ Long parentId = subject.getParentId();
+ if (parentId == null || parentId <= 0) {
+ continue;
+ }
+ childrenMap.computeIfAbsent(parentId, key -> new ArrayList<>()).add(subject);
+ }
+ return childrenMap;
+ }
+
+ /**
+ * 鍩轰簬 parentId 閫掑綊鏋勫缓绉戠洰鏍戙��
+ */
+ private List<AccountSubjectVo> buildTree(List<AccountSubject> subjects) {
+ if (subjects == null || subjects.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<AccountSubject> sortedSubjects = new ArrayList<>(subjects);
+ sortedSubjects.sort(Comparator
+ .comparing(AccountSubject::getSubjectCode, Comparator.nullsLast(String::compareTo))
+ .thenComparing(AccountSubject::getId, Comparator.nullsLast(Long::compareTo)));
+
+ Map<Long, AccountSubjectVo> subjectVoMap = new LinkedHashMap<>();
+ for (AccountSubject subject : sortedSubjects) {
+ if (subject.getId() == null) {
+ continue;
+ }
+ AccountSubjectVo vo = new AccountSubjectVo();
+ BeanUtils.copyProperties(subject, vo);
+ subjectVoMap.put(subject.getId(), vo);
+ }
+
+ List<AccountSubjectVo> roots = new ArrayList<>();
+ for (AccountSubject subject : sortedSubjects) {
+ if (subject.getId() == null) {
+ continue;
+ }
+ AccountSubjectVo current = subjectVoMap.get(subject.getId());
+ Long parentId = subject.getParentId();
+ if (parentId != null && parentId > 0 && subjectVoMap.containsKey(parentId)) {
+ subjectVoMap.get(parentId).getChildren().add(current);
+ } else {
+ roots.add(current);
+ }
+ }
+
+ markLeafRecursively(roots);
+ return roots;
+ }
+
+ private void markLeafRecursively(List<AccountSubjectVo> nodes) {
+ for (AccountSubjectVo node : nodes) {
+ List<AccountSubjectVo> children = node.getChildren();
+ node.setLeaf(children == null || children.isEmpty());
+ if (children != null && !children.isEmpty()) {
+ markLeafRecursively(children);
+ }
+ }
+ }
+
+ /**
+ * 鏍¢獙鐖跺瓙鍏崇郴锛氱埗鑺傜偣蹇呴』瀛樺湪锛屼笖涓嶈兘褰㈡垚寰幆寮曠敤銆�
+ */
+ private void validateParent(Long parentId, Long currentId) {
+ if (parentId == null || parentId <= 0) {
+ return;
+ }
+ if (currentId != null && parentId.equals(currentId)) {
+ throw new ServiceException("鐖剁鐩笉鑳介�夋嫨鑷韩");
+ }
+ AccountSubject parent = getById(parentId);
+ if (parent == null) {
+ throw new ServiceException("鐖剁鐩笉瀛樺湪锛岃閲嶆柊閫夋嫨");
+ }
+ // 闃叉褰㈡垚鐜細鏇存柊鏃讹紝鐖惰妭鐐逛笉鑳芥槸褰撳墠鑺傜偣鐨勪换鎰忓瓙瀛欒妭鐐广��
+ if (currentId != null) {
+ Set<Long> visited = new HashSet<>();
+ Long traceParentId = parentId;
+ while (traceParentId != null && traceParentId > 0) {
+ if (!visited.add(traceParentId)) {
+ throw new ServiceException("绉戠洰灞傜骇瀛樺湪寰幆寮曠敤锛岃妫�鏌ョ埗绉戠洰璁剧疆");
+ }
+ if (traceParentId.equals(currentId)) {
+ throw new ServiceException("鐖剁鐩笉鑳芥槸褰撳墠绉戠洰鎴栧叾瀛愮鐩�");
+ }
+ AccountSubject traceNode = getById(traceParentId);
+ if (traceNode == null) {
+ break;
+ }
+ traceParentId = traceNode.getParentId();
+ }
+ }
+ }
+
+ private Map<Long, List<Long>> buildChildrenIdMap(List<AccountSubject> subjects) {
+ Map<Long, List<Long>> map = new HashMap<>();
+ for (AccountSubject subject : subjects) {
+ if (subject.getId() == null || subject.getParentId() == null || subject.getParentId() <= 0) {
+ continue;
+ }
+ map.computeIfAbsent(subject.getParentId(), key -> new ArrayList<>()).add(subject.getId());
+ }
+ return map;
+ }
+
+ /**
+ * 鏀堕泦寰呭垹闄よ妭鐐瑰強鍏舵墍鏈夊瓙瀛欒妭鐐广��
+ */
+ private void collectDescendantIds(Long id, Map<Long, List<Long>> childrenIdMap, Set<Long> result) {
+ if (id == null || !result.add(id)) {
+ return;
+ }
+ List<Long> children = childrenIdMap.getOrDefault(id, Collections.emptyList());
+ for (Long childId : children) {
+ collectDescendantIds(childId, childrenIdMap, result);
+ }
+ }
}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
new file mode 100644
index 0000000..cb7a476
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
@@ -0,0 +1,231 @@
+package com.ruoyi.account.service.impl.financial;
+
+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.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.mapper.financial.FinFixedAssetMapper;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import com.ruoyi.account.service.financial.FinFixedAssetService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 鍥哄畾璧勪骇鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinFixedAssetServiceImpl extends ServiceImpl<FinFixedAssetMapper, FinFixedAsset> implements FinFixedAssetService {
+
+ private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
+ private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+ private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+ @Override
+ public IPage<FinFixedAsset> pageList(Page<FinFixedAsset> page, FinFixedAssetDto queryDto) {
+ LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetCode())) {
+ wrapper.like(FinFixedAsset::getAssetCode, queryDto.getAssetCode());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetName())) {
+ wrapper.like(FinFixedAsset::getAssetName, queryDto.getAssetName());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCategory())) {
+ wrapper.eq(FinFixedAsset::getCategory, queryDto.getCategory());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+ wrapper.eq(FinFixedAsset::getStatus, queryDto.getStatus());
+ }
+ wrapper.orderByDesc(FinFixedAsset::getId);
+ return page(page, wrapper);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean add(FinFixedAssetDto dto) {
+ validateForSave(dto, false);
+ if (StringUtils.isEmpty(dto.getAssetCode())) {
+ dto.setAssetCode(generateAssetCode());
+ }
+ BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+ dto.setResidualRate(residualRate);
+ BigDecimal accumulatedDepreciation = defaultMoney(dto.getAccumulatedDepreciation());
+ dto.setAccumulatedDepreciation(accumulatedDepreciation);
+ dto.setNetValue(calculateNetValue(dto.getOriginalValue(), accumulatedDepreciation));
+ if (StringUtils.isEmpty(dto.getStatus())) {
+ dto.setStatus("in_use");
+ }
+ return save(dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean update(FinFixedAssetDto dto) {
+ if (dto == null || dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+ }
+ FinFixedAsset existed = getById(dto.getId());
+ if (existed == null) {
+ throw new ServiceException("淇敼澶辫触锛屽浐瀹氳祫浜т笉瀛樺湪");
+ }
+ if (StringUtils.isEmpty(dto.getAssetCode())) {
+ dto.setAssetCode(existed.getAssetCode());
+ }
+ if (StringUtils.isEmpty(dto.getStatus())) {
+ dto.setStatus(existed.getStatus());
+ }
+ validateForSave(dto, true);
+ BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+ dto.setResidualRate(residualRate);
+ if (dto.getAccumulatedDepreciation() == null) {
+ dto.setAccumulatedDepreciation(defaultMoney(existed.getAccumulatedDepreciation()));
+ }
+ dto.setNetValue(calculateNetValue(dto.getOriginalValue(), dto.getAccumulatedDepreciation()));
+ return updateById(dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean deleteByIds(List<Long> ids) {
+ if (ids == null || ids.isEmpty()) {
+ throw new ServiceException("鍒犻櫎澶辫触锛岃閫夋嫨瑕佸垹闄ょ殑鏁版嵁");
+ }
+ return removeByIds(ids);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Map<String, Object> depreciate(List<Long> ids) {
+ LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+ if (ids != null && !ids.isEmpty()) {
+ wrapper.in(FinFixedAsset::getId, ids);
+ } else {
+ wrapper.eq(FinFixedAsset::getStatus, "in_use");
+ }
+ List<FinFixedAsset> assets = list(wrapper);
+ BigDecimal totalMonthlyDepreciation = ZERO;
+ int processedCount = 0;
+ for (FinFixedAsset asset : assets) {
+ if (!"in_use".equals(asset.getStatus())) {
+ continue;
+ }
+ BigDecimal monthlyDepreciation = calculateMonthlyDepreciation(
+ asset.getOriginalValue(),
+ asset.getResidualRate(),
+ asset.getUsefulLife()
+ );
+ BigDecimal accumulatedDepreciation = defaultMoney(asset.getAccumulatedDepreciation()).add(monthlyDepreciation);
+ if (accumulatedDepreciation.compareTo(defaultMoney(asset.getOriginalValue())) > 0) {
+ accumulatedDepreciation = defaultMoney(asset.getOriginalValue());
+ }
+ asset.setAccumulatedDepreciation(roundMoney(accumulatedDepreciation));
+ asset.setNetValue(calculateNetValue(asset.getOriginalValue(), asset.getAccumulatedDepreciation()));
+ updateById(asset);
+ processedCount++;
+ totalMonthlyDepreciation = totalMonthlyDepreciation.add(monthlyDepreciation);
+ }
+ Map<String, Object> result = new HashMap<>(4);
+ result.put("processedCount", processedCount);
+ result.put("totalMonthlyDepreciation", roundMoney(totalMonthlyDepreciation));
+ result.put("executionTime", LocalDateTime.now());
+ return result;
+ }
+
+ /**
+ * 鎸夋枃妗h鍒欐牎楠屽浐瀹氳祫浜ф暟鎹��
+ */
+ private void validateForSave(FinFixedAssetDto dto, boolean isUpdate) {
+ if (dto == null) {
+ throw new ServiceException("鍥哄畾璧勪骇鏁版嵁涓嶈兘涓虹┖");
+ }
+ if (isUpdate && dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getAssetName())) {
+ throw new ServiceException("璧勪骇鍚嶇О涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getCategory())) {
+ throw new ServiceException("璧勪骇绫诲埆涓嶈兘涓虹┖");
+ }
+ if (dto.getPurchaseDate() == null) {
+ throw new ServiceException("璐疆鏃ユ湡涓嶈兘涓虹┖");
+ }
+ if (dto.getOriginalValue() == null || dto.getOriginalValue().compareTo(BigDecimal.ZERO) < 0) {
+ throw new ServiceException("璧勪骇鍘熷�间笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+ }
+ if (dto.getUsefulLife() == null || dto.getUsefulLife() <= 0) {
+ throw new ServiceException("浣跨敤骞撮檺蹇呴』澶т簬0");
+ }
+ if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(BigDecimal.ZERO) < 0) {
+ throw new ServiceException("娈嬪�肩巼涓嶈兘灏忎簬0");
+ }
+ if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(ONE_HUNDRED) > 0) {
+ throw new ServiceException("娈嬪�肩巼涓嶈兘澶т簬100%");
+ }
+ if (StringUtils.isNotEmpty(dto.getAssetCode())) {
+ LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FinFixedAsset::getAssetCode, dto.getAssetCode());
+ if (isUpdate) {
+ wrapper.ne(FinFixedAsset::getId, dto.getId());
+ }
+ if (count(wrapper) > 0) {
+ throw new ServiceException("璧勪骇缂栧彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+ }
+ }
+ }
+
+ /**
+ * 鍥哄畾璧勪骇鎶樻棫鍏紡锛�
+ * monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)
+ */
+ private BigDecimal calculateMonthlyDepreciation(BigDecimal originalValue, BigDecimal residualRate, Integer usefulLife) {
+ BigDecimal normalizedOriginalValue = defaultMoney(originalValue);
+ BigDecimal normalizedResidualRate = normalizeResidualRate(residualRate);
+ BigDecimal depreciableRatio = BigDecimal.ONE.subtract(normalizedResidualRate.divide(ONE_HUNDRED, 8, RoundingMode.HALF_UP));
+ BigDecimal months = BigDecimal.valueOf((long) usefulLife * 12L);
+ if (months.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("浣跨敤骞撮檺鏃犳晥锛屾棤娉曡鎻愭姌鏃�");
+ }
+ return roundMoney(normalizedOriginalValue.multiply(depreciableRatio).divide(months, 8, RoundingMode.HALF_UP));
+ }
+
+ /**
+ * 鍑�鍊� = 鍘熷�� - 绱鎶樻棫銆�
+ */
+ private BigDecimal calculateNetValue(BigDecimal originalValue, BigDecimal accumulatedDepreciation) {
+ BigDecimal value = defaultMoney(originalValue).subtract(defaultMoney(accumulatedDepreciation));
+ if (value.compareTo(BigDecimal.ZERO) < 0) {
+ value = BigDecimal.ZERO;
+ }
+ return roundMoney(value);
+ }
+
+ private BigDecimal normalizeResidualRate(BigDecimal residualRate) {
+ return residualRate == null ? BigDecimal.ZERO : residualRate;
+ }
+
+ private BigDecimal defaultMoney(BigDecimal value) {
+ return value == null ? ZERO : roundMoney(value);
+ }
+
+ private BigDecimal roundMoney(BigDecimal value) {
+ if (value == null) {
+ return ZERO;
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private String generateAssetCode() {
+ return "GD" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java
new file mode 100644
index 0000000..72e5f06
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java
@@ -0,0 +1,250 @@
+package com.ruoyi.account.service.impl.financial;
+
+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.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.mapper.financial.FinIntangibleAssetMapper;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import com.ruoyi.account.service.financial.FinIntangibleAssetService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 鏃犲舰璧勪骇鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinIntangibleAssetServiceImpl extends ServiceImpl<FinIntangibleAssetMapper, FinIntangibleAsset> implements FinIntangibleAssetService {
+
+ private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
+ private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+ private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+ @Override
+ public IPage<FinIntangibleAsset> pageList(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto) {
+ LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetCode())) {
+ wrapper.like(FinIntangibleAsset::getAssetCode, queryDto.getAssetCode());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetName())) {
+ wrapper.like(FinIntangibleAsset::getAssetName, queryDto.getAssetName());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCategory())) {
+ wrapper.eq(FinIntangibleAsset::getCategory, queryDto.getCategory());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+ wrapper.eq(FinIntangibleAsset::getStatus, queryDto.getStatus());
+ }
+ wrapper.orderByDesc(FinIntangibleAsset::getId);
+ return page(page, wrapper);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean add(FinIntangibleAssetDto dto) {
+ validateForSave(dto, false);
+ if (StringUtils.isEmpty(dto.getAssetCode())) {
+ dto.setAssetCode(generateAssetCode());
+ }
+ BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+ dto.setResidualRate(residualRate);
+ BigDecimal accumulatedAmortization = defaultMoney(dto.getAccumulatedAmortization());
+ dto.setAccumulatedAmortization(accumulatedAmortization);
+ dto.setNetValue(calculateNetValue(dto.getOriginalValue(), accumulatedAmortization));
+ if (StringUtils.isEmpty(dto.getStatus())) {
+ dto.setStatus("in_use");
+ }
+ return save(dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean update(FinIntangibleAssetDto dto) {
+ if (dto == null || dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+ }
+ FinIntangibleAsset existed = getById(dto.getId());
+ if (existed == null) {
+ throw new ServiceException("淇敼澶辫触锛屾棤褰㈣祫浜т笉瀛樺湪");
+ }
+ if (StringUtils.isEmpty(dto.getAssetCode())) {
+ dto.setAssetCode(existed.getAssetCode());
+ }
+ if (StringUtils.isEmpty(dto.getStatus())) {
+ dto.setStatus(existed.getStatus());
+ }
+ validateForSave(dto, true);
+ BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+ dto.setResidualRate(residualRate);
+ if (dto.getAccumulatedAmortization() == null) {
+ dto.setAccumulatedAmortization(defaultMoney(existed.getAccumulatedAmortization()));
+ }
+ dto.setNetValue(calculateNetValue(dto.getOriginalValue(), dto.getAccumulatedAmortization()));
+ if (dto.getNetValue().compareTo(BigDecimal.ZERO) <= 0) {
+ dto.setStatus("amortized");
+ } else if ("amortized".equals(dto.getStatus())) {
+ dto.setStatus("in_use");
+ }
+ if (dto.getValidityDate() != null
+ && dto.getValidityDate().isBefore(LocalDate.now())
+ && !"amortized".equals(dto.getStatus())) {
+ dto.setStatus("expired");
+ }
+ return updateById(dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean deleteByIds(List<Long> ids) {
+ if (ids == null || ids.isEmpty()) {
+ throw new ServiceException("鍒犻櫎澶辫触锛岃閫夋嫨瑕佸垹闄ょ殑鏁版嵁");
+ }
+ return removeByIds(ids);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Map<String, Object> amortize(List<Long> ids) {
+ LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+ if (ids != null && !ids.isEmpty()) {
+ wrapper.in(FinIntangibleAsset::getId, ids);
+ } else {
+ wrapper.eq(FinIntangibleAsset::getStatus, "in_use");
+ }
+ List<FinIntangibleAsset> assets = list(wrapper);
+ BigDecimal totalMonthlyAmortization = ZERO;
+ int processedCount = 0;
+ for (FinIntangibleAsset asset : assets) {
+ if (!"in_use".equals(asset.getStatus())) {
+ continue;
+ }
+ BigDecimal monthlyAmortization = calculateMonthlyAmortization(
+ asset.getOriginalValue(),
+ asset.getResidualRate(),
+ asset.getAmortizationPeriod()
+ );
+ BigDecimal accumulatedAmortization = defaultMoney(asset.getAccumulatedAmortization()).add(monthlyAmortization);
+ if (accumulatedAmortization.compareTo(defaultMoney(asset.getOriginalValue())) > 0) {
+ accumulatedAmortization = defaultMoney(asset.getOriginalValue());
+ }
+ asset.setAccumulatedAmortization(roundMoney(accumulatedAmortization));
+ asset.setNetValue(calculateNetValue(asset.getOriginalValue(), asset.getAccumulatedAmortization()));
+
+ // 瑙勫垯锛氬綋鍑�鍊� <= 0 鏃讹紝鍑�鍊煎綊闆跺苟鏍囪涓哄凡鎽婇攢瀹屻��
+ if (asset.getNetValue().compareTo(BigDecimal.ZERO) <= 0) {
+ asset.setNetValue(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
+ asset.setStatus("amortized");
+ } else if (asset.getValidityDate() != null && asset.getValidityDate().isBefore(LocalDate.now())) {
+ asset.setStatus("expired");
+ }
+ updateById(asset);
+ processedCount++;
+ totalMonthlyAmortization = totalMonthlyAmortization.add(monthlyAmortization);
+ }
+ Map<String, Object> result = new HashMap<>(4);
+ result.put("processedCount", processedCount);
+ result.put("totalMonthlyAmortization", roundMoney(totalMonthlyAmortization));
+ result.put("executionTime", LocalDateTime.now());
+ return result;
+ }
+
+ /**
+ * 鎸夋枃妗h鍒欐牎楠屾棤褰㈣祫浜ф暟鎹��
+ */
+ private void validateForSave(FinIntangibleAssetDto dto, boolean isUpdate) {
+ if (dto == null) {
+ throw new ServiceException("鏃犲舰璧勪骇鏁版嵁涓嶈兘涓虹┖");
+ }
+ if (isUpdate && dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getAssetName())) {
+ throw new ServiceException("璧勪骇鍚嶇О涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getCategory())) {
+ throw new ServiceException("璧勪骇绫诲埆涓嶈兘涓虹┖");
+ }
+ if (dto.getAcquisitionDate() == null) {
+ throw new ServiceException("鍙栧緱鏃ユ湡涓嶈兘涓虹┖");
+ }
+ if (dto.getOriginalValue() == null || dto.getOriginalValue().compareTo(BigDecimal.ZERO) < 0) {
+ throw new ServiceException("璧勪骇鍘熷�间笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+ }
+ if (dto.getAmortizationPeriod() == null || dto.getAmortizationPeriod() <= 0) {
+ throw new ServiceException("鎽婇攢骞撮檺蹇呴』澶т簬0");
+ }
+ if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(BigDecimal.ZERO) < 0) {
+ throw new ServiceException("娈嬪�肩巼涓嶈兘灏忎簬0");
+ }
+ if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(ONE_HUNDRED) > 0) {
+ throw new ServiceException("娈嬪�肩巼涓嶈兘澶т簬100%");
+ }
+ if (StringUtils.isNotEmpty(dto.getAssetCode())) {
+ LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FinIntangibleAsset::getAssetCode, dto.getAssetCode());
+ if (isUpdate) {
+ wrapper.ne(FinIntangibleAsset::getId, dto.getId());
+ }
+ if (count(wrapper) > 0) {
+ throw new ServiceException("璧勪骇缂栧彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+ }
+ }
+ }
+
+ /**
+ * 鏃犲舰璧勪骇鎽婇攢鍏紡锛�
+ * monthlyAmortization = originalValue * (1 - residualRate/100) / (amortizationPeriod*12)
+ */
+ private BigDecimal calculateMonthlyAmortization(BigDecimal originalValue, BigDecimal residualRate, Integer amortizationPeriod) {
+ BigDecimal normalizedOriginalValue = defaultMoney(originalValue);
+ BigDecimal normalizedResidualRate = normalizeResidualRate(residualRate);
+ BigDecimal amortizableRatio = BigDecimal.ONE.subtract(normalizedResidualRate.divide(ONE_HUNDRED, 8, RoundingMode.HALF_UP));
+ BigDecimal months = BigDecimal.valueOf((long) amortizationPeriod * 12L);
+ if (months.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("鎽婇攢骞撮檺鏃犳晥锛屾棤娉曡鎻愭憡閿�");
+ }
+ return roundMoney(normalizedOriginalValue.multiply(amortizableRatio).divide(months, 8, RoundingMode.HALF_UP));
+ }
+
+ /**
+ * 鍑�鍊� = 鍘熷�� - 绱鎽婇攢銆�
+ */
+ private BigDecimal calculateNetValue(BigDecimal originalValue, BigDecimal accumulatedAmortization) {
+ BigDecimal value = defaultMoney(originalValue).subtract(defaultMoney(accumulatedAmortization));
+ if (value.compareTo(BigDecimal.ZERO) < 0) {
+ value = BigDecimal.ZERO;
+ }
+ return roundMoney(value);
+ }
+
+ private BigDecimal normalizeResidualRate(BigDecimal residualRate) {
+ return residualRate == null ? BigDecimal.ZERO : residualRate;
+ }
+
+ private BigDecimal defaultMoney(BigDecimal value) {
+ return value == null ? ZERO : roundMoney(value);
+ }
+
+ private BigDecimal roundMoney(BigDecimal value) {
+ if (value == null) {
+ return ZERO;
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private String generateAssetCode() {
+ return "WX" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
new file mode 100644
index 0000000..a36b241
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
@@ -0,0 +1,237 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
+import com.ruoyi.account.service.financial.FinLedgerService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.*;
+
+/**
+ * 绉戠洰鎬昏处/鏄庣粏璐︽湇鍔″疄鐜般��
+ */
+@Service
+@RequiredArgsConstructor
+public class FinLedgerServiceImpl implements FinLedgerService {
+
+ private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
+ private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+
+ private final FinVoucherEntryMapper finVoucherEntryMapper;
+
+ @Override
+ public List<FinLedgerRowVo> queryGeneralLedger(FinLedgerQueryDto queryDto) {
+ if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
+ return Collections.emptyList();
+ }
+ YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "寮�濮嬫湀浠�");
+ YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "缁撴潫鏈堜唤");
+ if (startMonth.isAfter(endMonth)) {
+ throw new ServiceException("寮�濮嬫湀浠戒笉鑳藉ぇ浜庣粨鏉熸湀浠�");
+ }
+ return Collections.singletonList(buildGeneralLedgerTotalRow(queryDto.getSubjectCode(), startMonth, endMonth));
+ }
+
+ @Override
+ public List<FinLedgerRowVo> queryDetailLedger(FinDetailLedgerQueryDto queryDto) {
+ if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
+ return Collections.emptyList();
+ }
+ YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "寮�濮嬫湀浠�");
+ YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "缁撴潫鏈堜唤");
+ if (startMonth.isAfter(endMonth)) {
+ throw new ServiceException("寮�濮嬫湀浠戒笉鑳藉ぇ浜庣粨鏉熸湀浠�");
+ }
+ return buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, queryDto.getAuxiliaryType(), queryDto.getAuxiliaryId());
+ }
+
+ /**
+ * 鏋勫缓璐︾翱琛屾暟鎹紝杈撳嚭鏈熷垵銆佸垎褰曘�佹湰鏈堝悎璁°�佹湰骞寸疮璁°��
+ */
+ private List<FinLedgerRowVo> buildLedgerRows(String subjectCode,
+ YearMonth startMonth,
+ YearMonth endMonth,
+ String auxiliaryType,
+ String auxiliaryId) {
+ LocalDate startDate = startMonth.atDay(1);
+ LocalDate endDate = endMonth.atEndOfMonth();
+
+ List<FinLedgerEntryRecordVo> openingEntries = finVoucherEntryMapper.listPostedEntriesBefore(
+ subjectCode, startDate, auxiliaryType, auxiliaryId
+ );
+ BigDecimal openingBalance = calculateBalance(openingEntries);
+
+ List<FinLedgerEntryRecordVo> currentPeriodEntries = finVoucherEntryMapper.listPostedEntries(
+ subjectCode, startDate, endDate, auxiliaryType, auxiliaryId
+ );
+ Map<YearMonth, List<FinLedgerEntryRecordVo>> monthEntriesMap = groupEntriesByMonth(currentPeriodEntries);
+
+ List<FinLedgerRowVo> rows = new ArrayList<>();
+ BigDecimal runningBalance = openingBalance;
+ BigDecimal yearDebit = ZERO;
+ BigDecimal yearCredit = ZERO;
+
+ for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
+ rows.add(buildOpeningRow(month.atDay(1), runningBalance));
+
+ List<FinLedgerEntryRecordVo> monthEntries = monthEntriesMap.getOrDefault(month, Collections.emptyList());
+ BigDecimal monthDebit = ZERO;
+ BigDecimal monthCredit = ZERO;
+ for (FinLedgerEntryRecordVo entry : monthEntries) {
+ BigDecimal debit = money(entry.getDebit());
+ BigDecimal credit = money(entry.getCredit());
+ runningBalance = runningBalance.add(debit).subtract(credit);
+ monthDebit = monthDebit.add(debit);
+ monthCredit = monthCredit.add(credit);
+
+ FinLedgerRowVo row = new FinLedgerRowVo();
+ row.setRowType("entry");
+ row.setDate(entry.getVoucherDate());
+ row.setVoucherNo(entry.getVoucherNo());
+ row.setSummary(StringUtils.isNotEmpty(entry.getSummary()) ? entry.getSummary() : "");
+ row.setDebit(debit);
+ row.setCredit(credit);
+ row.setBalance(money(runningBalance));
+ row.setDirection(resolveDirection(runningBalance));
+ rows.add(row);
+ }
+
+ rows.add(buildMonthlyTotalRow(month.atEndOfMonth(), monthDebit, monthCredit, runningBalance));
+ yearDebit = yearDebit.add(monthDebit);
+ yearCredit = yearCredit.add(monthCredit);
+ }
+
+ rows.add(buildYearlyTotalRow(endMonth.atEndOfMonth(), yearDebit, yearCredit, runningBalance));
+ return rows;
+ }
+
+ private FinLedgerRowVo buildGeneralLedgerTotalRow(String subjectCode, YearMonth startMonth, YearMonth endMonth) {
+ LocalDate startDate = startMonth.atDay(1);
+ LocalDate endDate = endMonth.atEndOfMonth();
+
+ List<FinLedgerEntryRecordVo> openingEntries = finVoucherEntryMapper.listPostedEntriesBefore(
+ subjectCode, startDate, null, null
+ );
+ BigDecimal openingBalance = calculateBalance(openingEntries);
+
+ List<FinLedgerEntryRecordVo> currentPeriodEntries = finVoucherEntryMapper.listPostedEntries(
+ subjectCode, startDate, endDate, null, null
+ );
+
+ BigDecimal totalDebit = ZERO;
+ BigDecimal totalCredit = ZERO;
+ for (FinLedgerEntryRecordVo entry : currentPeriodEntries) {
+ totalDebit = totalDebit.add(money(entry.getDebit()));
+ totalCredit = totalCredit.add(money(entry.getCredit()));
+ }
+
+ BigDecimal endingBalance = openingBalance.add(totalDebit).subtract(totalCredit);
+ FinLedgerRowVo totalRow = new FinLedgerRowVo();
+ totalRow.setRowType("yearly_total");
+ totalRow.setDate(endDate);
+ totalRow.setDebit(money(totalDebit));
+ totalRow.setCredit(money(totalCredit));
+ totalRow.setBalance(money(endingBalance));
+ totalRow.setDirection(resolveDirection(endingBalance));
+ return totalRow;
+ }
+
+ private Map<YearMonth, List<FinLedgerEntryRecordVo>> groupEntriesByMonth(List<FinLedgerEntryRecordVo> entries) {
+ Map<YearMonth, List<FinLedgerEntryRecordVo>> map = new LinkedHashMap<>();
+ for (FinLedgerEntryRecordVo entry : entries) {
+ if (entry.getVoucherDate() == null) {
+ continue;
+ }
+ YearMonth month = YearMonth.from(entry.getVoucherDate());
+ map.computeIfAbsent(month, key -> new ArrayList<>()).add(entry);
+ }
+ return map;
+ }
+
+ private FinLedgerRowVo buildOpeningRow(LocalDate date, BigDecimal openingBalance) {
+ FinLedgerRowVo row = new FinLedgerRowVo();
+ row.setRowType("opening");
+ row.setDate(date);
+ row.setVoucherNo("-");
+ row.setSummary("鏈熷垵浣欓");
+ row.setDebit(ZERO);
+ row.setCredit(ZERO);
+ row.setBalance(money(openingBalance));
+ row.setDirection(resolveDirection(openingBalance));
+ return row;
+ }
+
+ private FinLedgerRowVo buildMonthlyTotalRow(LocalDate date,
+ BigDecimal monthDebit,
+ BigDecimal monthCredit,
+ BigDecimal monthBalance) {
+ FinLedgerRowVo row = new FinLedgerRowVo();
+ row.setRowType("monthly_total");
+ row.setDate(date);
+ row.setVoucherNo("-");
+ row.setSummary("鏈湀鍚堣");
+ row.setDebit(money(monthDebit));
+ row.setCredit(money(monthCredit));
+ row.setBalance(money(monthBalance));
+ row.setDirection(resolveDirection(monthBalance));
+ return row;
+ }
+
+ private FinLedgerRowVo buildYearlyTotalRow(LocalDate date,
+ BigDecimal yearDebit,
+ BigDecimal yearCredit,
+ BigDecimal yearBalance) {
+ FinLedgerRowVo row = new FinLedgerRowVo();
+ row.setRowType("yearly_total");
+ row.setDate(date);
+ row.setVoucherNo("-");
+ row.setSummary("鍚堣");
+ row.setDebit(money(yearDebit));
+ row.setCredit(money(yearCredit));
+ row.setBalance(money(yearBalance));
+ row.setDirection(resolveDirection(yearBalance));
+ return row;
+ }
+
+ private BigDecimal calculateBalance(List<FinLedgerEntryRecordVo> entries) {
+ BigDecimal balance = ZERO;
+ for (FinLedgerEntryRecordVo entry : entries) {
+ balance = balance.add(money(entry.getDebit())).subtract(money(entry.getCredit()));
+ }
+ return money(balance);
+ }
+
+ private String resolveDirection(BigDecimal balance) {
+ return money(balance).compareTo(BigDecimal.ZERO) >= 0 ? "鍊�" : "璐�";
+ }
+
+ private YearMonth parseMonth(String value, String fieldLabel) {
+ if (StringUtils.isEmpty(value)) {
+ throw new ServiceException(fieldLabel + "涓嶈兘涓虹┖锛屾牸寮忓簲涓篩YYY-MM");
+ }
+ try {
+ return YearMonth.parse(value, MONTH_FORMATTER);
+ } catch (DateTimeParseException ex) {
+ throw new ServiceException(fieldLabel + "鏍煎紡閿欒锛屾牸寮忓簲涓篩YYY-MM");
+ }
+ }
+
+ private BigDecimal money(BigDecimal value) {
+ if (value == null) {
+ return ZERO;
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
new file mode 100644
index 0000000..b7548ef
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
@@ -0,0 +1,307 @@
+package com.ruoyi.account.service.impl.financial;
+
+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.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherEntryDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.mapper.AccountSubjectMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherMapper;
+import com.ruoyi.account.pojo.AccountSubject;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import com.ruoyi.account.service.financial.FinVoucherService;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 鍑瘉鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinVoucherServiceImpl extends ServiceImpl<FinVoucherMapper, FinVoucher> implements FinVoucherService {
+
+ private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+
+ private final FinVoucherEntryMapper finVoucherEntryMapper;
+ private final AccountSubjectMapper accountSubjectMapper;
+ private final FileUtil fileUtil;
+
+ @Override
+ public IPage<FinVoucher> pageList(Page<FinVoucher> page, FinVoucherPageDto queryDto) {
+ LambdaQueryWrapper<FinVoucher> wrapper = new LambdaQueryWrapper<>();
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getVoucherNo())) {
+ wrapper.like(FinVoucher::getVoucherNo, queryDto.getVoucherNo());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCreator())) {
+ wrapper.eq(FinVoucher::getCreator, queryDto.getCreator());
+ }
+ if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+ wrapper.eq(FinVoucher::getStatus, queryDto.getStatus());
+ }
+ if (queryDto != null && queryDto.getStartDate() != null) {
+ wrapper.ge(FinVoucher::getVoucherDate, queryDto.getStartDate());
+ }
+ if (queryDto != null && queryDto.getEndDate() != null) {
+ wrapper.le(FinVoucher::getVoucherDate, queryDto.getEndDate());
+ }
+ wrapper.orderByDesc(FinVoucher::getVoucherDate).orderByDesc(FinVoucher::getId);
+ return page(page, wrapper);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean addVoucher(FinVoucherDto dto) {
+ validateVoucherBasicInfo(dto, false);
+ List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+ FinVoucher voucher = new FinVoucher();
+ BeanUtils.copyProperties(dto, voucher);
+ voucher.setStatus("unposted");
+ voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ voucher.setDebit(totalDebit);
+ voucher.setCredit(totalCredit);
+ if (StringUtils.isEmpty(voucher.getSummary())) {
+ voucher.setSummary(findDefaultSummary(validEntries));
+ }
+ save(voucher);
+ saveEntries(voucher.getId(), validEntries);
+ // 5. 淇濆瓨閿�鍞彴璐﹂檮浠�
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_VOUCHER, voucher.getId(), dto.getStorageBlobDTOs());
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean updateVoucher(FinVoucherDto dto) {
+ validateVoucherBasicInfo(dto, true);
+ FinVoucher existed = getById(dto.getId());
+ if (existed == null) {
+ throw new ServiceException("淇敼澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(existed.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽淇敼");
+ }
+ List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+ FinVoucher voucher = new FinVoucher();
+ BeanUtils.copyProperties(dto, voucher);
+ voucher.setStatus(existed.getStatus());
+ voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ voucher.setDebit(totalDebit);
+ voucher.setCredit(totalCredit);
+ if (StringUtils.isEmpty(voucher.getSummary())) {
+ voucher.setSummary(findDefaultSummary(validEntries));
+ }
+ updateById(voucher);
+
+ LambdaQueryWrapper<FinVoucherEntry> deleteWrapper = new LambdaQueryWrapper<>();
+ deleteWrapper.eq(FinVoucherEntry::getVoucherId, voucher.getId());
+ finVoucherEntryMapper.delete(deleteWrapper);
+ saveEntries(voucher.getId(), validEntries);
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_VOUCHER, voucher.getId(), dto.getStorageBlobDTOs());
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean postVoucher(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("杩囪处澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(voucher.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽杩囪处");
+ }
+ voucher.setStatus("posted");
+ return updateById(voucher);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean cancelVoucher(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("浣滃簾澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ if (!"unposted".equals(voucher.getStatus())) {
+ throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽浣滃簾");
+ }
+ voucher.setStatus("cancelled");
+ return updateById(voucher);
+ }
+
+ @Override
+ public FinVoucherDetailVo detail(Long id) {
+ FinVoucher voucher = getById(id);
+ if (voucher == null) {
+ throw new ServiceException("鏌ヨ澶辫触锛屽嚟璇佷笉瀛樺湪");
+ }
+ LambdaQueryWrapper<FinVoucherEntry> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FinVoucherEntry::getVoucherId, id)
+ .orderByAsc(FinVoucherEntry::getRowNo)
+ .orderByAsc(FinVoucherEntry::getId);
+ List<FinVoucherEntry> entries = finVoucherEntryMapper.selectList(wrapper);
+
+ FinVoucherDetailVo vo = new FinVoucherDetailVo();
+ BeanUtils.copyProperties(voucher, vo);
+ vo.setEntries(entries);
+ vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_VOUCHER, id));
+ return vo;
+ }
+
+ /**
+ * 鏍¢獙鍑瘉涓昏〃瀛楁銆佺姸鎬佸瓧娈典笌鍞竴鎬с��
+ */
+ private void validateVoucherBasicInfo(FinVoucherDto dto, boolean isUpdate) {
+ if (dto == null) {
+ throw new ServiceException("鍑瘉鏁版嵁涓嶈兘涓虹┖");
+ }
+ if (isUpdate && dto.getId() == null) {
+ throw new ServiceException("淇敼澶辫触锛屽嚟璇両D涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isEmpty(dto.getVoucherNo())) {
+ throw new ServiceException("鍑瘉瀛楀彿涓嶈兘涓虹┖");
+ }
+ if (dto.getVoucherDate() == null) {
+ throw new ServiceException("鍑瘉鏃ユ湡涓嶈兘涓虹┖");
+ }
+ LambdaQueryWrapper<FinVoucher> uniqueWrapper = new LambdaQueryWrapper<>();
+ uniqueWrapper.eq(FinVoucher::getVoucherNo, dto.getVoucherNo());
+ if (isUpdate) {
+ uniqueWrapper.ne(FinVoucher::getId, dto.getId());
+ }
+ if (count(uniqueWrapper) > 0) {
+ throw new ServiceException("鍑瘉瀛楀彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+ }
+ }
+
+ /**
+ * 杩囨护鏈夋晥鍒嗗綍骞舵墽琛屽�熻捶骞宠 鏍¢獙銆�
+ */
+ private List<FinVoucherEntry> buildAndValidateEntries(FinVoucherDto dto) {
+ List<FinVoucherEntryDto> rawEntries = dto.getEntries();
+ if (rawEntries == null || rawEntries.isEmpty()) {
+ throw new ServiceException("鍒嗗綍涓嶈兘涓虹┖锛岃嚦灏戦渶瑕佷竴鏉℃湁鏁堝垎褰�");
+ }
+ List<FinVoucherEntry> validEntries = new ArrayList<>();
+ int rowNo = 1;
+ for (FinVoucherEntryDto entryDto : rawEntries) {
+ if (entryDto == null || StringUtils.isEmpty(entryDto.getSubjectCode())) {
+ continue;
+ }
+ BigDecimal debit = defaultMoney(entryDto.getDebit());
+ BigDecimal credit = defaultMoney(entryDto.getCredit());
+ if (debit.compareTo(BigDecimal.ZERO) <= 0 && credit.compareTo(BigDecimal.ZERO) <= 0) {
+ continue;
+ }
+ if (debit.compareTo(BigDecimal.ZERO) > 0 && credit.compareTo(BigDecimal.ZERO) > 0) {
+ throw new ServiceException("鍒嗗綍鍊熸柟鍜岃捶鏂逛笉鑳藉悓鏃跺ぇ浜�0");
+ }
+ FinVoucherEntry entry = new FinVoucherEntry();
+ BeanUtils.copyProperties(entryDto, entry);
+ entry.setDebit(debit);
+ entry.setCredit(credit);
+ entry.setRowNo(rowNo++);
+ validEntries.add(entry);
+ }
+ if (validEntries.isEmpty()) {
+ throw new ServiceException("鍒嗗綍鑷冲皯闇�瑕佷竴鏉℃湁鏁堣锛堢鐩笉绌猴紝涓斿�熸柟鎴栬捶鏂瑰ぇ浜�0锛�");
+ }
+
+ // 鍒嗗綍绉戠洰蹇呴』瀛樺湪锛岄伩鍏嶈剰绉戠洰缂栫爜鍏ヨ处銆�
+ Set<String> subjectCodes = validEntries.stream()
+ .map(FinVoucherEntry::getSubjectCode)
+ .filter(StringUtils::isNotEmpty)
+ .collect(Collectors.toSet());
+ if (subjectCodes.isEmpty()) {
+ throw new ServiceException("鍒嗗綍绉戠洰涓嶈兘涓虹┖");
+ }
+ LambdaQueryWrapper<AccountSubject> subjectWrapper = new LambdaQueryWrapper<>();
+ subjectWrapper.in(AccountSubject::getSubjectCode, subjectCodes);
+ List<AccountSubject> subjects = accountSubjectMapper.selectList(subjectWrapper);
+ Map<String, AccountSubject> subjectMap = subjects.stream()
+ .collect(Collectors.toMap(AccountSubject::getSubjectCode, it -> it, (a, b) -> a));
+ for (FinVoucherEntry entry : validEntries) {
+ AccountSubject accountSubject = subjectMap.get(entry.getSubjectCode());
+ if (accountSubject == null) {
+ throw new ServiceException("绉戠洰缂栫爜涓嶅瓨鍦細" + entry.getSubjectCode());
+ }
+ if (StringUtils.isEmpty(entry.getSubjectName())) {
+ entry.setSubjectName(accountSubject.getSubjectName());
+ }
+ }
+
+ BigDecimal totalDebit = calculateTotalDebit(validEntries);
+ BigDecimal totalCredit = calculateTotalCredit(validEntries);
+ if (totalDebit.compareTo(BigDecimal.ZERO) <= 0 || totalCredit.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("鍊熻捶閲戦蹇呴』澶т簬0");
+ }
+ if (totalDebit.compareTo(totalCredit) != 0) {
+ throw new ServiceException("鍊熻捶涓嶅钩琛★紝绂佹淇濆瓨");
+ }
+ return validEntries;
+ }
+
+ private void saveEntries(Long voucherId, List<FinVoucherEntry> entries) {
+ if (voucherId == null) {
+ throw new ServiceException("鍑瘉ID涓嶈兘涓虹┖");
+ }
+ for (FinVoucherEntry entry : entries) {
+ entry.setVoucherId(voucherId);
+ finVoucherEntryMapper.insert(entry);
+ }
+ }
+
+ private String findDefaultSummary(List<FinVoucherEntry> entries) {
+ for (FinVoucherEntry entry : entries) {
+ if (StringUtils.isNotEmpty(entry.getSummary())) {
+ return entry.getSummary();
+ }
+ }
+ return "";
+ }
+
+ private BigDecimal calculateTotalDebit(List<FinVoucherEntry> entries) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (FinVoucherEntry entry : entries) {
+ total = total.add(defaultMoney(entry.getDebit()));
+ }
+ return total.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal calculateTotalCredit(List<FinVoucherEntry> entries) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (FinVoucherEntry entry : entries) {
+ total = total.add(defaultMoney(entry.getCredit()));
+ }
+ return total.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal defaultMoney(BigDecimal value) {
+ if (value == null) {
+ return ZERO;
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+}
diff --git a/src/main/java/com/ruoyi/ai/assistant/ManufacturingAgent.java b/src/main/java/com/ruoyi/ai/assistant/ManufacturingAgent.java
new file mode 100644
index 0000000..f0e8cf7
--- /dev/null
+++ b/src/main/java/com/ruoyi/ai/assistant/ManufacturingAgent.java
@@ -0,0 +1,21 @@
+package com.ruoyi.ai.assistant;
+
+import dev.langchain4j.service.MemoryId;
+import dev.langchain4j.service.SystemMessage;
+import dev.langchain4j.service.UserMessage;
+import dev.langchain4j.service.spring.AiService;
+import reactor.core.publisher.Flux;
+
+import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
+
+@AiService(
+ wiringMode = EXPLICIT,
+ streamingChatModel = "qwenStreamingChatModel",
+ chatMemoryProvider = "chatMemoryProviderManufacturing",
+ tools = "manufacturingAgentTools"
+)
+public interface ManufacturingAgent {
+
+ @SystemMessage(fromResource = "manufacturing-agent-prompt.txt")
+ Flux<String> chat(@MemoryId String memoryId, @UserMessage String userMessage);
+}
diff --git a/src/main/java/com/ruoyi/ai/assistant/ManufacturingIntentExecutor.java b/src/main/java/com/ruoyi/ai/assistant/ManufacturingIntentExecutor.java
new file mode 100644
index 0000000..e9d9396
--- /dev/null
+++ b/src/main/java/com/ruoyi/ai/assistant/ManufacturingIntentExecutor.java
@@ -0,0 +1,136 @@
+package com.ruoyi.ai.assistant;
+
+import com.ruoyi.ai.tools.ManufacturingAgentTools;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Component
+public class ManufacturingIntentExecutor {
+
+ private static final Pattern LIMIT_PATTERN = Pattern.compile("(鍓峾鏈�杩�)?(\\d{1,2})鏉�");
+ private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
+
+ private final ManufacturingAgentTools manufacturingAgentTools;
+
+ public ManufacturingIntentExecutor(ManufacturingAgentTools manufacturingAgentTools) {
+ this.manufacturingAgentTools = manufacturingAgentTools;
+ }
+
+ public String tryExecute(String memoryId, String message) {
+ if (!StringUtils.hasText(message)) {
+ return null;
+ }
+ String text = message.trim();
+ String keyword = extractKeyword(text);
+ Integer limit = extractLimit(text);
+ String startDate = extractStartDate(text);
+ String endDate = extractEndDate(text);
+
+ if (containsAny(text, "棰勮", "鍛婅", "椋庨櫓", "鎻愰啋")) {
+ return manufacturingAgentTools.getWarningBoard(memoryId, startDate, endDate, text);
+ }
+ if (containsAny(text, "鍒嗘瀽", "缁熻", "瓒嬪娍", "鐪嬫澘", "鎶ヨ〃", "鎬昏")) {
+ return manufacturingAgentTools.analyzeFactory(memoryId, startDate, endDate, text);
+ }
+ if (containsAny(text, "鍔�", "澶勭悊", "娲惧伐", "瀹夋帓", "闂幆", "璺熻繘", "澶勭疆")) {
+ return manufacturingAgentTools.planActions(memoryId, text);
+ }
+
+ if (containsAny(text, "鐢熶骇鐜板満", "鐜板満", "杞﹂棿")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "site", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "璁″垝", "鎺掍骇", "mps")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "plan", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "宸ュ崟", "浣滀笟鍗�", "浠诲姟鍗�", "浠诲姟")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "workorder", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "璁惧", "缁翠慨", "淇濆吇", "鏁呴殰")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "device", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "璐ㄩ噺", "璐ㄦ", "涓嶅悎鏍�", "妫�楠�")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "quality", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "鐗╂枡", "搴撳瓨", "搴撲綅", "鍏ュ簱", "鍑哄簱")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "material", keyword, limit, startDate, endDate, text);
+ }
+ if (containsAny(text, "寮傚父", "渚嬪", "鍋忓樊")) {
+ return manufacturingAgentTools.queryDomain(memoryId, "exception", keyword, limit, startDate, endDate, text);
+ }
+ return null;
+ }
+
+ private boolean containsAny(String text, String... keywords) {
+ for (String keyword : keywords) {
+ if (text.toLowerCase().contains(keyword.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Integer extractLimit(String text) {
+ Matcher matcher = LIMIT_PATTERN.matcher(text);
+ return matcher.find() ? Integer.parseInt(matcher.group(2)) : 10;
+ }
+
+ private String extractStartDate(String text) {
+ Matcher matcher = DATE_PATTERN.matcher(text);
+ return matcher.find() ? matcher.group(1) : null;
+ }
+
+ private String extractEndDate(String text) {
+ Matcher matcher = DATE_PATTERN.matcher(text);
+ if (!matcher.find()) {
+ return null;
+ }
+ return matcher.find() ? matcher.group(1) : null;
+ }
+
+ private String extractKeyword(String text) {
+ String cleaned = text
+ .replace("鏌ヨ", "")
+ .replace("鏌ョ湅", "")
+ .replace("甯垜", "")
+ .replace("璇�", "")
+ .replace("涓�涓�", "")
+ .replace("鎵�鏈�", "")
+ .replace("鍏ㄩ儴", "")
+ .replace("浠婂勾", "")
+ .replace("鏈勾", "")
+ .replace("鍘诲勾", "")
+ .replace("鏈湀", "")
+ .replace("涓婃湀", "")
+ .replace("鏈懆", "")
+ .replace("涓婂懆", "")
+ .replace("浠婂ぉ", "")
+ .replace("鏄ㄥぉ", "")
+ .replace("杩�30澶�", "")
+ .replace("杩�7澶�", "")
+ .replace("杩�15澶�", "")
+ .replace("杩�60澶�", "")
+ .replace("鏈�杩�30澶�", "")
+ .replace("鏈�杩�7澶�", "")
+ .replace("鏈�杩�15澶�", "")
+ .replace("鏈�杩�60澶�", "")
+ .replace("鐢熶骇鐜板満", "")
+ .replace("鐜板満", "")
+ .replace("鐢熶骇宸ュ崟", "")
+ .replace("鐢熶骇", "")
+ .replace("璁″垝", "")
+ .replace("鎺掍骇", "")
+ .replace("宸ュ崟", "")
+ .replace("璁惧", "")
+ .replace("璐ㄩ噺", "")
+ .replace("鐗╂枡", "")
+ .replace("搴撳瓨", "")
+ .replace("寮傚父", "")
+ .replace("鍓�10鏉�", "")
+ .replace("鏈�杩�10鏉�", "")
+ .trim();
+ return cleaned.length() >= 2 ? cleaned : null;
+ }
+}
diff --git a/src/main/java/com/ruoyi/ai/config/ManufacturingAgentConfig.java b/src/main/java/com/ruoyi/ai/config/ManufacturingAgentConfig.java
new file mode 100644
index 0000000..79aa222
--- /dev/null
+++ b/src/main/java/com/ruoyi/ai/config/ManufacturingAgentConfig.java
@@ -0,0 +1,20 @@
+package com.ruoyi.ai.config;
+
+import com.ruoyi.ai.store.MongoChatMemoryStore;
+import dev.langchain4j.memory.chat.ChatMemoryProvider;
+import dev.langchain4j.memory.chat.MessageWindowChatMemory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ManufacturingAgentConfig {
+
+ @Bean
+ ChatMemoryProvider chatMemoryProviderManufacturing(MongoChatMemoryStore mongoChatMemoryStore) {
+ return memoryId -> MessageWindowChatMemory.builder()
+ .id(memoryId)
+ .maxMessages(30)
+ .chatMemoryStore(mongoChatMemoryStore)
+ .build();
+ }
+}
diff --git a/src/main/java/com/ruoyi/ai/controller/ManufacturingAiController.java b/src/main/java/com/ruoyi/ai/controller/ManufacturingAiController.java
new file mode 100644
index 0000000..cb7c0ba
--- /dev/null
+++ b/src/main/java/com/ruoyi/ai/controller/ManufacturingAiController.java
@@ -0,0 +1,102 @@
+package com.ruoyi.ai.controller;
+
+import com.ruoyi.ai.assistant.ManufacturingAgent;
+import com.ruoyi.ai.assistant.ManufacturingIntentExecutor;
+import com.ruoyi.ai.bean.ChatForm;
+import com.ruoyi.ai.context.AiSessionUserContext;
+import com.ruoyi.ai.service.AiChatSessionService;
+import com.ruoyi.ai.store.MongoChatMemoryStore;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import dev.langchain4j.data.message.AiMessage;
+import dev.langchain4j.data.message.UserMessage;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+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;
+import reactor.core.publisher.Flux;
+
+import java.util.List;
+
+@Tag(name = "鍒堕�犳櫤鑳藉姪鎵�")
+@RestController
+@RequestMapping("/manufacturing-ai")
+public class ManufacturingAiController extends BaseController {
+
+ private final ManufacturingAgent manufacturingAgent;
+ private final ManufacturingIntentExecutor manufacturingIntentExecutor;
+ private final AiSessionUserContext aiSessionUserContext;
+ private final MongoChatMemoryStore mongoChatMemoryStore;
+ private final AiChatSessionService aiChatSessionService;
+
+ public ManufacturingAiController(ManufacturingAgent manufacturingAgent,
+ ManufacturingIntentExecutor manufacturingIntentExecutor,
+ AiSessionUserContext aiSessionUserContext,
+ MongoChatMemoryStore mongoChatMemoryStore,
+ AiChatSessionService aiChatSessionService) {
+ this.manufacturingAgent = manufacturingAgent;
+ this.manufacturingIntentExecutor = manufacturingIntentExecutor;
+ this.aiSessionUserContext = aiSessionUserContext;
+ this.mongoChatMemoryStore = mongoChatMemoryStore;
+ this.aiChatSessionService = aiChatSessionService;
+ }
+
+ @Operation(summary = "鍒堕�犲璇�")
+ @PostMapping(value = "/chat", produces = "text/stream;charset=utf-8")
+ public Flux<String> chat(@RequestBody ChatForm chatForm) {
+ if (!StringUtils.hasText(chatForm.getMemoryId())) {
+ return Flux.just("memoryId涓嶈兘涓虹┖");
+ }
+ if (!StringUtils.hasText(chatForm.getMessage())) {
+ return Flux.just("message涓嶈兘涓虹┖");
+ }
+
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ String memoryId = chatForm.getMemoryId();
+ String userMessage = chatForm.getMessage();
+
+ aiSessionUserContext.bind(memoryId, loginUser);
+ aiChatSessionService.touchSession(memoryId, loginUser, userMessage);
+
+ String directResponse = manufacturingIntentExecutor.tryExecute(memoryId, userMessage);
+ if (StringUtils.isNotEmpty(directResponse)) {
+ mongoChatMemoryStore.appendMessages(
+ memoryId,
+ List.of(UserMessage.from(userMessage), AiMessage.from(directResponse))
+ );
+ aiChatSessionService.refreshSessionStats(memoryId, loginUser);
+ return Flux.just(directResponse);
+ }
+
+ return manufacturingAgent.chat(memoryId, userMessage)
+ .doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser))
+ .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser));
+ }
+
+ @Operation(summary = "鍒堕�犱細璇濆垪琛�")
+ @GetMapping("/history/sessions")
+ public AjaxResult listSessions() {
+ return success(aiChatSessionService.listCurrentUserSessions(SecurityUtils.getLoginUser()));
+ }
+
+ @Operation(summary = "鍒堕�犱細璇濇秷鎭�")
+ @GetMapping("/history/messages/{memoryId}")
+ public AjaxResult listMessages(@PathVariable String memoryId) {
+ return success(aiChatSessionService.listCurrentUserMessages(memoryId, SecurityUtils.getLoginUser()));
+ }
+
+ @Operation(summary = "鍒犻櫎鍒堕�犱細璇�")
+ @DeleteMapping("/history/{memoryId}")
+ public AjaxResult deleteSession(@PathVariable String memoryId) {
+ aiSessionUserContext.remove(memoryId);
+ return toAjax(aiChatSessionService.deleteCurrentUserSession(memoryId, SecurityUtils.getLoginUser()));
+ }
+}
diff --git a/src/main/java/com/ruoyi/ai/tools/ManufacturingAgentTools.java b/src/main/java/com/ruoyi/ai/tools/ManufacturingAgentTools.java
new file mode 100644
index 0000000..1ff96c1
--- /dev/null
+++ b/src/main/java/com/ruoyi/ai/tools/ManufacturingAgentTools.java
@@ -0,0 +1,1035 @@
+package com.ruoyi.ai.tools;
+
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.ruoyi.ai.context.AiSessionUserContext;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.device.mapper.DeviceDefectRecordMapper;
+import com.ruoyi.device.mapper.DeviceLedgerMapper;
+import com.ruoyi.device.mapper.DeviceRepairMapper;
+import com.ruoyi.device.pojo.DeviceDefectRecord;
+import com.ruoyi.device.pojo.DeviceLedger;
+import com.ruoyi.device.pojo.DeviceRepair;
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.procurementrecord.mapper.ProcurementExceptionRecordMapper;
+import com.ruoyi.procurementrecord.pojo.ProcurementExceptionRecord;
+import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
+import com.ruoyi.production.mapper.ProductionOrderMapper;
+import com.ruoyi.production.mapper.ProductionPlanMapper;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
+import com.ruoyi.production.pojo.ProductionOperationTask;
+import com.ruoyi.production.pojo.ProductionOrder;
+import com.ruoyi.production.pojo.ProductionPlan;
+import com.ruoyi.production.pojo.ProductionProductMain;
+import com.ruoyi.quality.mapper.QualityInspectMapper;
+import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
+import com.ruoyi.quality.pojo.QualityInspect;
+import com.ruoyi.quality.pojo.QualityUnqualified;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.pojo.StockInventory;
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+import dev.langchain4j.agent.tool.ToolMemoryId;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Component
+public class ManufacturingAgentTools {
+
+ private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final int DEFAULT_LIMIT = 10;
+ private static final int MAX_LIMIT = 30;
+ private static final int DEVICE_REPAIR_STATUS_PENDING = 0;
+
+ private final ProductionPlanMapper productionPlanMapper;
+ private final ProductionOrderMapper productionOrderMapper;
+ private final ProductionOperationTaskMapper productionOperationTaskMapper;
+ private final ProductionProductMainMapper productionProductMainMapper;
+ private final DeviceLedgerMapper deviceLedgerMapper;
+ private final DeviceRepairMapper deviceRepairMapper;
+ private final DeviceDefectRecordMapper deviceDefectRecordMapper;
+ private final QualityInspectMapper qualityInspectMapper;
+ private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
+ private final StockInventoryMapper stockInventoryMapper;
+ private final ProcurementExceptionRecordMapper procurementExceptionRecordMapper;
+ private final AiSessionUserContext aiSessionUserContext;
+
+ public ManufacturingAgentTools(ProductionPlanMapper productionPlanMapper,
+ ProductionOrderMapper productionOrderMapper,
+ ProductionOperationTaskMapper productionOperationTaskMapper,
+ ProductionProductMainMapper productionProductMainMapper,
+ DeviceLedgerMapper deviceLedgerMapper,
+ DeviceRepairMapper deviceRepairMapper,
+ DeviceDefectRecordMapper deviceDefectRecordMapper,
+ QualityInspectMapper qualityInspectMapper,
+ QualityUnqualifiedMapper qualityUnqualifiedMapper,
+ StockInventoryMapper stockInventoryMapper,
+ ProcurementExceptionRecordMapper procurementExceptionRecordMapper,
+ AiSessionUserContext aiSessionUserContext) {
+ this.productionPlanMapper = productionPlanMapper;
+ this.productionOrderMapper = productionOrderMapper;
+ this.productionOperationTaskMapper = productionOperationTaskMapper;
+ this.productionProductMainMapper = productionProductMainMapper;
+ this.deviceLedgerMapper = deviceLedgerMapper;
+ this.deviceRepairMapper = deviceRepairMapper;
+ this.deviceDefectRecordMapper = deviceDefectRecordMapper;
+ this.qualityInspectMapper = qualityInspectMapper;
+ this.qualityUnqualifiedMapper = qualityUnqualifiedMapper;
+ this.stockInventoryMapper = stockInventoryMapper;
+ this.procurementExceptionRecordMapper = procurementExceptionRecordMapper;
+ this.aiSessionUserContext = aiSessionUserContext;
+ }
+
+ @Tool(name = "鏌ヨ鍒堕�犱笟鍔″煙鏁版嵁", value = "鎸変笟鍔″煙鏌ヨ鐢熶骇鐜板満銆佽鍒掋�佸伐鍗曘�佽澶囥�佽川閲忋�佺墿鏂欍�佸紓甯稿鐞嗙浉鍏虫暟鎹��")
+ public String queryDomain(@ToolMemoryId String memoryId,
+ @P(value = "涓氬姟鍩燂紝site/plan/workorder/device/quality/material/exception") String domain,
+ @P(value = "鍏抽敭瀛楋紝鍙笉浼�", required = false) String keyword,
+ @P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit,
+ @P(value = "寮�濮嬫棩鏈� yyyy-MM-dd锛屽彲涓嶄紶", required = false) String startDate,
+ @P(value = "缁撴潫鏃ユ湡 yyyy-MM-dd锛屽彲涓嶄紶", required = false) String endDate,
+ @P(value = "鏃堕棿鑼冨洿鎻忚堪锛屼緥濡備粖骞淬�佹湰鏈堛�佽繎30澶�", required = false) String timeRange) {
+ LoginUser loginUser = currentLoginUser(memoryId);
+ int finalLimit = normalizeLimit(limit);
+ DateRange range = resolveDateRange(startDate, endDate, timeRange);
+ boolean hasTimeConstraint = hasTimeConstraint(startDate, endDate, timeRange);
+ String normalizedDomain = normalizeDomain(domain);
+
+ return switch (normalizedDomain) {
+ case "site" -> siteSnapshot(loginUser, range);
+ case "plan" -> listProductionPlans(loginUser, keyword, finalLimit, range);
+ case "workorder" -> listWorkOrders(loginUser, keyword, finalLimit, range);
+ case "device" -> isRepairIntent(keyword, timeRange)
+ ? listDeviceRepairs(loginUser, normalizeDeviceQueryKeyword(keyword, timeRange), finalLimit, range, hasTimeConstraint)
+ : listDevices(loginUser, normalizeDeviceQueryKeyword(keyword, timeRange), finalLimit);
+ case "repair" -> listDeviceRepairs(loginUser, normalizeDeviceQueryKeyword(keyword, timeRange), finalLimit, range, hasTimeConstraint);
+ case "quality" -> listQualityIssues(loginUser, keyword, finalLimit, range);
+ case "material" -> listMaterialInventory(loginUser, keyword, finalLimit);
+ case "exception" -> listExceptions(loginUser, keyword, finalLimit, range);
+ default -> jsonResponse(false, "manufacturing_query", "涓嶆敮鎸佺殑涓氬姟鍩�: " + safe(domain), Map.of(), Map.of(), Map.of());
+ };
+ }
+
+ @Tool(name = "鍒堕�犻璀︾湅鏉�", value = "璁$畻璁″垝銆佸伐鍗曘�佽澶囥�佽川閲忋�佺墿鏂欍�佸紓甯稿鐞嗙殑棰勮淇℃伅銆�")
+ public String getWarningBoard(@ToolMemoryId String memoryId,
+ @P(value = "寮�濮嬫棩鏈� yyyy-MM-dd锛屽彲涓嶄紶", required = false) String startDate,
+ @P(value = "缁撴潫鏃ユ湡 yyyy-MM-dd锛屽彲涓嶄紶", required = false) String endDate,
+ @P(value = "鏃堕棿鑼冨洿鎻忚堪锛屼緥濡備粖澶┿�佹湰鍛ㄣ�佹湰鏈堛�佽繎30澶�", required = false) String timeRange) {
+ LoginUser loginUser = currentLoginUser(memoryId);
+ DateRange range = resolveDateRange(startDate, endDate, timeRange);
+ LocalDate today = LocalDate.now();
+
+ long overduePlanCount = countOverduePlans(loginUser, today);
+ long overdueWorkOrderCount = countOverdueWorkOrders(loginUser, today);
+ long pendingRepairCount = countPendingRepairs(loginUser);
+ long qualityOpenCount = countOpenQualityIssues(loginUser, range);
+ long lowStockCount = countLowStock(loginUser);
+ long exceptionCount = countExceptionRecords(loginUser, range);
+
+ List<Map<String, Object>> warningItems = new ArrayList<>();
+ if (overduePlanCount > 0) {
+ warningItems.add(warningItem("high", "璁″垝閫炬湡", overduePlanCount, "鏈夌敓浜ц鍒掕秴杩囬渶姹傛棩鏈熶粛鏈畬鎴�"));
+ }
+ if (overdueWorkOrderCount > 0) {
+ warningItems.add(warningItem("high", "宸ュ崟閫炬湡", overdueWorkOrderCount, "鏈夊伐鍗曡鍒掔粨鏉熸棩鏈熷凡杩囦粛鏈畬宸�"));
+ }
+ if (pendingRepairCount > 0) {
+ warningItems.add(warningItem("medium", "璁惧寰呯淮淇�", pendingRepairCount, "瀛樺湪寰呯淮淇�/缁翠慨涓殑璁惧"));
+ }
+ if (qualityOpenCount > 0) {
+ warningItems.add(warningItem("high", "璐ㄩ噺鏈棴鐜�", qualityOpenCount, "瀛樺湪鏈鐞嗗畬鎴愮殑涓嶅悎鏍艰褰�"));
+ }
+ if (lowStockCount > 0) {
+ warningItems.add(warningItem("medium", "鐗╂枡浣庡簱瀛�", lowStockCount, "搴撳瓨鏁伴噺浣庝簬鎴栫瓑浜庨璀﹂槇鍊�"));
+ }
+ if (exceptionCount > 0) {
+ warningItems.add(warningItem("medium", "寮傚父璁板綍", exceptionCount, "鏃堕棿鑼冨洿鍐呭瓨鍦ㄥ紓甯稿鐞嗚褰�"));
+ }
+
+ Map<String, Object> summary = new LinkedHashMap<>();
+ summary.put("timeRange", range.label());
+ summary.put("startDate", range.start().toString());
+ summary.put("endDate", range.end().toString());
+ summary.put("warningCount", warningItems.size());
+ summary.put("overduePlanCount", overduePlanCount);
+ summary.put("overdueWorkOrderCount", overdueWorkOrderCount);
+ summary.put("pendingRepairCount", pendingRepairCount);
+ summary.put("qualityOpenCount", qualityOpenCount);
+ summary.put("lowStockCount", lowStockCount);
+ summary.put("exceptionCount", exceptionCount);
+
+ return jsonResponse(true, "manufacturing_warning", "宸茶繑鍥炲埗閫犻璀︾湅鏉裤��", summary,
+ Map.of("items", warningItems), Map.of());
+ }
+
+ @Tool(name = "鍒堕�犵粡钀ュ垎鏋�", value = "鎸夋椂闂磋寖鍥磋緭鍑哄埗閫犲叧閿寚鏍囷紝鏀寔鏌ャ�侀棶銆佸垎鏋愬満鏅��")
+ public String analyzeFactory(@ToolMemoryId String memoryId,
+ @P(value = "寮�濮嬫棩鏈� yyyy-MM-dd锛屽彲涓嶄紶", required = false) String startDate,
+ @P(value = "缁撴潫鏃ユ湡 yyyy-MM-dd锛屽彲涓嶄紶", required = false) String endDate,
+ @P(value = "鏃堕棿鑼冨洿鎻忚堪锛屼緥濡傛湰鏈堛�佽繎30澶�", required = false) String timeRange) {
+ LoginUser loginUser = currentLoginUser(memoryId);
+ DateRange range = resolveDateRange(startDate, endDate, timeRange);
+
+ long planTotal = countPlans(loginUser, range);
+ long planCompleted = countPlansByStatus(loginUser, range, 2);
+ long workOrderTotal = countWorkOrders(loginUser, range);
+ long workOrderCompleted = countWorkOrdersByStatus(loginUser, range, 2);
+ long workOrderInProgress = countWorkOrdersByStatus(loginUser, range, 1);
+
+ long outputCount = countOutputs(loginUser, range);
+ long deviceTotal = countDevices(loginUser);
+ long pendingRepairCount = countPendingRepairs(loginUser);
+ long qualityInspectTotal = countQualityInspect(loginUser, range);
+ long qualityNgCount = countOpenQualityIssues(loginUser, range);
+ long materialSkuCount = countInventorySku(loginUser);
+ long lowStockCount = countLowStock(loginUser);
+ long exceptionCount = countExceptionRecords(loginUser, range);
+
+ Map<String, Object> summary = new LinkedHashMap<>();
+ summary.put("timeRange", range.label());
+ summary.put("startDate", range.start().toString());
+ summary.put("endDate", range.end().toString());
+ summary.put("planTotal", planTotal);
+ summary.put("planCompleted", planCompleted);
+ summary.put("planCompletionRate", toRate(planCompleted, planTotal));
+ summary.put("workOrderTotal", workOrderTotal);
+ summary.put("workOrderCompleted", workOrderCompleted);
+ summary.put("workOrderInProgress", workOrderInProgress);
+ summary.put("workOrderCompletionRate", toRate(workOrderCompleted, workOrderTotal));
+ summary.put("outputCount", outputCount);
+ summary.put("deviceTotal", deviceTotal);
+ summary.put("pendingRepairCount", pendingRepairCount);
+ summary.put("qualityInspectTotal", qualityInspectTotal);
+ summary.put("qualityNgCount", qualityNgCount);
+ summary.put("qualityIssueRate", toRate(qualityNgCount, qualityInspectTotal));
+ summary.put("materialSkuCount", materialSkuCount);
+ summary.put("lowStockCount", lowStockCount);
+ summary.put("exceptionCount", exceptionCount);
+
+ List<Map<String, Object>> coreMetrics = List.of(
+ metric("璁″垝瀹屾垚鐜�", toRate(planCompleted, planTotal)),
+ metric("宸ュ崟瀹屾垚鐜�", toRate(workOrderCompleted, workOrderTotal)),
+ metric("璐ㄩ噺寮傚父鐜�", toRate(qualityNgCount, qualityInspectTotal)),
+ metric("浣庡簱瀛樺崰姣�", toRate(lowStockCount, materialSkuCount))
+ );
+
+ Map<String, Object> charts = new LinkedHashMap<>();
+ charts.put("domainBarOption", buildDomainBarOption(summary));
+ charts.put("qualityPieOption", buildQualityPieOption(qualityInspectTotal, qualityNgCount));
+
+ return jsonResponse(true, "manufacturing_analysis", "宸茶繑鍥炲埗閫犲垎鏋愮粨鏋溿��", summary,
+ Map.of("coreMetrics", coreMetrics), charts);
+ }
+
+ @Tool(name = "鐢熸垚鍒堕�犲姙鐞嗗缓璁�", value = "鏍规嵁鐢ㄦ埛闂杈撳嚭鍙墽琛岀殑鍔炵悊鍔ㄤ綔寤鸿锛屽寘鎷洰鏍囦笟鍔℃帴鍙c�佸繀濉瓧娈靛拰绀轰緥銆�")
+ public String planActions(@ToolMemoryId String memoryId,
+ @P("鐢ㄦ埛璇夋眰鍘熸枃") String userQuery) {
+ LoginUser loginUser = currentLoginUser(memoryId);
+ List<Map<String, Object>> actionCards = new ArrayList<>();
+
+ if (!StringUtils.hasText(userQuery) || containsAny(userQuery, "宸ュ崟", "娲惧伐", "浣滀笟")) {
+ actionCards.add(actionCard(
+ "workorder_assign",
+ "宸ュ崟娲惧伐",
+ "POST",
+ "/productionOperationTask/assign",
+ List.of("id", "userIds"),
+ Map.of("id", 10001, "userIds", "12,13"),
+ "灏嗗伐鍗曞垎閰嶇粰鎸囧畾浜哄憳锛岄�傜敤浜庣幇鍦鸿皟搴︺��"));
+ }
+ if (!StringUtils.hasText(userQuery) || containsAny(userQuery, "璁惧", "缁翠慨", "鏁呴殰")) {
+ actionCards.add(actionCard(
+ "device_repair_create",
+ "鍒涘缓璁惧缁翠慨鍗�",
+ "POST",
+ "/device/repair",
+ List.of("deviceLedgerId", "deviceName", "repairName", "remark"),
+ Map.of("deviceLedgerId", 1001, "deviceName", "绌哄帇鏈篈-01", "repairName", "寮犱笁", "remark", "寮傚搷骞朵即闅忔俯鍗�"),
+ "鏂板缓缁翠慨鍗曪紝杩涘叆璁惧寮傚父澶勭悊闂幆銆�"));
+ }
+ if (!StringUtils.hasText(userQuery) || containsAny(userQuery, "璐ㄩ噺", "涓嶅悎鏍�", "闂幆")) {
+ actionCards.add(actionCard(
+ "quality_unqualified_deal",
+ "澶勭悊涓嶅悎鏍煎崟",
+ "POST",
+ "/quality/qualityUnqualified/deal",
+ List.of("id", "dealResult", "dealName"),
+ Map.of("id", 3001, "dealResult", "杩斿伐鍚庡妫�", "dealName", "鏉庡洓"),
+ "瀵逛笉鍚堟牸璁板綍鎵ц澶勭疆骞堕棴鐜��"));
+ }
+ if (!StringUtils.hasText(userQuery) || containsAny(userQuery, "鐗╂枡", "搴撳瓨", "琛ユ枡")) {
+ actionCards.add(actionCard(
+ "material_inbound",
+ "琛ュ厖搴撳瓨",
+ "POST",
+ "/stockInventory/addstockInventory",
+ List.of("productModelId", "batchNo", "qualitity"),
+ Map.of("productModelId", 5001, "batchNo", "B2026051601", "qualitity", 120),
+ "褰撲綆搴撳瓨棰勮瑙﹀彂鏃讹紝澧炲姞搴撳瓨鏁伴噺銆�"));
+ }
+ if (!StringUtils.hasText(userQuery) || containsAny(userQuery, "寮傚父", "閲囪喘寮傚父", "鏉ユ枡寮傚父")) {
+ actionCards.add(actionCard(
+ "procurement_exception_add",
+ "鐧昏寮傚父璁板綍",
+ "POST",
+ "/procurementExceptionRecord/add",
+ List.of("purchaseLedgerId", "exceptionReason", "exceptionNum"),
+ Map.of("purchaseLedgerId", 888, "exceptionReason", "鍒版枡鐭己", "exceptionNum", 24),
+ "鐧昏閲囪喘/鏉ユ枡寮傚父锛屼究浜庡悗缁拷韪拰鍒嗘瀽銆�"));
+ }
+
+ Map<String, Object> summary = new LinkedHashMap<>();
+ summary.put("actionCount", actionCards.size());
+ summary.put("userId", loginUser.getUserId());
+ summary.put("tenantId", loginUser.getTenantId());
+
+ return jsonResponse(true, "manufacturing_action_plan", "宸茬敓鎴愬姙鐞嗗缓璁紝璇峰墠绔紩瀵肩敤鎴风‘璁ゅ悗璋冪敤鐩爣涓氬姟鎺ュ彛銆�",
+ summary, Map.of("actionCards", actionCards), Map.of());
+ }
+
+ private String siteSnapshot(LoginUser loginUser, DateRange range) {
+ long planTotal = countPlans(loginUser, range);
+ long workOrderTotal = countWorkOrders(loginUser, range);
+ long outputCount = countOutputs(loginUser, range);
+ long deviceTotal = countDevices(loginUser);
+ long pendingRepairCount = countPendingRepairs(loginUser);
+ long qualityOpenCount = countOpenQualityIssues(loginUser, range);
+ long lowStockCount = countLowStock(loginUser);
+ long exceptionCount = countExceptionRecords(loginUser, range);
+
+ Map<String, Object> summary = new LinkedHashMap<>();
+ summary.put("timeRange", range.label());
+ summary.put("startDate", range.start().toString());
+ summary.put("endDate", range.end().toString());
+ summary.put("planTotal", planTotal);
+ summary.put("workOrderTotal", workOrderTotal);
+ summary.put("outputCount", outputCount);
+ summary.put("deviceTotal", deviceTotal);
+ summary.put("pendingRepairCount", pendingRepairCount);
+ summary.put("qualityOpenCount", qualityOpenCount);
+ summary.put("lowStockCount", lowStockCount);
+ summary.put("exceptionCount", exceptionCount);
+
+ return jsonResponse(true, "manufacturing_site_snapshot", "宸茶繑鍥炵敓浜х幇鍦烘瑙堛��", summary, Map.of(), Map.of());
+ }
+
+ private String listProductionPlans(LoginUser loginUser, String keyword, int limit, DateRange range) {
+ LambdaQueryWrapper<ProductionPlan> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionPlan::getDeptId);
+ wrapper.ge(ProductionPlan::getRequiredDate, range.start()).le(ProductionPlan::getRequiredDate, range.end());
+ if (StringUtils.hasText(keyword)) {
+ wrapper.and(w -> w.like(ProductionPlan::getMpsNo, keyword)
+ .or().like(ProductionPlan::getRemark, keyword)
+ .or().like(ProductionPlan::getSource, keyword));
+ }
+ wrapper.orderByDesc(ProductionPlan::getRequiredDate, ProductionPlan::getId).last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(productionPlanMapper.selectList(wrapper)).stream()
+ .map(this::toPlanItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_plan_list", "宸茶繑鍥炵敓浜ц鍒掑垪琛ㄣ��",
+ rangeSummary(range, items.size(), keyword), Map.of("items", items), Map.of());
+ }
+
+ private String listWorkOrders(LoginUser loginUser, String keyword, int limit, DateRange range) {
+ LambdaQueryWrapper<ProductionOperationTask> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionOperationTask::getDeptId);
+ wrapper.ge(ProductionOperationTask::getPlanStartTime, range.start())
+ .le(ProductionOperationTask::getPlanEndTime, range.end());
+ if (StringUtils.hasText(keyword)) {
+ wrapper.and(w -> w.like(ProductionOperationTask::getWorkOrderNo, keyword)
+ .or().like(ProductionOperationTask::getUserIds, keyword));
+ }
+ wrapper.orderByDesc(ProductionOperationTask::getPlanEndTime, ProductionOperationTask::getId)
+ .last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(productionOperationTaskMapper.selectList(wrapper)).stream()
+ .map(this::toWorkOrderItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_workorder_list", "宸茶繑鍥炲伐鍗曞垪琛ㄣ��",
+ rangeSummary(range, items.size(), keyword), Map.of("items", items), Map.of());
+ }
+
+ private String listDevices(LoginUser loginUser, String keyword, int limit) {
+ LambdaQueryWrapper<DeviceLedger> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceLedger::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceLedger::getDeptId);
+ if (StringUtils.hasText(keyword)) {
+ wrapper.and(w -> w.like(DeviceLedger::getDeviceName, keyword)
+ .or().like(DeviceLedger::getDeviceModel, keyword)
+ .or().like(DeviceLedger::getDeviceBrand, keyword));
+ }
+ wrapper.orderByDesc(DeviceLedger::getId).last("limit " + limit);
+
+ Map<Long, Long> pendingRepairMap = pendingRepairCountByDevice(loginUser);
+ List<Map<String, Object>> items = defaultList(deviceLedgerMapper.selectList(wrapper)).stream()
+ .map(item -> toDeviceItem(item, pendingRepairMap.getOrDefault(item.getId(), 0L)))
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_device_list", "宸茶繑鍥炶澶囧垪琛ㄣ��",
+ Map.of("count", items.size(), "keyword", safe(keyword)), Map.of("items", items), Map.of());
+ }
+
+ private String listDeviceRepairs(LoginUser loginUser, String keyword, int limit, DateRange range, boolean hasTimeConstraint) {
+ LambdaQueryWrapper<DeviceRepair> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceRepair::getTenantId);
+ Long currentDeptId = loginUser.getCurrentDeptId();
+ if (currentDeptId != null) {
+ wrapper.and(w -> w.eq(DeviceRepair::getDeptId, currentDeptId).or().isNull(DeviceRepair::getDeptId));
+ }
+ if (hasTimeConstraint) {
+ wrapper.ge(DeviceRepair::getCreateTime, range.start().atStartOfDay())
+ .lt(DeviceRepair::getCreateTime, range.end().plusDays(1).atStartOfDay());
+ }
+ if (StringUtils.hasText(keyword)) {
+ List<Long> matchedDeviceIds = findDeviceLedgerIdsByKeyword(loginUser, keyword);
+ wrapper.and(w -> {
+ w.like(DeviceRepair::getDeviceName, keyword)
+ .or().like(DeviceRepair::getDeviceModel, keyword)
+ .or().like(DeviceRepair::getRemark, keyword)
+ .or().like(DeviceRepair::getRepairName, keyword)
+ .or().like(DeviceRepair::getMaintenanceName, keyword);
+ if (!matchedDeviceIds.isEmpty()) {
+ w.or().in(DeviceRepair::getDeviceLedgerId, matchedDeviceIds);
+ }
+ });
+ }
+ wrapper.orderByDesc(DeviceRepair::getCreateTime, DeviceRepair::getId).last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(deviceRepairMapper.selectList(wrapper)).stream()
+ .map(this::toDeviceRepairItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_device_repair_list", "宸茶繑鍥炶澶囩淮淇褰曘��",
+ rangeSummary(range, items.size(), keyword), Map.of("items", items), Map.of());
+ }
+
+ private String listQualityIssues(LoginUser loginUser, String keyword, int limit, DateRange range) {
+ LambdaQueryWrapper<QualityUnqualified> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), QualityUnqualified::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), QualityUnqualified::getDeptId);
+ wrapper.ge(QualityUnqualified::getCheckTime, toDate(range.start()))
+ .lt(QualityUnqualified::getCheckTime, toExclusiveEndDate(range.end()));
+ if (StringUtils.hasText(keyword)) {
+ wrapper.and(w -> w.like(QualityUnqualified::getProductName, keyword)
+ .or().like(QualityUnqualified::getDefectivePhenomena, keyword)
+ .or().like(QualityUnqualified::getDealResult, keyword));
+ }
+ wrapper.orderByDesc(QualityUnqualified::getCheckTime, QualityUnqualified::getId).last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(qualityUnqualifiedMapper.selectList(wrapper)).stream()
+ .map(this::toQualityItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_quality_list", "宸茶繑鍥炶川閲忓紓甯稿垪琛ㄣ��",
+ rangeSummary(range, items.size(), keyword), Map.of("items", items), Map.of());
+ }
+
+ private String listMaterialInventory(LoginUser loginUser, String keyword, int limit) {
+ LambdaQueryWrapper<StockInventory> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), StockInventory::getDeptId);
+ if (StringUtils.hasText(keyword)) {
+ wrapper.and(w -> w.like(StockInventory::getBatchNo, keyword)
+ .or().like(StockInventory::getProductModelId, keyword));
+ }
+ wrapper.orderByDesc(StockInventory::getId).last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(stockInventoryMapper.selectList(wrapper)).stream()
+ .map(this::toMaterialItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_material_list", "宸茶繑鍥炵墿鏂欏簱瀛樺垪琛ㄣ��",
+ Map.of("count", items.size(), "keyword", safe(keyword)), Map.of("items", items), Map.of());
+ }
+
+ private String listExceptions(LoginUser loginUser, String keyword, int limit, DateRange range) {
+ LambdaQueryWrapper<ProcurementExceptionRecord> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), ProcurementExceptionRecord::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProcurementExceptionRecord::getDeptId);
+ wrapper.ge(ProcurementExceptionRecord::getCreateTime, range.start().atStartOfDay())
+ .lt(ProcurementExceptionRecord::getCreateTime, range.end().plusDays(1).atStartOfDay());
+ if (StringUtils.hasText(keyword)) {
+ wrapper.like(ProcurementExceptionRecord::getExceptionReason, keyword);
+ }
+ wrapper.orderByDesc(ProcurementExceptionRecord::getCreateTime, ProcurementExceptionRecord::getId)
+ .last("limit " + limit);
+
+ List<Map<String, Object>> items = defaultList(procurementExceptionRecordMapper.selectList(wrapper)).stream()
+ .map(this::toExceptionItem)
+ .collect(Collectors.toList());
+ return jsonResponse(true, "manufacturing_exception_list", "宸茶繑鍥炲紓甯稿鐞嗗垪琛ㄣ��",
+ rangeSummary(range, items.size(), keyword), Map.of("items", items), Map.of());
+ }
+
+ private long countPlans(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<ProductionPlan> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionPlan::getDeptId);
+ wrapper.ge(ProductionPlan::getRequiredDate, range.start()).le(ProductionPlan::getRequiredDate, range.end());
+ return productionPlanMapper.selectCount(wrapper);
+ }
+
+ private long countPlansByStatus(LoginUser loginUser, DateRange range, int status) {
+ LambdaQueryWrapper<ProductionPlan> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionPlan::getDeptId);
+ wrapper.ge(ProductionPlan::getRequiredDate, range.start())
+ .le(ProductionPlan::getRequiredDate, range.end())
+ .eq(ProductionPlan::getStatus, status);
+ return productionPlanMapper.selectCount(wrapper);
+ }
+
+ private long countWorkOrders(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<ProductionOperationTask> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionOperationTask::getDeptId);
+ wrapper.ge(ProductionOperationTask::getPlanStartTime, range.start())
+ .le(ProductionOperationTask::getPlanEndTime, range.end());
+ return productionOperationTaskMapper.selectCount(wrapper);
+ }
+
+ private long countWorkOrdersByStatus(LoginUser loginUser, DateRange range, int status) {
+ LambdaQueryWrapper<ProductionOperationTask> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionOperationTask::getDeptId);
+ wrapper.ge(ProductionOperationTask::getPlanStartTime, range.start())
+ .le(ProductionOperationTask::getPlanEndTime, range.end())
+ .eq(ProductionOperationTask::getStatus, status);
+ return productionOperationTaskMapper.selectCount(wrapper);
+ }
+
+ private long countOutputs(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<ProductionProductMain> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionProductMain::getDeptId);
+ wrapper.ge(ProductionProductMain::getCreateTime, range.start().atStartOfDay())
+ .lt(ProductionProductMain::getCreateTime, range.end().plusDays(1).atStartOfDay());
+ return productionProductMainMapper.selectCount(wrapper);
+ }
+
+ private long countDevices(LoginUser loginUser) {
+ LambdaQueryWrapper<DeviceLedger> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceLedger::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceLedger::getDeptId);
+ return deviceLedgerMapper.selectCount(wrapper);
+ }
+
+ private long countPendingRepairs(LoginUser loginUser) {
+ LambdaQueryWrapper<DeviceRepair> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceRepair::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceRepair::getDeptId);
+ wrapper.eq(DeviceRepair::getStatus, DEVICE_REPAIR_STATUS_PENDING);
+ return deviceRepairMapper.selectCount(wrapper);
+ }
+
+ private long countQualityInspect(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<QualityInspect> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), QualityInspect::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), QualityInspect::getDeptId);
+ wrapper.ge(QualityInspect::getCheckTime, toDate(range.start()))
+ .lt(QualityInspect::getCheckTime, toExclusiveEndDate(range.end()));
+ return qualityInspectMapper.selectCount(wrapper);
+ }
+
+ private long countOpenQualityIssues(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<QualityUnqualified> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), QualityUnqualified::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), QualityUnqualified::getDeptId);
+ wrapper.ge(QualityUnqualified::getCheckTime, toDate(range.start()))
+ .lt(QualityUnqualified::getCheckTime, toExclusiveEndDate(range.end()))
+ .ne(QualityUnqualified::getInspectState, 2);
+ return qualityUnqualifiedMapper.selectCount(wrapper);
+ }
+
+ private long countInventorySku(LoginUser loginUser) {
+ LambdaQueryWrapper<StockInventory> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), StockInventory::getDeptId);
+ return stockInventoryMapper.selectCount(wrapper);
+ }
+
+ private long countLowStock(LoginUser loginUser) {
+ LambdaQueryWrapper<StockInventory> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), StockInventory::getDeptId);
+ wrapper.isNotNull(StockInventory::getWarnNum);
+ List<StockInventory> stocks = defaultList(stockInventoryMapper.selectList(wrapper));
+ return stocks.stream()
+ .filter(this::isLowStock)
+ .count();
+ }
+
+ private long countExceptionRecords(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<ProcurementExceptionRecord> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), ProcurementExceptionRecord::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProcurementExceptionRecord::getDeptId);
+ wrapper.ge(ProcurementExceptionRecord::getCreateTime, range.start().atStartOfDay())
+ .lt(ProcurementExceptionRecord::getCreateTime, range.end().plusDays(1).atStartOfDay());
+ return procurementExceptionRecordMapper.selectCount(wrapper);
+ }
+
+ private long countOverduePlans(LoginUser loginUser, LocalDate today) {
+ LambdaQueryWrapper<ProductionPlan> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionPlan::getDeptId);
+ wrapper.lt(ProductionPlan::getRequiredDate, today).ne(ProductionPlan::getStatus, 2);
+ return productionPlanMapper.selectCount(wrapper);
+ }
+
+ private long countOverdueWorkOrders(LoginUser loginUser, LocalDate today) {
+ LambdaQueryWrapper<ProductionOperationTask> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProductionOperationTask::getDeptId);
+ wrapper.lt(ProductionOperationTask::getPlanEndTime, today).ne(ProductionOperationTask::getStatus, 2);
+ return productionOperationTaskMapper.selectCount(wrapper);
+ }
+
+ private Map<Long, Long> pendingRepairCountByDevice(LoginUser loginUser) {
+ LambdaQueryWrapper<DeviceRepair> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceRepair::getTenantId);
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceRepair::getDeptId);
+ wrapper.eq(DeviceRepair::getStatus, DEVICE_REPAIR_STATUS_PENDING);
+ return defaultList(deviceRepairMapper.selectList(wrapper)).stream()
+ .filter(item -> item.getDeviceLedgerId() != null)
+ .collect(Collectors.groupingBy(DeviceRepair::getDeviceLedgerId, Collectors.counting()));
+ }
+
+ private Map<String, Object> toPlanItem(ProductionPlan item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("mpsNo", safe(item.getMpsNo()));
+ map.put("requiredDate", formatDate(item.getRequiredDate()));
+ map.put("promisedDeliveryDate", formatDate(item.getPromisedDeliveryDate()));
+ map.put("qtyRequired", item.getQtyRequired());
+ map.put("quantityIssued", item.getQuantityIssued());
+ map.put("status", item.getStatus());
+ map.put("source", safe(item.getSource()));
+ map.put("remark", safe(item.getRemark()));
+ return map;
+ }
+
+ private Map<String, Object> toWorkOrderItem(ProductionOperationTask item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("workOrderNo", safe(item.getWorkOrderNo()));
+ map.put("productionOrderId", item.getProductionOrderId());
+ map.put("planStartTime", formatDate(item.getPlanStartTime()));
+ map.put("planEndTime", formatDate(item.getPlanEndTime()));
+ map.put("actualStartTime", formatDate(item.getActualStartTime()));
+ map.put("actualEndTime", formatDate(item.getActualEndTime()));
+ map.put("planQuantity", item.getPlanQuantity());
+ map.put("completeQuantity", item.getCompleteQuantity());
+ map.put("status", item.getStatus());
+ map.put("userIds", safe(item.getUserIds()));
+ return map;
+ }
+
+ private Map<String, Object> toDeviceItem(DeviceLedger item, long pendingRepairCount) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("deviceName", safe(item.getDeviceName()));
+ map.put("deviceModel", safe(item.getDeviceModel()));
+ map.put("deviceBrand", safe(item.getDeviceBrand()));
+ map.put("status", safe(item.getStatus()));
+ map.put("storageLocation", safe(item.getStorageLocation()));
+ map.put("supplierName", safe(item.getSupplierName()));
+ map.put("pendingRepairCount", pendingRepairCount);
+ return map;
+ }
+
+ private Map<String, Object> toDeviceRepairItem(DeviceRepair item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("deviceLedgerId", item.getDeviceLedgerId());
+ map.put("deviceName", safe(item.getDeviceName()));
+ map.put("deviceModel", safe(item.getDeviceModel()));
+ map.put("repairTime", formatDate(item.getRepairTime()));
+ map.put("repairName", safe(item.getRepairName()));
+ map.put("maintenanceName", safe(item.getMaintenanceName()));
+ map.put("maintenanceTime", formatDateTime(item.getMaintenanceTime()));
+ map.put("maintenanceResult", safe(item.getMaintenanceResult()));
+ map.put("acceptanceName", safe(item.getAcceptanceName()));
+ map.put("acceptanceTime", formatDateTime(item.getAcceptanceTime()));
+ map.put("status", item.getStatus());
+ map.put("remark", safe(item.getRemark()));
+ map.put("createTime", formatDateTime(item.getCreateTime()));
+ return map;
+ }
+
+ private Map<String, Object> toQualityItem(QualityUnqualified item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("checkTime", formatDate(item.getCheckTime()));
+ map.put("inspectState", item.getInspectState());
+ map.put("productId", item.getProductId());
+ map.put("productName", safe(item.getProductName()));
+ map.put("model", safe(item.getModel()));
+ map.put("quantity", item.getQuantity());
+ map.put("defectivePhenomena", safe(item.getDefectivePhenomena()));
+ map.put("dealResult", safe(item.getDealResult()));
+ map.put("dealName", safe(item.getDealName()));
+ map.put("dealTime", formatDate(item.getDealTime()));
+ return map;
+ }
+
+ private Map<String, Object> toMaterialItem(StockInventory item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("productModelId", item.getProductModelId());
+ map.put("batchNo", safe(item.getBatchNo()));
+ map.put("qualitity", item.getQualitity());
+ map.put("lockedQuantity", item.getLockedQuantity());
+ map.put("warnNum", item.getWarnNum());
+ map.put("lowStock", isLowStock(item));
+ map.put("remark", safe(item.getRemark()));
+ return map;
+ }
+
+ private Map<String, Object> toExceptionItem(ProcurementExceptionRecord item) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", item.getId());
+ map.put("purchaseLedgerId", item.getPurchaseLedgerId());
+ map.put("exceptionReason", safe(item.getExceptionReason()));
+ map.put("exceptionNum", item.getExceptionNum());
+ map.put("createTime", formatDateTime(item.getCreateTime()));
+ return map;
+ }
+
+ private boolean isLowStock(StockInventory item) {
+ BigDecimal quantity = item.getQualitity();
+ BigDecimal warnNum = item.getWarnNum();
+ if (quantity == null || warnNum == null) {
+ return false;
+ }
+ return quantity.compareTo(warnNum) <= 0;
+ }
+
+ private Map<String, Object> warningItem(String level, String title, long count, String detail) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("level", level);
+ map.put("title", title);
+ map.put("count", count);
+ map.put("detail", detail);
+ return map;
+ }
+
+ private Map<String, Object> actionCard(String code,
+ String name,
+ String method,
+ String targetApi,
+ List<String> requiredFields,
+ Map<String, Object> examplePayload,
+ String description) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("code", code);
+ map.put("name", name);
+ map.put("method", method);
+ map.put("targetApi", targetApi);
+ map.put("requiredFields", requiredFields);
+ map.put("examplePayload", examplePayload);
+ map.put("description", description);
+ return map;
+ }
+
+ private Map<String, Object> metric(String label, String value) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("label", label);
+ map.put("value", value);
+ return map;
+ }
+
+ private Map<String, Object> rangeSummary(DateRange range, int count, String keyword) {
+ Map<String, Object> summary = new LinkedHashMap<>();
+ summary.put("timeRange", range.label());
+ summary.put("startDate", range.start().toString());
+ summary.put("endDate", range.end().toString());
+ summary.put("count", count);
+ summary.put("keyword", safe(keyword));
+ return summary;
+ }
+
+ private Map<String, Object> buildDomainBarOption(Map<String, Object> summary) {
+ List<String> xData = List.of("璁″垝", "宸ュ崟", "璁惧", "璐ㄩ噺", "鐗╂枡", "寮傚父");
+ List<Number> yData = List.of(
+ numberValue(summary.get("planTotal")),
+ numberValue(summary.get("workOrderTotal")),
+ numberValue(summary.get("deviceTotal")),
+ numberValue(summary.get("qualityNgCount")),
+ numberValue(summary.get("lowStockCount")),
+ numberValue(summary.get("exceptionCount"))
+ );
+ Map<String, Object> option = new LinkedHashMap<>();
+ option.put("title", Map.of("text", "鍒堕�犲煙鍏抽敭鏁伴噺", "left", "center"));
+ option.put("tooltip", Map.of("trigger", "axis"));
+ option.put("xAxis", Map.of("type", "category", "data", xData));
+ option.put("yAxis", Map.of("type", "value"));
+ option.put("series", List.of(Map.of("name", "鏁伴噺", "type", "bar", "data", yData)));
+ return option;
+ }
+
+ private Map<String, Object> buildQualityPieOption(long inspectTotal, long ngCount) {
+ long passCount = Math.max(inspectTotal - ngCount, 0);
+ List<Map<String, Object>> data = List.of(
+ Map.of("name", "涓嶅悎鏍�", "value", ngCount),
+ Map.of("name", "闈炰笉鍚堟牸", "value", passCount)
+ );
+ Map<String, Object> option = new LinkedHashMap<>();
+ option.put("title", Map.of("text", "璐ㄩ噺缁撴灉鍒嗗竷", "left", "center"));
+ option.put("tooltip", Map.of("trigger", "item"));
+ option.put("series", List.of(Map.of("name", "璐ㄩ噺", "type", "pie", "radius", "60%", "data", data)));
+ return option;
+ }
+
+ private int numberValue(Object value) {
+ if (value instanceof Number number) {
+ return number.intValue();
+ }
+ return 0;
+ }
+
+ private String toRate(long numerator, long denominator) {
+ if (denominator <= 0) {
+ return "0.00%";
+ }
+ BigDecimal rate = new BigDecimal(numerator)
+ .multiply(new BigDecimal("100"))
+ .divide(new BigDecimal(denominator), 2, RoundingMode.HALF_UP);
+ return rate.toPlainString() + "%";
+ }
+
+ private String normalizeDomain(String domain) {
+ if (!StringUtils.hasText(domain)) {
+ return "";
+ }
+ String value = domain.trim().toLowerCase();
+ return switch (value) {
+ case "鐢熶骇鐜板満", "site", "factory", "workshop" -> "site";
+ case "璁″垝", "plan", "schedule" -> "plan";
+ case "宸ュ崟", "workorder", "work_order", "task" -> "workorder";
+ case "璁惧", "device", "equipment" -> "device";
+ case "缁翠慨", "repair", "maintenance" -> "repair";
+ case "璐ㄩ噺", "quality", "qc" -> "quality";
+ case "鐗╂枡", "material", "inventory", "stock" -> "material";
+ case "寮傚父", "exception", "abnormal" -> "exception";
+ default -> value;
+ };
+ }
+
+ private boolean isRepairIntent(String keyword, String userQuery) {
+ String query = safe(userQuery);
+ return containsAny(safe(keyword), "缁翠慨", "鎶ヤ慨", "妫�淇�", "缁存姢")
+ || containsAny(query, "缁翠慨", "鎶ヤ慨", "妫�淇�", "缁存姢");
+ }
+
+ private String normalizeDeviceQueryKeyword(String keyword, String userQuery) {
+ String source = StringUtils.hasText(keyword) ? keyword : userQuery;
+ if (!StringUtils.hasText(source)) {
+ return null;
+ }
+ String cleaned = source
+ .replace("鏌ヨ", "")
+ .replace("鏌ョ湅", "")
+ .replace("甯垜", "")
+ .replace("璇�", "")
+ .replace("鏌�", "")
+ .replace("璁惧", "")
+ .replace("缁翠慨璁板綍", "")
+ .replace("缁翠慨鎯呭喌", "")
+ .replace("鎶ヤ慨璁板綍", "")
+ .replace("鎶ヤ慨鎯呭喌", "")
+ .replace("缁翠慨", "")
+ .replace("鎶ヤ慨", "")
+ .replace("鎯呭喌", "")
+ .replace("璁板綍", "")
+ .replace("淇℃伅", "")
+ .replace("鐨�", "")
+ .replace("涓�涓�", "")
+ .trim();
+ return cleaned.length() >= 2 ? cleaned : null;
+ }
+
+ private List<Long> findDeviceLedgerIdsByKeyword(LoginUser loginUser, String keyword) {
+ if (!StringUtils.hasText(keyword)) {
+ return List.of();
+ }
+ LambdaQueryWrapper<DeviceLedger> wrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceLedger::getTenantId);
+ Long currentDeptId = loginUser.getCurrentDeptId();
+ if (currentDeptId != null) {
+ wrapper.and(w -> w.eq(DeviceLedger::getDeptId, currentDeptId).or().isNull(DeviceLedger::getDeptId));
+ }
+ wrapper.and(w -> w.like(DeviceLedger::getDeviceName, keyword)
+ .or().like(DeviceLedger::getDeviceModel, keyword)
+ .or().like(DeviceLedger::getDeviceBrand, keyword));
+ wrapper.orderByDesc(DeviceLedger::getId).last("limit 200");
+ return defaultList(deviceLedgerMapper.selectList(wrapper)).stream()
+ .map(DeviceLedger::getId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ private boolean hasTimeConstraint(String startDate, String endDate, String userQuery) {
+ if (StringUtils.hasText(startDate) || StringUtils.hasText(endDate)) {
+ return true;
+ }
+ if (!StringUtils.hasText(userQuery)) {
+ return false;
+ }
+ String text = userQuery.trim();
+ return containsAny(text, "浠婂ぉ", "鏄ㄥぉ", "鏈懆", "涓婂懆", "鏈湀", "涓婃湀", "浠婂勾", "鍘诲勾", "杩�", "鏈�杩�");
+ }
+
+ private DateRange resolveDateRange(String startDate, String endDate, String timeRange) {
+ LocalDate today = LocalDate.now();
+ LocalDate start = parseLocalDate(startDate);
+ LocalDate end = parseLocalDate(endDate);
+ if (start != null || end != null) {
+ LocalDate s = start != null ? start : end;
+ LocalDate e = end != null ? end : start;
+ if (s.isAfter(e)) {
+ LocalDate temp = s;
+ s = e;
+ e = temp;
+ }
+ return new DateRange(s, e, s + "鑷�" + e);
+ }
+ if (!StringUtils.hasText(timeRange)) {
+ return new DateRange(today.minusDays(29), today, "杩�30澶�");
+ }
+ String text = timeRange.trim();
+ if (text.contains("浠婂ぉ")) {
+ return new DateRange(today, today, "浠婂ぉ");
+ }
+ if (text.contains("鏈懆")) {
+ LocalDate startOfWeek = today.minusDays(today.getDayOfWeek().getValue() - 1L);
+ return new DateRange(startOfWeek, today, "鏈懆");
+ }
+ if (text.contains("鏈湀")) {
+ return new DateRange(today.withDayOfMonth(1), today, "鏈湀");
+ }
+ if (text.contains("鏈勾") || text.contains("浠婂勾")) {
+ return new DateRange(today.withDayOfYear(1), today, "浠婂勾");
+ }
+ if (text.contains("鍘诲勾")) {
+ LocalDate firstDay = today.minusYears(1).withDayOfYear(1);
+ LocalDate lastDay = today.minusYears(1).withMonth(12).withDayOfMonth(31);
+ return new DateRange(firstDay, lastDay, "鍘诲勾");
+ }
+ if (text.contains("涓婃湀")) {
+ LocalDate startOfLastMonth = today.minusMonths(1).withDayOfMonth(1);
+ return new DateRange(startOfLastMonth, startOfLastMonth.withDayOfMonth(startOfLastMonth.lengthOfMonth()), "涓婃湀");
+ }
+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(杩憒鏈�杩�)(\\d+)(澶﹟鍛▅涓湀|鏈坾骞�)").matcher(text);
+ if (matcher.find()) {
+ int amount = Integer.parseInt(matcher.group(2));
+ String unit = matcher.group(3);
+ LocalDate relativeStart = switch (unit) {
+ case "澶�" -> today.minusDays(Math.max(amount - 1L, 0));
+ case "鍛�" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1);
+ case "涓湀", "鏈�" -> today.minusMonths(Math.max(amount, 1)).plusDays(1);
+ case "骞�" -> today.minusYears(Math.max(amount, 1)).plusDays(1);
+ default -> today.minusDays(29);
+ };
+ return new DateRange(relativeStart, today, "杩�" + amount + unit);
+ }
+ return new DateRange(today.minusDays(29), today, "杩�30澶�");
+ }
+
+ private LocalDate parseLocalDate(String text) {
+ if (!StringUtils.hasText(text)) {
+ return null;
+ }
+ return LocalDate.parse(text.trim(), DATE_FMT);
+ }
+
+ private Date toDate(LocalDate date) {
+ return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
+ }
+
+ private Date toExclusiveEndDate(LocalDate date) {
+ return Date.from(date.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
+ }
+
+ private String formatDate(LocalDate date) {
+ return date == null ? "" : DATE_FMT.format(date);
+ }
+
+ private String formatDate(Date date) {
+ if (date == null) {
+ return "";
+ }
+ return DATE_FMT.format(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
+ }
+
+ private String formatDateTime(LocalDateTime time) {
+ if (time == null) {
+ return "";
+ }
+ return time.truncatedTo(ChronoUnit.SECONDS).toString().replace('T', ' ');
+ }
+
+ private int normalizeLimit(Integer limit) {
+ if (limit == null || limit <= 0) {
+ return DEFAULT_LIMIT;
+ }
+ return Math.min(limit, MAX_LIMIT);
+ }
+
+ private boolean containsAny(String text, String... values) {
+ for (String value : values) {
+ if (text.contains(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private <T> void applyTenantFilter(LambdaQueryWrapper<T> wrapper, Long tenantId, SFunction<T, Long> field) {
+ if (tenantId != null) {
+ wrapper.eq(field, tenantId);
+ }
+ }
+
+ private <T> void applyDeptFilter(LambdaQueryWrapper<T> wrapper, Long deptId, SFunction<T, Long> field) {
+ if (deptId != null) {
+ wrapper.eq(field, deptId);
+ }
+ }
+
+ private LoginUser currentLoginUser(String memoryId) {
+ LoginUser loginUser = aiSessionUserContext.get(memoryId);
+ if (loginUser != null) {
+ return loginUser;
+ }
+ return SecurityUtils.getLoginUser();
+ }
+
+ private String safe(Object value) {
+ return value == null ? "" : String.valueOf(value).replace('\n', ' ').replace('\r', ' ');
+ }
+
+ private <T> List<T> defaultList(List<T> list) {
+ return list == null ? List.of() : list;
+ }
+
+ private String jsonResponse(boolean success,
+ String type,
+ String description,
+ Map<String, Object> summary,
+ Map<String, Object> data,
+ Map<String, Object> charts) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("success", success);
+ result.put("type", type);
+ result.put("description", description);
+ result.put("summary", summary == null ? Map.of() : summary);
+ result.put("data", data == null ? Map.of() : data);
+ result.put("charts", charts == null ? Map.of() : charts);
+ return JSON.toJSONString(result);
+ }
+
+ private record DateRange(LocalDate start, LocalDate end, String label) {
+ }
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java b/src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java
index 56d420e..8c5db04 100644
--- a/src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java
+++ b/src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java
@@ -75,5 +75,5 @@
*/
private BigDecimal maintenancePrice;
- private List<StorageBlobDTO> storageBlobDTOList;
+ private List<StorageBlobDTO> storageBlobDTOS;
}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
index 34e1aed..0b1a854 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
@@ -216,9 +216,12 @@
if (shippingInfo != null) {
if (status.equals(2)) {
shippingInfo.setStatus("瀹℃牳閫氳繃");
+ shippingInfo.setShippingDate(new Date());
//鏇存敼鍑哄簱瀹℃牳鐘舵�侊紙寰呯‘璁ゆ敼鎴愬緟瀹℃牳锛�
stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
} else if (status.equals(3)) {
+ //鍒犻櫎鍘熸湰锛堝緟纭锛夌殑鍑哄簱瀹℃牳鐘舵��
+ stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
shippingInfo.setStatus("瀹℃牳鎷掔粷");
} else if (status.equals(1)) {
shippingInfo.setStatus("瀹℃牳涓�");
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
index bf2cdb2..c9d7aae 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -138,7 +138,7 @@
.collect(Collectors.joining(","));
approveNodeService.initApproveNodes(nodeIdStr, no, approveProcessVO.getApproveDeptId());
// 闄勪欢缁戝畾
- fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_PROCESS, approveProcess.getId(), approveProcessVO.getStorageBlobDTOList());
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_PROCESS, approveProcess.getId(), approveProcessVO.getStorageBlobDTOS());
/*娑堟伅閫氱煡*/
Long id = nodeIds.getFirst();
if (approveProcess.getApproveType() == 8) {
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java b/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
index 4a88508..15e428d 100644
--- a/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
+++ b/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
@@ -3,9 +3,11 @@
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.production.bean.dto.ProductStructureDto;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import java.util.List;
+@EqualsAndHashCode(callSuper = true)
@Data
public class ProductModelDto extends ProductModel {
private List<ProductStructureDto> productStructureList;
diff --git a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
index 9c63efb..ef7e57c 100644
--- a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -204,6 +204,7 @@
SALES_REFUND_AMOUNT_ORDER("sales_refund_amount_order"),
SALES_RECEIPT_RETURN("sales_receipt_return"),
ACCOUNT_EXPENSE("account_expense"),
+ FIN_VOUCHER("fin_voucher"),
ACCOUNT_FILE("account_file");
private final String type;
diff --git a/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java b/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
index 36876d0..b245741 100644
--- a/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
+++ b/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
@@ -5,7 +5,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.vo.ProductModelVo;
-import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto;
import org.apache.ibatis.annotations.Param;
import java.util.List;
diff --git a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
index f0e9470..8c0dc07 100644
--- a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
+++ b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
@@ -38,7 +38,6 @@
private String model;
@Excel(name = "浜у搧缂栫爜")
- @TableField("product_code")
private String productCode;
/**
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
index 85b542a..be34fc0 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
@@ -18,8 +18,11 @@
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.sales.dto.LossProductModelDto;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.technology.mapper.TechnologyBomMapper;
+import com.ruoyi.technology.pojo.TechnologyBom;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -43,10 +46,16 @@
private final ProductMapper productMapper;
private final SalesLedgerProductMapper salesLedgerProductMapper;
+ private final TechnologyBomMapper technologyBomMapper;
private ProductModelMapper productModelMapper;
@Override
public int addOrEditProductModel(ProductModelDto productModelDto) {
+ String model = StringUtils.trim(productModelDto.getModel());
+ String productCode = StringUtils.trim(productModelDto.getProductCode());
+ productModelDto.setModel(model);
+ productModelDto.setProductCode(productCode);
+ checkModelAndProductCodeUnique(model, productCode, productModelDto.getId());
if (productModelDto.getId() == null) {
ProductModel productModel = new ProductModel();
@@ -54,6 +63,21 @@
return productModelMapper.insert(productModel);
} else {
return productModelMapper.updateById(productModelDto);
+ }
+ }
+
+ private void checkModelAndProductCodeUnique(String model, String productCode, Long currentId) {
+ if (StringUtils.isEmpty(model) || StringUtils.isEmpty(productCode)) {
+ return;
+ }
+ LambdaQueryWrapper<ProductModel> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(ProductModel::getModel, model)
+ .eq(ProductModel::getProductCode, productCode)
+ .ne(currentId != null, ProductModel::getId, currentId)
+ .last("limit 1");
+ ProductModel duplicateProductModel = productModelMapper.selectOne(queryWrapper);
+ if (duplicateProductModel != null) {
+ throw new ServiceException("瀵瑰簲鐨勫瀷鍙�" + model + "鐨勪骇鍝佺紪鐮�" + productCode + "宸茬粡瀛樺湪");
}
}
@@ -66,6 +90,14 @@
throw new RuntimeException("宸茬粡瀛樺湪璇ヤ骇鍝佺殑閿�鍞彴璐﹀拰閲囪喘鍙拌处");
}
+
+ // 鏄惁瀛樺湪BOM
+ List<TechnologyBom> technologyBoms = technologyBomMapper.selectList(new QueryWrapper<TechnologyBom>()
+ .lambda().in(TechnologyBom::getProductModelId, ids));
+ if (CollectionUtils.isNotEmpty(technologyBoms)) {
+ throw new RuntimeException("宸茬粡瀛樺湪璇ヤ骇鍝佺殑BOM鏁版嵁");
+ }
+
return productModelMapper.deleteBatchIds(Arrays.asList(ids));
}
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
index 5a7f679..2b9eb40 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
@@ -13,12 +13,10 @@
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.service.IProductService;
import com.ruoyi.basic.vo.ProductModelVo;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
-import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.framework.web.domain.AjaxResult;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
-import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Arrays;
@@ -94,17 +92,18 @@
if (ObjectUtils.isEmpty(productDto.getParentId())) {
throw new IllegalArgumentException("璇烽�夋嫨鐖惰妭鐐�");
}
+ String productName = StringUtils.trim(productDto.getProductName());
+ if (StringUtils.isEmpty(productName)) {
+ throw new IllegalArgumentException("浜у搧鍚嶇О涓嶈兘涓虹┖");
+ }
+ productDto.setProductName(productName);
+ checkProductNameUnique(productDto.getParentId(), productName, productDto.getId());
if (productDto.getId() == null) {
// 鏂板浜у搧閫昏緫
- if (productDto.getParentId() == null) {
- // 鑻ユ湭鎸囧畾鐖惰妭鐐癸紝榛樿涓烘牴鑺傜偣锛坧arentId 璁句负 null锛�
- productDto.setParentId(null);
- } else {
- // 妫�鏌ョ埗鑺傜偣鏄惁瀛樺湪锛堝彲閫夛紝鏍规嵁涓氬姟闇�姹傦級
- Product parent = productMapper.selectById(productDto.getParentId());
- if (parent == null) {
- throw new IllegalArgumentException("鐖惰妭鐐逛笉瀛樺湪锛屾棤娉曟坊鍔犲瓙浜у搧");
- }
+ // 妫�鏌ョ埗鑺傜偣鏄惁瀛樺湪锛堝彲閫夛紝鏍规嵁涓氬姟闇�姹傦級
+ Product parent = productMapper.selectById(productDto.getParentId());
+ if (parent == null) {
+ throw new IllegalArgumentException("鐖惰妭鐐逛笉瀛樺湪锛屾棤娉曟坊鍔犲瓙浜у搧");
}
return productMapper.insert(productDto);
} else {
@@ -118,6 +117,18 @@
}
}
+ private void checkProductNameUnique(Long parentId, String productName, Long currentId) {
+ LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(Product::getParentId, parentId)
+ .eq(Product::getProductName, productName)
+ .ne(currentId != null, Product::getId, currentId)
+ .last("limit 1");
+ Product duplicateProduct = productMapper.selectOne(queryWrapper);
+ if (duplicateProduct != null) {
+ throw new IllegalArgumentException("瀵瑰簲鐨�" + productName + "宸茬粡瀛樺湪");
+ }
+ }
+
@Override
public int delProductByIds(Long[] ids) {
// 1. 鍒犻櫎瀛愯〃 product_model 涓叧鑱旂殑鏁版嵁
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java
index 6a3824d..dc6a64e 100644
--- a/src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.approve.pojo.KnowledgeBase;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.collaborativeApproval.dto.SealApplicationManagementDTO;
import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement;
import com.ruoyi.collaborativeApproval.service.SealApplicationManagementService;
import com.ruoyi.common.utils.SecurityUtils;
@@ -27,6 +31,7 @@
public class SealApplicationManagementController {
private SealApplicationManagementService sealApplicationManagementService;
private ISysNoticeService sysNoticeService;
+ private FileUtil fileUtil;
@GetMapping("/getList")
@Operation(summary = "鍒嗛〉鏌ヨ")
@@ -36,8 +41,13 @@
@PostMapping("/add")
@Operation(summary = "鏂板")
- public AjaxResult add(@RequestBody SealApplicationManagement sealApplicationManagement){
+ public AjaxResult add(@RequestBody SealApplicationManagementDTO sealApplicationManagement){
sealApplicationManagementService.save(sealApplicationManagement);
+ // 5. 淇濆瓨閿�鍞彴璐﹂檮浠�
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE,
+ RecordTypeEnum.SEAL_APPLICATION_MANAGEMENT,
+ sealApplicationManagement.getId(),
+ sealApplicationManagement.getStorageBlobDTOs());
//娑堟伅閫氱煡
sysNoticeService.simpleNoticeByUser("鐢ㄥ嵃瀹℃壒",
"鐢宠缂栧彿锛�"+sealApplicationManagement.getApplicationNum()+"\t"
@@ -49,7 +59,12 @@
@PostMapping("/update")
@Operation(summary = "淇敼")
- public AjaxResult update(@RequestBody SealApplicationManagement sealApplicationManagement){
+ public AjaxResult update(@RequestBody SealApplicationManagementDTO sealApplicationManagement){
+ // 5. 淇濆瓨閿�鍞彴璐﹂檮浠�
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE,
+ RecordTypeEnum.SEAL_APPLICATION_MANAGEMENT,
+ sealApplicationManagement.getId(),
+ sealApplicationManagement.getStorageBlobDTOs());
return AjaxResult.success(sealApplicationManagementService.updateById(sealApplicationManagement));
}
@@ -59,6 +74,9 @@
if (CollectionUtils.isEmpty(ids)) {
throw new RuntimeException("璇蜂紶鍏ヨ鍒犻櫎鐨処D");
}
+ fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE,
+ RecordTypeEnum.SEAL_APPLICATION_MANAGEMENT,
+ ids);
return AjaxResult.success(sealApplicationManagementService.removeBatchByIds(ids));
}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/dto/SealApplicationManagementDTO.java b/src/main/java/com/ruoyi/collaborativeApproval/dto/SealApplicationManagementDTO.java
index 8dc8454..7757490 100644
--- a/src/main/java/com/ruoyi/collaborativeApproval/dto/SealApplicationManagementDTO.java
+++ b/src/main/java/com/ruoyi/collaborativeApproval/dto/SealApplicationManagementDTO.java
@@ -1,7 +1,11 @@
package com.ruoyi.collaborativeApproval.dto;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement;
import lombok.Data;
+
+import java.util.List;
@Data
public class SealApplicationManagementDTO extends SealApplicationManagement {
@@ -11,4 +15,8 @@
//瀹℃壒浜�
private String approveUserName;
+
+ private List<StorageBlobDTO> storageBlobDTOs;
+ private List<StorageBlobVO> storageBlobVOList;
+
}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/SealApplicationManagementServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/SealApplicationManagementServiceImpl.java
index d63f2d2..ba20ae2 100644
--- a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/SealApplicationManagementServiceImpl.java
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/SealApplicationManagementServiceImpl.java
@@ -3,6 +3,8 @@
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.collaborativeApproval.dto.SealApplicationManagementDTO;
import com.ruoyi.collaborativeApproval.mapper.SealApplicationManagementMapper;
import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement;
@@ -14,9 +16,14 @@
@RequiredArgsConstructor
public class SealApplicationManagementServiceImpl extends ServiceImpl<SealApplicationManagementMapper, SealApplicationManagement> implements SealApplicationManagementService {
private final SealApplicationManagementMapper sealApplicationManagementMapper;
+ private final FileUtil fileUtil;
@Override
public IPage<SealApplicationManagementDTO> listPage(Page page, SealApplicationManagement sealApplicationManagement) {
- return sealApplicationManagementMapper.listPage(page, sealApplicationManagement);
+ IPage<SealApplicationManagementDTO> sealApplicationManagementDTOIPage = sealApplicationManagementMapper.listPage(page, sealApplicationManagement);
+ sealApplicationManagementDTOIPage.getRecords().forEach(item -> {
+ item.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.SEAL_APPLICATION_MANAGEMENT, item.getId()));
+ });
+ return sealApplicationManagementDTOIPage;
}
}
diff --git a/src/main/java/com/ruoyi/device/controller/DeviceRepairController.java b/src/main/java/com/ruoyi/device/controller/DeviceRepairController.java
index d3c19bb..7df7c26 100644
--- a/src/main/java/com/ruoyi/device/controller/DeviceRepairController.java
+++ b/src/main/java/com/ruoyi/device/controller/DeviceRepairController.java
@@ -46,10 +46,16 @@
return deviceRepairService.updateDeviceRepair(deviceRepairDto);
}
- @PostMapping ("repair")
+ @PostMapping ("/repair")
@Operation(summary = "璁惧缁翠慨")
public AjaxResult repair( @RequestBody DeviceRepairDto deviceRepairDto) {
- return deviceRepairService.updateDeviceRepair(deviceRepairDto);
+ return deviceRepairService.confirmRepair(deviceRepairDto);
+ }
+
+ @PostMapping ("/acceptance")
+ @Operation(summary = "璁惧鎶ヤ慨楠屾敹瀹℃壒")
+ public AjaxResult acceptance(@RequestBody DeviceRepairDto deviceRepairDto) {
+ return deviceRepairService.approveRepairAcceptance(deviceRepairDto);
}
@DeleteMapping("/{ids}")
diff --git a/src/main/java/com/ruoyi/device/execl/DeviceRepairExeclDto.java b/src/main/java/com/ruoyi/device/execl/DeviceRepairExeclDto.java
index c782da2..caf5c1a 100644
--- a/src/main/java/com/ruoyi/device/execl/DeviceRepairExeclDto.java
+++ b/src/main/java/com/ruoyi/device/execl/DeviceRepairExeclDto.java
@@ -47,6 +47,18 @@
@Excel(name = "缁翠慨缁撴灉")
private String maintenanceResult;
+ @Schema(description = "楠屾敹浜�")
+ @Excel(name = "楠屾敹浜�")
+ private String acceptanceName;
+
+ @Schema(description = "楠屾敹鏃堕棿")
+ @Excel(name = "楠屾敹鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime acceptanceTime;
+
+ @Schema(description = "楠屾敹澶囨敞")
+ @Excel(name = "楠屾敹澶囨敞")
+ private String acceptanceRemark;
+
@Schema(description = "鐘舵��")
@Excel(name = "鐘舵��")
private String statusStr;
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java b/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
index 9db134e..c9efa55 100644
--- a/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
@@ -50,7 +50,19 @@
@Schema(description = "缁翠慨缁撴灉")
private String maintenanceResult;
+ @Schema(description = "楠屾敹浜�")
+ private String acceptanceName;
+
+ @Schema(description = "楠屾敹鏃堕棿")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime acceptanceTime;
+
+ @Schema(description = "楠屾敹澶囨敞")
+ private String acceptanceRemark;
+
@Schema(description = "鐘舵�� 0 寰呯淮淇� 1瀹岀粨 2 澶辫触")
+ // 0:寰呯淮淇� 1:瀹岀粨 2:澶辫触 3:寰呴獙鏀�
private Integer status;
@Schema(description = "鍒涘缓鏃堕棿")
diff --git a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
index 2e35584..350a09d 100644
--- a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
+++ b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -44,6 +44,10 @@
@Schema(description = "璁惧id")
private Long taskId;
+ @Schema(description = "淇濆吇浜�")
+ @Excel(name = "淇濆吇浜�")
+ private String maintenancePerson;
+
@Schema(description = "棰戞")
@Excel(name = "棰戞")
private String frequencyType;
diff --git a/src/main/java/com/ruoyi/device/service/IDeviceRepairService.java b/src/main/java/com/ruoyi/device/service/IDeviceRepairService.java
index 8d20acd..ae0b913 100644
--- a/src/main/java/com/ruoyi/device/service/IDeviceRepairService.java
+++ b/src/main/java/com/ruoyi/device/service/IDeviceRepairService.java
@@ -19,6 +19,10 @@
AjaxResult updateDeviceRepair(DeviceRepairDto deviceRepairDto);
+ AjaxResult confirmRepair(DeviceRepairDto deviceRepairDto);
+
+ AjaxResult approveRepairAcceptance(DeviceRepairDto deviceRepairDto);
+
void export(HttpServletResponse response, Long[] ids);
DeviceRepairVo detailById(Long id);
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
index e3ebb0e..e5a73ac 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
@@ -47,6 +47,11 @@
private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
private final FileUtil fileUtil;
+ private static final int STATUS_PENDING_REPAIR = 0;
+ private static final int STATUS_COMPLETED = 1;
+ private static final int STATUS_FAILED = 2;
+ private static final int STATUS_PENDING_ACCEPTANCE = 3;
+
@Override
public IPage<DeviceRepairVo> queryPage(Page page, DeviceRepairDto deviceRepairDto) {
IPage<DeviceRepairVo> pageDto = deviceRepairMapper.queryPage(page, deviceRepairDto);
@@ -62,6 +67,9 @@
DeviceLedger byId = deviceLedgerService.getById(deviceRepairDto.getDeviceLedgerId());
deviceRepairDto.setDeviceName(byId.getDeviceName());
deviceRepairDto.setDeviceModel(byId.getDeviceModel());
+ if (deviceRepairDto.getStatus() == null) {
+ deviceRepairDto.setStatus(STATUS_PENDING_REPAIR);
+ }
boolean save = this.save(deviceRepairDto);
if (save) {
// 澶勭悊鍥剧墖涓婁紶
@@ -75,6 +83,15 @@
@Transactional(rollbackFor = Exception.class)
public AjaxResult updateDeviceRepair(DeviceRepairDto deviceRepairDto) {
DeviceRepair oldDeviceRepair = this.getById(deviceRepairDto.getId());
+ if (oldDeviceRepair == null) {
+ return AjaxResult.error("鎶ヤ慨璁板綍涓嶅瓨鍦�");
+ }
+ if (deviceRepairDto.getStatus() != null
+ && deviceRepairDto.getStatus() == STATUS_COMPLETED
+ && (oldDeviceRepair.getStatus() == null
+ || oldDeviceRepair.getStatus() != STATUS_COMPLETED)) {
+ return AjaxResult.error("璇峰厛鎻愪氦楠屾敹瀹℃壒锛岄獙鏀堕�氳繃鍚庢墠鍙畬缁�");
+ }
// 澶勭悊澶囦欢浣跨敤鎯呭喌
if (CollectionUtils.isNotEmpty(deviceRepairDto.getSparePartsUseList())) {
List<Long> sparePartIds = new ArrayList<>();
@@ -131,6 +148,58 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult confirmRepair(DeviceRepairDto deviceRepairDto) {
+ DeviceRepair oldDeviceRepair = this.getById(deviceRepairDto.getId());
+ if (oldDeviceRepair == null) {
+ return AjaxResult.error("鎶ヤ慨璁板綍涓嶅瓨鍦�");
+ }
+ if (oldDeviceRepair.getStatus() != null && oldDeviceRepair.getStatus() == STATUS_COMPLETED) {
+ return AjaxResult.error("璇ユ姤淇凡瀹岀粨锛屼笉鑳介噸澶嶇‘璁ょ淮淇�");
+ }
+ if (oldDeviceRepair.getStatus() != null && oldDeviceRepair.getStatus() == STATUS_PENDING_ACCEPTANCE) {
+ return AjaxResult.error("璇ユ姤淇凡鎻愪氦楠屾敹瀹℃壒");
+ }
+ deviceRepairDto.setStatus(STATUS_PENDING_ACCEPTANCE);
+ return updateDeviceRepair(deviceRepairDto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult approveRepairAcceptance(DeviceRepairDto deviceRepairDto) {
+ if (deviceRepairDto.getId() == null) {
+ return AjaxResult.error("鎶ヤ慨璁板綍id涓嶈兘涓虹┖");
+ }
+ DeviceRepair oldDeviceRepair = this.getById(deviceRepairDto.getId());
+ if (oldDeviceRepair == null) {
+ return AjaxResult.error("鎶ヤ慨璁板綍涓嶅瓨鍦�");
+ }
+ if (oldDeviceRepair.getStatus() == null || oldDeviceRepair.getStatus() != STATUS_PENDING_ACCEPTANCE) {
+ return AjaxResult.error("璇ユ姤淇湭杩涘叆寰呴獙鏀剁姸鎬侊紝涓嶈兘瀹℃壒");
+ }
+ if (StringUtils.isBlank(deviceRepairDto.getAcceptanceName())) {
+ return AjaxResult.error("楠屾敹浜轰笉鑳戒负绌�");
+ }
+ if (deviceRepairDto.getAcceptanceTime() == null) {
+ return AjaxResult.error("楠屾敹鏃堕棿涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isBlank(deviceRepairDto.getAcceptanceRemark())) {
+ return AjaxResult.error("楠屾敹澶囨敞涓嶈兘涓虹┖");
+ }
+
+ DeviceRepair update = new DeviceRepair();
+ update.setId(deviceRepairDto.getId());
+ update.setAcceptanceName(deviceRepairDto.getAcceptanceName());
+ update.setAcceptanceTime(deviceRepairDto.getAcceptanceTime());
+ update.setAcceptanceRemark(deviceRepairDto.getAcceptanceRemark());
+ update.setStatus(STATUS_COMPLETED);
+ if (this.updateById(update)) {
+ return AjaxResult.success();
+ }
+ return AjaxResult.error("楠屾敹瀹℃壒澶辫触");
+ }
+
+ @Override
public void export(HttpServletResponse response, Long[] ids) {
if (ids == null || ids.length == 0) {
List<DeviceRepair> supplierManageList = this.list();
@@ -138,24 +207,19 @@
supplierManageList.stream().forEach(deviceRepair -> {
DeviceRepairExeclDto deviceRepairExeclDto = new DeviceRepairExeclDto();
BeanUtils.copyProperties(deviceRepair,deviceRepairExeclDto);
- deviceRepairExeclDto.setStatusStr(deviceRepair.getStatus() == 0 ? "寰呯淮淇�" : deviceRepair.getStatus() == 1 ? "瀹岀粨" : "澶辫触");
-
+ deviceRepairExeclDto.setStatusStr(resolveStatusText(deviceRepair.getStatus()));
deviceLedgerExeclDtos.add(deviceRepairExeclDto);
});
ExcelUtil<DeviceRepairExeclDto> util = new ExcelUtil<DeviceRepairExeclDto>(DeviceRepairExeclDto.class);
util.exportExcel(response, deviceLedgerExeclDtos, "璁惧鎶ヤ慨瀵煎嚭");
}else {
- ArrayList<Long> arrayList = new ArrayList<>();
- Arrays.stream(ids).map(id -> {
- return arrayList.add( id);
- });
+ ArrayList<Long> arrayList = new ArrayList<>(Arrays.asList(ids));
List<DeviceRepair> supplierManageList = deviceRepairMapper.selectBatchIds(arrayList);
ArrayList<DeviceRepairExeclDto> deviceLedgerExeclDtos = new ArrayList<>();
supplierManageList.stream().forEach(deviceRepair -> {
DeviceRepairExeclDto deviceRepairExeclDto = new DeviceRepairExeclDto();
BeanUtils.copyProperties(deviceRepair,deviceRepairExeclDto);
- deviceRepairExeclDto.setStatusStr(deviceRepair.getStatus() == 0 ? "寰呯淮淇�" : deviceRepair.getStatus() == 1 ? "瀹岀粨" : "澶辫触");
-
+ deviceRepairExeclDto.setStatusStr(resolveStatusText(deviceRepair.getStatus()));
deviceLedgerExeclDtos.add(deviceRepairExeclDto);
});
ExcelUtil<DeviceRepairExeclDto> util = new ExcelUtil<DeviceRepairExeclDto>(DeviceRepairExeclDto.class);
@@ -164,6 +228,25 @@
}
+ private String resolveStatusText(Integer status) {
+ if (status == null) {
+ return "";
+ }
+ if (status == STATUS_PENDING_REPAIR) {
+ return "寰呯淮淇�";
+ }
+ if (status == STATUS_COMPLETED) {
+ return "瀹岀粨";
+ }
+ if (status == STATUS_FAILED) {
+ return "澶辫触";
+ }
+ if (status == STATUS_PENDING_ACCEPTANCE) {
+ return "寰呴獙鏀�";
+ }
+ return "鏈煡";
+ }
+
@Override
public DeviceRepairVo detailById(Long id) {
DeviceRepairVo vo = deviceRepairMapper.detailById(id);
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
index 09d5d77..204667b 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -93,6 +93,7 @@
inspectionTask.setMaintenanceTaskId(timingTask.getId());
inspectionTask.setDeviceLedgerId(timingTask.getTaskId());
inspectionTask.setMaintenancePlanTime(LocalDateTime.now());
+ inspectionTask.setMaintenanceActuallyName(timingTask.getMaintenancePerson());
inspectionTask.setFrequencyType(timingTask.getFrequencyType());
inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
inspectionTask.setTenantId(timingTask.getTenantId());
diff --git a/src/main/java/com/ruoyi/framework/security/LoginUser.java b/src/main/java/com/ruoyi/framework/security/LoginUser.java
index 330ae43..aad84a9 100644
--- a/src/main/java/com/ruoyi/framework/security/LoginUser.java
+++ b/src/main/java/com/ruoyi/framework/security/LoginUser.java
@@ -301,7 +301,10 @@
public void setUser(SysUser user)
{
this.user = user;
- this.aiEnabled = user == null ? null : user.getAiEnabled();
+ if (user != null && user.getAiEnabled() != null)
+ {
+ this.aiEnabled = user.getAiEnabled();
+ }
}
@Override
diff --git a/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java b/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
index c41d626..4e7efad 100644
--- a/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
+++ b/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
@@ -30,6 +30,10 @@
@Excel(name = "宸℃浠诲姟鍚嶇О")
private String taskName;
+ @Schema(description = "宸℃椤圭洰")
+ @Excel(name = "宸℃椤圭洰")
+ private String inspectionProject;
+
@Schema(description = "璁惧id")
private Integer taskId;
@@ -44,6 +48,22 @@
@Excel(name = "澶囨敞")
private String remarks;
+ @Schema(description = "宸℃缁撴灉 0 寮傚父 1 姝e父")
+ private String inspectionResult;
+
+ @Schema(description = "寮傚父鎻忚堪")
+ private String abnormalDescription;
+
+ @Schema(description = "鍏宠仈缁翠慨鍗旾D")
+ private Long deviceRepairId;
+
+ @Schema(description = "楠屾敹浜篒D")
+ private Long acceptanceUserId;
+
+ @Schema(description = "楠屾敹浜�")
+ @Excel(name = "楠屾敹浜�")
+ private String acceptanceName;
+
@Schema(description = "浠诲姟鐧昏浜篒D")
private Long registrantId;
diff --git a/src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java b/src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java
index 2d8722e..43a3edc 100644
--- a/src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java
+++ b/src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java
@@ -33,6 +33,10 @@
@Excel(name = "宸℃浠诲姟鍚嶇О")
private String taskName;
+ @Schema(description = "宸℃椤圭洰")
+ @Excel(name = "宸℃椤圭洰")
+ private String inspectionProject;
+
@Schema(description = "璁惧id")
private Integer taskId;
@@ -60,6 +64,10 @@
@Schema(description = "鏄惁婵�娲�")
private boolean isActive;
+ @Schema(description = "鏄惁鍚敤 0鍚� 1鏄�")
+ @Excel(name = "鏄惁鍚敤", readConverterExp = "0=鍚�,1=鏄�")
+ private Integer isEnabled;
+
@Schema(description = "澶囨敞")
@Excel(name = "澶囨敞")
private String remarks;
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
index 410336e..5826fa7 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -5,12 +5,17 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.device.mapper.DeviceLedgerMapper;
+import com.ruoyi.device.mapper.DeviceRepairMapper;
+import com.ruoyi.device.pojo.DeviceLedger;
+import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
@@ -23,6 +28,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.time.format.DateTimeFormatter;
+import java.util.Date;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -43,6 +49,14 @@
private final FileUtil fileUtil;
+ private final DeviceRepairMapper deviceRepairMapper;
+
+ private final DeviceLedgerMapper deviceLedgerMapper;
+
+ private static final String INSPECTION_RESULT_ABNORMAL = "0";
+ private static final String INSPECTION_RESULT_NORMAL = "1";
+ private static final int REPAIR_STATUS_PENDING = 0;
+
@Override
public IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
LambdaQueryWrapper<InspectionTask> queryWrapper = new LambdaQueryWrapper<>();
@@ -50,14 +64,15 @@
if (StringUtils.isNotBlank(inspectionTaskDto.getTaskName())) {
queryWrapper.like(InspectionTask::getTaskName, inspectionTaskDto.getTaskName());
}
+ if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionProject())) {
+ queryWrapper.like(InspectionTask::getInspectionProject, inspectionTaskDto.getInspectionProject());
+ }
IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper);
// 鏃犳暟鎹彁鍓嶈繑鍥�
if (CollectionUtils.isEmpty(entityPage.getRecords())) {
return new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
}
- // 鑾峰彇id闆嗗悎
- List<Long> ids = entityPage.getRecords().stream().map(InspectionTask::getId).collect(Collectors.toList());
//鐧昏浜篿ds
List<Long> registrantIds = entityPage.getRecords().stream().map(InspectionTask::getRegistrantId).collect(Collectors.toList());
// 鎵归噺鏌ヨ鐧昏浜�
@@ -68,9 +83,6 @@
} else {
sysUserMap = new HashMap<>();
}
- //宸℃浜篿ds
- List<String> inspectorIds = entityPage.getRecords().stream().map(InspectionTask::getInspectorId).collect(Collectors.toList());
-
//鑾峰彇鎵�鏈変笉閲嶅鐨勭敤鎴稩D
Set<Long> allUserIds = entityPage.getRecords().stream()
.map(InspectionTask::getInspectorId) // 鑾峰彇"2,3"杩欐牱鐨勫瓧绗︿覆
@@ -140,24 +152,230 @@
@Override
@Transactional(rollbackFor = Exception.class)
public int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) {
+ InspectionTask oldInspectionTask = null;
+ if (Objects.nonNull(inspectionTaskDto.getId())) {
+ oldInspectionTask = inspectionTaskMapper.selectById(inspectionTaskDto.getId());
+ if (oldInspectionTask == null) {
+ throw new IllegalArgumentException("宸℃浠诲姟涓嶅瓨鍦�");
+ }
+ }
+ validateInspectionInput(inspectionTaskDto, oldInspectionTask);
+
InspectionTask inspectionTask = new InspectionTask();
BeanUtils.copyProperties(inspectionTaskDto, inspectionTask);
inspectionTask.setRegistrantId(SecurityUtils.getLoginUser().getUserId());
inspectionTask.setRegistrant(SecurityUtils.getLoginUser().getUsername());
+ fillAcceptanceInfo(inspectionTask, oldInspectionTask);
int i;
if (Objects.isNull(inspectionTaskDto.getId())) {
i = inspectionTaskMapper.insert(inspectionTask);
} else {
i = inspectionTaskMapper.updateById(inspectionTask);
}
- // 淇濆瓨鏂囦欢
- fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListDTO());
- fileUtil.saveStorageAttachment(ApplicationTypeEnum.AFTER_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListAfterDTO());
- fileUtil.saveStorageAttachment(ApplicationTypeEnum.BEFORE_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListBeforeDTO());
+ if (i <= 0) {
+ return i;
+ }
+ Long linkedRepairId = syncRepairOrderIfAbnormal(inspectionTask, oldInspectionTask);
+ if (linkedRepairId != null && !Objects.equals(linkedRepairId, inspectionTask.getDeviceRepairId())) {
+ InspectionTask relationUpdate = new InspectionTask();
+ relationUpdate.setId(inspectionTask.getId());
+ relationUpdate.setDeviceRepairId(linkedRepairId);
+ inspectionTaskMapper.updateById(relationUpdate);
+ inspectionTask.setDeviceRepairId(linkedRepairId);
+ }
+ // 淇濆瓨鏂囦欢锛堝瓧娈典笉浼犲垯淇濈暀鍘嗗彶锛�
+ saveInspectionAttachments(inspectionTask.getId(), inspectionTaskDto);
return i;
}
+ private void validateInspectionInput(InspectionTaskDto inspectionTaskDto, InspectionTask oldInspectionTask) {
+ String inspectionResult = inspectionTaskDto.getInspectionResult();
+ if (StringUtils.isBlank(inspectionResult) && oldInspectionTask != null) {
+ inspectionResult = oldInspectionTask.getInspectionResult();
+ }
+ if (StringUtils.isBlank(inspectionResult)) {
+ throw new IllegalArgumentException("璇烽�夋嫨宸℃缁撴灉");
+ }
+ if (!INSPECTION_RESULT_ABNORMAL.equals(inspectionResult) && !INSPECTION_RESULT_NORMAL.equals(inspectionResult)) {
+ throw new IllegalArgumentException("宸℃缁撴灉浠呮敮鎸侊細0-寮傚父锛�1-姝e父");
+ }
+ inspectionTaskDto.setInspectionResult(inspectionResult);
+
+ if (!INSPECTION_RESULT_ABNORMAL.equals(inspectionResult)) {
+ return;
+ }
+
+ String abnormalDescription = inspectionTaskDto.getAbnormalDescription();
+ if (StringUtils.isBlank(abnormalDescription) && oldInspectionTask != null) {
+ abnormalDescription = oldInspectionTask.getAbnormalDescription();
+ }
+ if (StringUtils.isBlank(abnormalDescription)) {
+ throw new IllegalArgumentException("宸℃缁撴灉涓哄紓甯告椂锛屽紓甯告弿杩颁笉鑳戒负绌�");
+ }
+ inspectionTaskDto.setAbnormalDescription(abnormalDescription);
+
+ if (!hasAnyInspectionPhotoAfterSave(inspectionTaskDto, oldInspectionTask)) {
+ throw new IllegalArgumentException("宸℃缁撴灉涓哄紓甯告椂锛屽繀椤讳笂浼犺嚦灏戜竴寮犵収鐗�");
+ }
+ }
+
+ private boolean hasAnyInspectionPhotoAfterSave(InspectionTaskDto inspectionTaskDto, InspectionTask oldInspectionTask) {
+ Long recordId = oldInspectionTask == null ? null : oldInspectionTask.getId();
+ return hasApplicationPhotoAfterSave(inspectionTaskDto.getCommonFileListDTO(), ApplicationTypeEnum.FILE, recordId)
+ || hasApplicationPhotoAfterSave(inspectionTaskDto.getCommonFileListAfterDTO(), ApplicationTypeEnum.AFTER_FILE, recordId)
+ || hasApplicationPhotoAfterSave(inspectionTaskDto.getCommonFileListBeforeDTO(), ApplicationTypeEnum.BEFORE_FILE, recordId);
+ }
+
+ private boolean hasApplicationPhotoAfterSave(List<StorageBlobDTO> requestPhotos, ApplicationTypeEnum applicationType, Long recordId) {
+ if (requestPhotos != null) {
+ return !requestPhotos.isEmpty();
+ }
+ if (recordId == null) {
+ return false;
+ }
+ return CollectionUtils.isNotEmpty(fileUtil.getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(applicationType, RecordTypeEnum.INSPECTION_TASK, recordId));
+ }
+
+ private void fillAcceptanceInfo(InspectionTask inspectionTask, InspectionTask oldInspectionTask) {
+ Long acceptanceUserId = inspectionTask.getAcceptanceUserId();
+ if (acceptanceUserId == null && oldInspectionTask != null) {
+ acceptanceUserId = oldInspectionTask.getAcceptanceUserId();
+ }
+ if (acceptanceUserId != null) {
+ inspectionTask.setAcceptanceUserId(acceptanceUserId);
+ }
+
+ String acceptanceName = inspectionTask.getAcceptanceName();
+ if (StringUtils.isBlank(acceptanceName) && acceptanceUserId != null) {
+ SysUser acceptanceUser = sysUserMapper.selectUserById(acceptanceUserId);
+ if (acceptanceUser != null) {
+ acceptanceName = acceptanceUser.getNickName();
+ }
+ }
+ if (StringUtils.isBlank(acceptanceName) && oldInspectionTask != null) {
+ acceptanceName = oldInspectionTask.getAcceptanceName();
+ }
+ inspectionTask.setAcceptanceName(acceptanceName);
+ }
+
+ private Long syncRepairOrderIfAbnormal(InspectionTask inspectionTask, InspectionTask oldInspectionTask) {
+ if (!INSPECTION_RESULT_ABNORMAL.equals(inspectionTask.getInspectionResult())) {
+ return inspectionTask.getDeviceRepairId();
+ }
+ Long linkedRepairId = inspectionTask.getDeviceRepairId();
+ if (linkedRepairId == null && oldInspectionTask != null) {
+ linkedRepairId = oldInspectionTask.getDeviceRepairId();
+ }
+
+ if (linkedRepairId != null) {
+ DeviceRepair updateRepair = new DeviceRepair();
+ updateRepair.setId(linkedRepairId);
+ updateRepair.setRemark(inspectionTask.getAbnormalDescription());
+ deviceRepairMapper.updateById(updateRepair);
+ return linkedRepairId;
+ }
+
+ DeviceRepair deviceRepair = buildDeviceRepair(inspectionTask, oldInspectionTask);
+ deviceRepairMapper.insert(deviceRepair);
+ return deviceRepair.getId();
+ }
+
+ private DeviceRepair buildDeviceRepair(InspectionTask inspectionTask, InspectionTask oldInspectionTask) {
+ DeviceRepair deviceRepair = new DeviceRepair();
+ Long deviceLedgerId = resolveDeviceLedgerId(inspectionTask, oldInspectionTask);
+ DeviceLedger deviceLedger = resolveDeviceLedger(deviceLedgerId);
+ deviceRepair.setDeviceLedgerId(deviceLedgerId);
+ deviceRepair.setDeviceName(resolveDeviceName(inspectionTask, oldInspectionTask, deviceLedger));
+ deviceRepair.setDeviceModel(deviceLedger == null ? null : deviceLedger.getDeviceModel());
+ deviceRepair.setRepairName(resolveRepairReporter(inspectionTask, oldInspectionTask));
+ deviceRepair.setRepairTime(new Date());
+ deviceRepair.setRemark(inspectionTask.getAbnormalDescription());
+ deviceRepair.setMachineryCategory(deviceLedger == null ? null : deviceLedger.getType());
+ deviceRepair.setStatus(REPAIR_STATUS_PENDING);
+ return deviceRepair;
+ }
+
+ private Long resolveDeviceLedgerId(InspectionTask inspectionTask, InspectionTask oldInspectionTask) {
+ Integer taskId = inspectionTask.getTaskId();
+ if (taskId == null && oldInspectionTask != null) {
+ taskId = oldInspectionTask.getTaskId();
+ }
+ if (taskId == null || taskId <= 0) {
+ return null;
+ }
+ return taskId.longValue();
+ }
+
+ private DeviceLedger resolveDeviceLedger(Long deviceLedgerId) {
+ if (deviceLedgerId == null) {
+ return null;
+ }
+ return deviceLedgerMapper.selectById(deviceLedgerId);
+ }
+
+ private String resolveDeviceName(InspectionTask inspectionTask, InspectionTask oldInspectionTask, DeviceLedger deviceLedger) {
+ String taskName = inspectionTask.getTaskName();
+ if (StringUtils.isBlank(taskName) && oldInspectionTask != null) {
+ taskName = oldInspectionTask.getTaskName();
+ }
+ if (StringUtils.isNotBlank(taskName)) {
+ return taskName;
+ }
+ return deviceLedger == null ? null : deviceLedger.getDeviceName();
+ }
+
+ private String resolveRepairReporter(InspectionTask inspectionTask, InspectionTask oldInspectionTask) {
+ String reporter = inspectionTask.getInspector();
+ if (StringUtils.isBlank(reporter) && oldInspectionTask != null) {
+ reporter = oldInspectionTask.getInspector();
+ }
+ if (StringUtils.isNotBlank(reporter)) {
+ return reporter;
+ }
+ String inspectorNameByUserId = resolveInspectorNameByUserId(inspectionTask.getInspectorId());
+ if (StringUtils.isBlank(inspectorNameByUserId) && oldInspectionTask != null) {
+ inspectorNameByUserId = resolveInspectorNameByUserId(oldInspectionTask.getInspectorId());
+ }
+ if (StringUtils.isNotBlank(inspectorNameByUserId)) {
+ return inspectorNameByUserId;
+ }
+ try {
+ return SecurityUtils.getUsername();
+ } catch (Exception ignored) {
+ return "system";
+ }
+ }
+
+ private String resolveInspectorNameByUserId(String inspectorIds) {
+ if (StringUtils.isBlank(inspectorIds)) {
+ return null;
+ }
+ String firstInspectorId = Arrays.stream(inspectorIds.split(","))
+ .map(String::trim)
+ .filter(StringUtils::isNotBlank)
+ .findFirst()
+ .orElse(null);
+ if (!StringUtils.isNumeric(firstInspectorId)) {
+ return null;
+ }
+ SysUser sysUser = sysUserMapper.selectUserById(Long.parseLong(firstInspectorId));
+ return sysUser == null ? null : sysUser.getNickName();
+ }
+
+ private void saveInspectionAttachments(Long inspectionTaskId, InspectionTaskDto inspectionTaskDto) {
+ saveAttachmentIfPresent(inspectionTaskId, ApplicationTypeEnum.FILE, inspectionTaskDto.getCommonFileListDTO());
+ saveAttachmentIfPresent(inspectionTaskId, ApplicationTypeEnum.AFTER_FILE, inspectionTaskDto.getCommonFileListAfterDTO());
+ saveAttachmentIfPresent(inspectionTaskId, ApplicationTypeEnum.BEFORE_FILE, inspectionTaskDto.getCommonFileListBeforeDTO());
+ }
+
+ private void saveAttachmentIfPresent(Long inspectionTaskId, ApplicationTypeEnum applicationTypeEnum, List<StorageBlobDTO> storageBlobDTOS) {
+ if (storageBlobDTOS == null) {
+ return;
+ }
+ fileUtil.saveStorageAttachment(applicationTypeEnum, RecordTypeEnum.INSPECTION_TASK, inspectionTaskId, storageBlobDTOS);
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public int delByIds(Long[] ids) {
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
index d9c5f69..adc2416 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
@@ -3,7 +3,7 @@
import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
import com.ruoyi.inspectiontask.pojo.TimingTask;
-import lombok.RequiredArgsConstructor;
+import com.ruoyi.common.utils.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
@@ -47,6 +47,9 @@
TimingTask timingTask = tasks.isEmpty() ? null : tasks.get(0);
if (timingTask == null) {
throw new JobExecutionException("鎵句笉鍒板畾鏃朵换鍔�: " + taskId);
+ }
+ if (timingTask.getIsEnabled() != null && timingTask.getIsEnabled() == 0) {
+ return;
}
// if (!timingTask.isActive()) {
@@ -100,10 +103,15 @@
// 澶嶅埗鍩烘湰灞炴��
inspectionTask.setTaskName(timingTask.getTaskName());
+ inspectionTask.setInspectionProject(timingTask.getInspectionProject());
inspectionTask.setTaskId(timingTask.getTaskId());
inspectionTask.setInspectorId(timingTask.getInspectorIds());
inspectionTask.setInspectionLocation(timingTask.getInspectionLocation());
- inspectionTask.setRemarks("鑷姩鐢熸垚鑷畾鏃朵换鍔D: " + timingTask.getId());
+ String remarks = "鑷姩鐢熸垚鑷畾鏃朵换鍔D: " + timingTask.getId();
+ if (StringUtils.isNotBlank(timingTask.getRemarks())) {
+ remarks = remarks + "锛�" + timingTask.getRemarks();
+ }
+ inspectionTask.setRemarks(remarks);
inspectionTask.setRegistrantId(timingTask.getRegistrantId());
inspectionTask.setFrequencyType(timingTask.getFrequencyType());
inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
index 8772f48..779dc5f 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -35,6 +35,8 @@
private final TimingTaskMapper timingTaskMapper;
private final TimingTaskScheduler timingTaskScheduler;
private final SysUserMapper sysUserMapper;
+ private static final int ENABLED = 1;
+ private static final int DISABLED = 0;
@Override
@@ -44,6 +46,12 @@
LambdaQueryWrapper<TimingTask> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(timingTask.getTaskName())) {
queryWrapper.like(TimingTask::getTaskName, timingTask.getTaskName());
+ }
+ if (StringUtils.isNotBlank(timingTask.getInspectionProject())) {
+ queryWrapper.like(TimingTask::getInspectionProject, timingTask.getInspectionProject());
+ }
+ if (timingTask.getIsEnabled() != null) {
+ queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled());
}
IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper);
@@ -115,8 +123,17 @@
@Override
@Transactional
public int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException {
+ TimingTask oldTimingTask = null;
+ if (Objects.nonNull(timingTaskDto.getId())) {
+ oldTimingTask = timingTaskMapper.selectById(timingTaskDto.getId());
+ if (oldTimingTask == null) {
+ throw new IllegalArgumentException("瀹氭椂浠诲姟涓嶅瓨鍦�");
+ }
+ }
TimingTask timingTask = new TimingTask();
BeanUtils.copyProperties(timingTaskDto, timingTask);
+ timingTask.setIsEnabled(resolveEnabledValue(timingTask.getIsEnabled(), oldTimingTask));
+ timingTask.setActive(ENABLED == timingTask.getIsEnabled());
// 1. 瑙f瀽瀛楃涓蹭负 LocalDate锛堝彧鍖呭惈骞存湀鏃ワ級
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate localDate = LocalDate.now();
@@ -132,13 +149,12 @@
// 璁剧疆鍒涘缓浜轰俊鎭拰榛樿鍊�
if (Objects.isNull(timingTaskDto.getId())) {
timingTask.setRegistrationDate(LocalDate.now());
- timingTask.setActive(true);
// 璁$畻棣栨鎵ц鏃堕棿
LocalDateTime firstExecutionTime = calculateFirstExecutionTime(timingTask);
timingTask.setNextExecutionTime(firstExecutionTime);
int result = timingTaskMapper.insert(timingTask);
- if (result > 0) {
+ if (result > 0 && isEnabled(timingTask.getIsEnabled(), timingTask.isActive())) {
// 鏂板鎴愬姛鍚庢坊鍔犲埌璋冨害鍣�
timingTaskScheduler.scheduleTimingTask(timingTask);
}
@@ -148,8 +164,17 @@
int result = timingTaskMapper.updateById(timingTask);
if (result > 0) {
- // 鏇存柊鎴愬姛鍚庨噸鏂拌皟搴︿换鍔�
- timingTaskScheduler.rescheduleTimingTask(timingTask);
+ boolean oldEnabled = isEnabled(oldTimingTask == null ? null : oldTimingTask.getIsEnabled(), oldTimingTask != null && oldTimingTask.isActive());
+ boolean newEnabled = isEnabled(timingTask.getIsEnabled(), timingTask.isActive());
+ if (!newEnabled) {
+ timingTaskScheduler.unscheduleTimingTask(timingTask.getId());
+ } else if (oldEnabled) {
+ // 鏇存柊鎴愬姛鍚庨噸鏂拌皟搴︿换鍔�
+ timingTaskScheduler.rescheduleTimingTask(timingTask);
+ } else {
+ // 浠庣鐢ㄦ敼涓哄惎鐢ㄦ椂閲嶆柊鍒涘缓璋冨害浠诲姟
+ timingTaskScheduler.scheduleTimingTask(timingTask);
+ }
}
return result;
}
@@ -451,6 +476,26 @@
return days;
}
+ private Integer resolveEnabledValue(Integer requestEnabled, TimingTask oldTimingTask) {
+ if (requestEnabled != null) {
+ return requestEnabled;
+ }
+ if (oldTimingTask != null) {
+ if (oldTimingTask.getIsEnabled() != null) {
+ return oldTimingTask.getIsEnabled();
+ }
+ return oldTimingTask.isActive() ? ENABLED : DISABLED;
+ }
+ return ENABLED;
+ }
+
+ private boolean isEnabled(Integer enabledValue, boolean activeFallback) {
+ if (enabledValue != null) {
+ return ENABLED == enabledValue;
+ }
+ return activeFallback;
+ }
+
@Override
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/Details.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/Details.java
similarity index 90%
rename from src/main/java/com/ruoyi/procurementrecord/dto/Details.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/Details.java
index 08dd6af..ba226d1 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/Details.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/Details.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import lombok.Data;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/InventoryInformationDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/InventoryInformationDto.java
similarity index 91%
rename from src/main/java/com/ruoyi/procurementrecord/dto/InventoryInformationDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/InventoryInformationDto.java
index 2396b29..b45b5b9 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/InventoryInformationDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/InventoryInformationDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import lombok.Data;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementAddDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementAddDto.java
similarity index 89%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementAddDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementAddDto.java
index 6bb4e47..e231348 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementAddDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementAddDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import lombok.Data;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
similarity index 97%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
index d8004b4..aa88d45 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementManagementUpdateDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementManagementUpdateDto.java
similarity index 94%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementManagementUpdateDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementManagementUpdateDto.java
index ac896a2..f9fc9ff 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementManagementUpdateDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementManagementUpdateDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDto.java
similarity index 98%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDto.java
index 57c2dbb..fa3e337 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDtoCopy.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDtoCopy.java
similarity index 98%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDtoCopy.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDtoCopy.java
index 9173ce3..8e89c2f 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDtoCopy.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDtoCopy.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutAdd.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
similarity index 90%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutAdd.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
index 58fba64..09272c4 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutAdd.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import lombok.Data;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutPageDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
similarity index 97%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutPageDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
index 6b57f2a..a5dd8d6 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutPageDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementUpdateDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementUpdateDto.java
similarity index 90%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ProcurementUpdateDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementUpdateDto.java
index 1a51301..cf9320d 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ProcurementUpdateDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementUpdateDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
import lombok.Data;
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java
similarity index 65%
rename from src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java
rename to src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java
index b69f241..539f3c7 100644
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java
@@ -1,17 +1,9 @@
-package com.ruoyi.procurementrecord.dto;
+package com.ruoyi.procurementrecord.bean.dto;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
-import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
-import com.ruoyi.sales.pojo.SalesLedgerProduct;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-import java.time.LocalDateTime;
import java.util.List;
/**
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java
new file mode 100644
index 0000000..6cfae1b
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java
@@ -0,0 +1,38 @@
+package com.ruoyi.procurementrecord.bean.dto;
+
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ReturnSaleProductDto extends ReturnSaleProduct {
+
+ private String productName;
+
+ private String model;
+
+ private String unit;
+
+ //鏈��璐ф暟閲�
+ private BigDecimal unQuantity;
+
+ //鎬婚��璐ф暟閲�
+ private BigDecimal totalReturnNum;
+
+ // 閫�璐ф�讳环
+ private BigDecimal price;
+
+ // 閫�璐ф�讳环
+ private BigDecimal taxInclusiveUnitPrice;
+
+ @Schema(description = "鍑哄簱鍗曞彿")
+ private String outboundBatches;
+
+ @Schema(description = "鎵规鍙�")
+ private String batchNo;
+
+ @Schema(description = "鍙戣揣鍑哄簱鏁伴噺")
+ private BigDecimal stockOutNum;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingInfoVo.java b/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingInfoVo.java
new file mode 100644
index 0000000..d442c63
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingInfoVo.java
@@ -0,0 +1,17 @@
+package com.ruoyi.procurementrecord.bean.vo;
+
+import com.ruoyi.sales.pojo.ShippingInfo;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Schema(name = "ShippingInfoVo", description = "钀ラ攢绠$悊--鍙戣揣淇℃伅")
+public class ShippingInfoVo {
+
+ private ShippingInfo shippingInfo;
+
+ @Schema(description = "鍙戣揣浜у搧鍒楄〃")
+ private List<ShippingProductVo> shippingProductVoList;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java b/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java
new file mode 100644
index 0000000..9f87aef
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java
@@ -0,0 +1,43 @@
+package com.ruoyi.procurementrecord.bean.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@Schema(name = "ShippingProductVo", description = "钀ラ攢绠$悊--鍙戣揣鍑哄簱浜у搧鍒楄〃")
+public class ShippingProductVo {
+ @Schema(description = "鍑哄簱鍗昳d")
+ private Long id;
+
+ @Schema(description = "浜у搧瑙勬牸id")
+ private Long productModelId;
+
+ @Schema(description = "浜у搧澶х被")
+ private String productCategory;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String specificationModel;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "鍑哄簱鍗曞彿")
+ private String outboundBatches;
+
+ @Schema(description = "鍙戣揣鍑哄簱鏁伴噺")
+ private BigDecimal stockOutNum;
+
+ @Schema(description = "鎵规鍙�")
+ private String batchNo;
+
+ @Schema(description = "鏈��璐ф暟")
+ private BigDecimal unQuantity;
+
+ @Schema(description = "閫�璐ф�绘暟")
+ private BigDecimal totalReturnNum;
+
+ @Schema(description = "鍚◣鍗曚环")
+ private BigDecimal taxInclusiveUnitPrice;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java
index c594994..bb14493 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java
@@ -1,24 +1,13 @@
package com.ruoyi.procurementrecord.controller;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.framework.aspectj.lang.annotation.Log;
-import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.procurementrecord.dto.*;
import com.ruoyi.procurementrecord.mapper.ProcurementExceptionRecordMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementExceptionRecord;
-import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.List;
/**
* @author :yys
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java
index e567eea..7e49990 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java
@@ -8,7 +8,7 @@
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.procurementrecord.dto.*;
+import com.ruoyi.procurementrecord.bean.dto.*;
import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
import com.ruoyi.procurementrecord.pojo.CustomStorage;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
@@ -16,7 +16,6 @@
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import org.apache.ibatis.annotations.Delete;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
index f024bfe..c86d3e6 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
@@ -7,9 +7,9 @@
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.procurementrecord.dto.ProcurementRecordOutAdd;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
-import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutAdd;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
index 22882f4..b3f31b9 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
@@ -4,21 +4,16 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
-import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
-import com.ruoyi.procurementrecord.pojo.ReturnManagement;
+import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.bean.vo.ShippingInfoVo;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.ruoyi.procurementrecord.service.ReturnManagementService;
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
-import com.ruoyi.procurementrecord.service.impl.ReturnSaleProductServiceImpl;
-import com.ruoyi.sales.dto.SalesLedgerDto;
-import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@@ -87,10 +82,10 @@
}
@GetMapping("/getByShippingId")
- @Operation(summary = "閿�鍞��璐�-鏍规嵁鍑哄簱鍗曟煡璇㈤攢鍞鍗曚互鍙婁骇鍝佷俊鎭�")
+ @Operation(summary = "閿�鍞��璐�-鏍规嵁鍙戣揣鍗曟煡璇㈤攢鍞鍗曚互鍙婂嚭搴撶殑浜у搧淇℃伅")
public AjaxResult getByShippingId(Long shippingId) {
- SalesLedgerDto salesLedgerDto = returnManagementService.getReturnManagementDtoByShippingIdId(shippingId);
- return success(salesLedgerDto);
+ ShippingInfoVo shippingInfoVo = returnManagementService.getReturnManagementDtoByShippingIdId(shippingId);
+ return success(shippingInfoVo);
}
}
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java b/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java
deleted file mode 100644
index 51cb040..0000000
--- a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.ruoyi.procurementrecord.dto;
-
-import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-@Data
-public class ReturnSaleProductDto extends ReturnSaleProduct {
-
- private String productName;
-
- private String model;
-
- private String unit;
-
- //鏈��璐ф暟閲�
- private BigDecimal unQuantity;
-
- private BigDecimal totalReturnNum;
-
- // 閫�璐ф�讳环
- private BigDecimal price;
-
- // 閫�璐ф�讳环
- private BigDecimal taxInclusiveUnitPrice;
-}
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java
index b41cf62..f37f6d8 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java
@@ -3,9 +3,9 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.procurementrecord.dto.ProcurementDto;
-import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
-import com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import org.apache.ibatis.annotations.Param;
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
index eebd7cc..a1e003e 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
@@ -3,7 +3,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import org.apache.ibatis.annotations.Param;
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
index 5c6504b..815559c 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
@@ -5,7 +5,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.bean.dto.SalesReturnDto;
import com.ruoyi.account.bean.vo.SalesReturnVo;
-import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
import org.apache.ibatis.annotations.Param;
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java
index 879a074..d185189 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.procurementrecord.mapper;
-import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
diff --git a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
index f5d8c79..4e8e3fb 100644
--- a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
+++ b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
@@ -8,7 +8,6 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
-import java.util.Date;
/**
* @author :yys
@@ -64,10 +63,12 @@
@Schema(description = "鍒涘缓鏃堕棿")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(description = "鏇存柊鏃堕棿")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Schema(description = "鍒涘缓鐢ㄦ埛")
diff --git a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
index b7b0efa..2afeaa4 100644
--- a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
+++ b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
@@ -1,15 +1,12 @@
package com.ruoyi.procurementrecord.pojo;
-import com.baomidou.mybatisplus.annotation.FieldFill;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import java.io.Serializable;
-import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
/**
* <p>
@@ -37,8 +34,8 @@
@Schema(description = "閫�璐у崟id")
private Long returnManagementId;
- @Schema(description = "閫�璐т骇鍝乮d")
- private Long returnsalesLedgerProductId;
+ @Schema(description = "鍏宠仈鍑哄簱鍗昳d")
+ private Long stockOutRecordId;
@Schema(description = "閫�璐т骇鍝佹暟閲�")
private BigDecimal num;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
index 202ce88..803c03f 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
@@ -3,9 +3,9 @@
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.procurementrecord.dto.ProcurementRecordOutAdd;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
-import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutAdd;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import jakarta.servlet.http.HttpServletResponse;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java
index da23205..a209cf9 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java
@@ -5,7 +5,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.procurementrecord.dto.*;
+import com.ruoyi.procurementrecord.bean.dto.*;
import com.ruoyi.procurementrecord.pojo.CustomStorage;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java b/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
index 1a2de96..4e7555a 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
@@ -3,9 +3,9 @@
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.procurementrecord.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.bean.vo.ShippingInfoVo;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
-import com.ruoyi.sales.dto.SalesLedgerDto;
/**
* @author :yys
@@ -26,7 +26,7 @@
boolean updateReturnManagementDto(ReturnManagementDto returnManagementDto);
- SalesLedgerDto getReturnManagementDtoByShippingIdId(Long shippingId);
+ ShippingInfoVo getReturnManagementDtoByShippingIdId(Long shippingId);
boolean handle(Long returnManagementId);
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java b/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java
index 07c080f..d16b527 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java
@@ -1,6 +1,6 @@
package com.ruoyi.procurementrecord.service;
-import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.baomidou.mybatisplus.extension.service.IService;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
index 8b6b33d..8b73f4a 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
@@ -5,9 +5,9 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutAdd;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
-import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutAdd;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
+import com.ruoyi.procurementrecord.bean.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java
index 8a3c4ba..1d99dec 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java
@@ -12,7 +12,7 @@
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.procurementrecord.dto.*;
+import com.ruoyi.procurementrecord.bean.dto.*;
import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
index e34ca05..b4701f6 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -10,18 +10,17 @@
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
-import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.bean.vo.ShippingInfoVo;
+import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.ruoyi.procurementrecord.service.ReturnManagementService;
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
import com.ruoyi.procurementrecord.utils.StockUtils;
-import com.ruoyi.sales.dto.SalesLedgerDto;
-import com.ruoyi.sales.dto.SalesLedgerProductDto;
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;
@@ -32,7 +31,6 @@
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -76,28 +74,28 @@
@Override
public boolean updateReturnManagementDto(ReturnManagementDto returnManagementDto) {
- List<ReturnSaleProduct> returnSaleProducts = new ArrayList<>();
if (!CollectionUtils.isEmpty(returnManagementDto.getReturnSaleProducts())) {
returnManagementDto.getReturnSaleProducts().stream().forEach(returnSaleProductDto -> {
ReturnSaleProduct returnSaleProduct = new ReturnSaleProduct();
BeanUtils.copyProperties(returnSaleProductDto, returnSaleProduct);
- returnSaleProducts.add(returnSaleProduct);
+ if (returnSaleProductDto.getId() == null){
+ returnSaleProduct.setReturnManagementId(returnManagementDto.getId());
+ returnSaleProduct.setStatus(0);
+ returnSaleProductService.save(returnSaleProduct);
+ }else returnSaleProductService.updateById(returnSaleProduct);
});
}
- returnSaleProductService.updateBatchById(returnSaleProducts);
return updateById(returnManagementDto);
}
@Override
- public SalesLedgerDto getReturnManagementDtoByShippingIdId(Long shippingId) {
+ public ShippingInfoVo getReturnManagementDtoByShippingIdId(Long shippingId) {
ShippingInfo byId = shippingInfoService.getById(shippingId);
- SalesLedger salesLedger = salesLedgerMapper.selectById(byId.getSalesLedgerId());
- SalesLedgerDto salesLedgerDto = new SalesLedgerDto();
- BeanUtils.copyProperties(salesLedger, salesLedgerDto);
-
- List<SalesLedgerProductDto> salesLedgerProductDtos = shippingInfoService.getReturnManagementDtoById(byId.getId());
- salesLedgerDto.setProductDtoData(salesLedgerProductDtos);
- return salesLedgerDto;
+ ShippingInfoVo shippingInfoVo = new ShippingInfoVo();
+ shippingInfoVo.setShippingInfo(byId);
+ List<ShippingProductVo> shippingProductVos = shippingInfoService.getReturnManagementDtoById(byId.getId());
+ shippingInfoVo.setShippingProductVoList(shippingProductVos);
+ return shippingInfoVo;
}
@Override
@@ -105,6 +103,7 @@
ReturnManagement byId = this.getById(returnManagementId);
List<ReturnSaleProductDto> list = returnSaleProductService.listReturnSaleProduct(returnManagementId);
byId.setStatus(1);
+ byId.setSettler(SecurityUtils.getLoginUser().getNickName());
updateById(byId);
SalesRefundAmountOrderDto salesRefundAmountOrder = new SalesRefundAmountOrderDto();
salesRefundAmountOrder.setReturnManagementId(returnManagementId);
@@ -117,11 +116,11 @@
salesRefundAmountOrder.setRefundedAmount(new BigDecimal(0));
// 鏄惁鏈夎川閲忛棶棰�
if (returnSaleProduct.getIsQuality() == 1) {
- // 鏈夎川閲忛棶棰橈紝鍏ヤ笉鍚堟牸搴�
- stockUtils.addUnStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId());
+ // 鏈夎川閲忛棶棰橈紝鍏ヤ笉鍚堟牸搴�(甯︽壒娆�)
+ stockUtils.addUnStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
}else{
- // 鏃犺川閲忛棶棰橈紝鍏ュ悎鏍煎簱
- stockUtils.addStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId());
+ // 鏃犺川閲忛棶棰橈紝鍏ュ悎鏍煎簱(甯︽壒娆�)
+ stockUtils.addStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
}
}
salesRefundAmountOrder.setRefundAmount(bigDecimal);
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java
index 4809674..29a9ee7 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java
@@ -1,7 +1,7 @@
package com.ruoyi.procurementrecord.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto;
import com.ruoyi.procurementrecord.mapper.ReturnSaleProductMapper;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
diff --git a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
index 1434db6..0beecd2 100644
--- a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
+++ b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -3,11 +3,11 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.ruoyi.common.enums.ReviewStatusEnum;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
-import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockOutRecord;
import com.ruoyi.stock.service.StockInRecordService;
@@ -29,7 +29,6 @@
private final StockInventoryService stockInventoryService;
private final StockInRecordService stockInRecordService;
private final StockOutRecordService stockOutRecordService;
- private final StockInventoryMapper stockInventoryMapper;
/**
* 涓嶅悎鏍煎叆搴�
@@ -45,6 +44,24 @@
stockUninventoryDto.setRecordType(String.valueOf(recordType));
stockUninventoryDto.setQualitity(quantity);
stockUninventoryDto.setProductModelId(productModelId);
+ stockUninventoryService.addStockInRecordOnly(stockUninventoryDto);
+ }
+
+ /**
+ * 涓嶅悎鏍煎叆搴撳甫鎵规鍙�
+ *
+ * @param productModelId
+ * @param quantity
+ * @param recordType
+ * @param recordId
+ */
+ public void addUnStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo) {
+ StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+ stockUninventoryDto.setRecordId(recordId);
+ stockUninventoryDto.setRecordType(String.valueOf(recordType));
+ stockUninventoryDto.setQualitity(quantity);
+ stockUninventoryDto.setProductModelId(productModelId);
+ stockUninventoryDto.setBatchNo(batchNo);
stockUninventoryService.addStockInRecordOnly(stockUninventoryDto);
}
@@ -122,40 +139,38 @@
public void shipmentStatus(String recordType, Long recordId) {
LambdaQueryWrapper<StockOutRecord> queryWrapper = new LambdaQueryWrapper<StockOutRecord>().eq(StockOutRecord::getRecordType, recordType)
.eq(StockOutRecord::getRecordId, recordId);
- StockOutRecord stockOutRecord = stockOutRecordService.getOne(queryWrapper);
- stockOutRecord.setApprovalStatus(0);
- stockOutRecordService.updateById(stockOutRecord);
+ stockOutRecordService.list(queryWrapper).stream().forEach(stockOutRecord -> {
+ stockOutRecord.setApprovalStatus(0);
+ stockOutRecordService.updateById(stockOutRecord);
+ });
}
//涓嶅悎鏍煎簱瀛樺垹闄�
public void deleteStockInRecord(Long recordId, String recordType) {
StockInRecord one = stockInRecordService.getOne(new QueryWrapper<StockInRecord>()
.lambda().eq(StockInRecord::getRecordId, recordId)
- .eq(StockInRecord::getRecordType, recordType));
+ .eq(StockInRecord::getRecordType, recordType), false);
if (ObjectUtils.isNotEmpty(one)) {
- stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
- //灏嗗簱瀛樺噺鍥炴潵
- StockInventoryDto stockInventoryDto = new StockInventoryDto();
- stockInventoryDto.setRecordId(recordId);
- stockInventoryDto.setRecordType(recordType);
- stockInventoryDto.setQualitity(one.getStockInNum());
- stockInventoryMapper.updateSubtractStockInventory((stockInventoryDto));
+ if (ReviewStatusEnum.APPROVED.getCode().equals(one.getApprovalStatus())) {
+ stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
+ } else {
+ stockInRecordService.removeById(one.getId());
+ }
}
}
+ //鍒犻櫎鍑哄簱璁板綍
public void deleteStockOutRecord(Long recordId, String recordType) {
StockOutRecord one = stockOutRecordService.getOne(new QueryWrapper<StockOutRecord>()
.lambda().eq(StockOutRecord::getRecordId, recordId)
- .eq(StockOutRecord::getRecordType, recordType));
+ .eq(StockOutRecord::getRecordType, recordType), false);
if (ObjectUtils.isNotEmpty(one)) {
- stockOutRecordService.batchDelete(Collections.singletonList(one.getId()));
- //灏嗗簱瀛樺姞鍥炴潵
- StockInventoryDto stockInventoryDto = new StockInventoryDto();
- stockInventoryDto.setRecordId(recordId);
- stockInventoryDto.setRecordType(recordType);
- stockInventoryDto.setQualitity(one.getStockOutNum());
- stockInventoryMapper.updateAddStockInventory((stockInventoryDto));
+ if (ReviewStatusEnum.APPROVED.getCode().equals(one.getApprovalStatus())) {
+ stockOutRecordService.batchDelete(Collections.singletonList(one.getId()));
+ } else {
+ stockOutRecordService.removeById(one.getId());
+ }
}
}
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionAccount.java b/src/main/java/com/ruoyi/production/pojo/ProductionAccount.java
index e205db2..c53f138 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionAccount.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionAccount.java
@@ -27,12 +27,6 @@
@TableId(value = "id", type = IdType.AUTO)
private Long id;
- @Schema(description = "閿�鍞彴璐d")
- private Long salesLedgerId;
-
- @Schema(description = "閿�鍞骇鍝佽鏍糹d")
- private Long salesLedgerProductId;
-
@Schema(description = "鎶ュ伐琛╥d")
private Long productionProductMainId;
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java b/src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java
index 9c6e9d1..5171aa7 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java
@@ -14,9 +14,6 @@
@TableId(type = IdType.AUTO)
private Long id;
- @Schema(description = "鎶ュ伐id")
- private Long productMainId;
-
@Schema(description = "鐢熶骇鎶ュ伐涓昏〃id")
private Long productionProductMainId;
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java b/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
index 5e1daef..40f9a41 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
@@ -14,9 +14,6 @@
@TableId(type = IdType.AUTO)
private Long id;
- @Schema(description = "鎶ュ伐id")
- private Long productMainId;
-
@Schema(description = "鐢熶骇鎶ュ伐涓昏〃id")
private Long productionProductMainId;
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
index 35cdb27..31cdc79 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -1,18 +1,44 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.production.bean.dto.ProductionBomStructureDto;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.mapper.ProductionBomStructureMapper;
+import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
+import com.ruoyi.production.mapper.ProductionOrderBomMapper;
+import com.ruoyi.production.mapper.ProductionOrderMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.ProductionBomStructure;
+import com.ruoyi.production.pojo.ProductionOperationTask;
+import com.ruoyi.production.pojo.ProductionOrder;
+import com.ruoyi.production.pojo.ProductionOrderBom;
+import com.ruoyi.production.pojo.ProductionOrderRouting;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
+import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.service.ProductionBomStructureService;
+import com.ruoyi.technology.mapper.TechnologyOperationMapper;
+import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
+import com.ruoyi.technology.mapper.TechnologyParamMapper;
+import com.ruoyi.technology.pojo.TechnologyOperation;
+import com.ruoyi.technology.pojo.TechnologyOperationParam;
+import com.ruoyi.technology.pojo.TechnologyParam;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.*;
+import java.util.stream.Collectors;
/**
* <p>
@@ -26,7 +52,17 @@
@RequiredArgsConstructor()
public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
- private final ProductionBomStructureMapper productionBomStructureMapper;
+ private final ProductionBomStructureMapper productionBomStructureMapper;
+ private final ProductionOrderBomMapper productionOrderBomMapper;
+ private final ProductionOrderMapper productionOrderMapper;
+ private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
+ private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+ private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
+ private final ProductionOperationTaskMapper productionOperationTaskMapper;
+ private final ProductionProductMainMapper productionProductMainMapper;
+ private final TechnologyOperationMapper technologyOperationMapper;
+ private final TechnologyOperationParamMapper technologyOperationParamMapper;
+ private final TechnologyParamMapper technologyParamMapper;
/**
* 鏍规嵁BOM鏌ヨ骞剁粍瑁呯粨鏋勬爲銆�
@@ -136,9 +172,569 @@
if (!updateList.isEmpty()) {
this.updateBatchById(updateList);
}
+ syncDemandedQuantityAndTaskPlanQuantity(orderBomId, dto.getProductionOrderId());
return true;
}
+ private void syncDemandedQuantityAndTaskPlanQuantity(Long orderBomId, Long productionOrderId) {
+ if (orderBomId == null) {
+ return;
+ }
+ ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
+ if (orderBom == null) {
+ return;
+ }
+ Long currentProductionOrderId = productionOrderId != null ? productionOrderId : orderBom.getProductionOrderId();
+ if (currentProductionOrderId == null) {
+ return;
+ }
+ ProductionOrder productionOrder = productionOrderMapper.selectById(currentProductionOrderId);
+ if (productionOrder == null) {
+ return;
+ }
+
+ BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity());
+ List<ProductionBomStructure> structureList = this.list(
+ Wrappers.<ProductionBomStructure>lambdaQuery()
+ .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)
+ .orderByAsc(ProductionBomStructure::getId));
+ //鍚屾闇�姹傛暟閲�
+ syncStructureDemandedQuantity(structureList, orderQuantity);
+ Long rootProductModelId = orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId();
+ //鍚屾鐢熶骇宸ヨ壓璺嚎
+ syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId);
+ //鍚屾宸ュ崟
+ syncTaskPlanQuantity(
+ currentProductionOrderId,
+ structureList,
+ orderQuantity,
+ rootProductModelId);
+ }
+
+ private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) {
+ if (structureList == null || structureList.isEmpty()) {
+ return;
+ }
+ List<ProductionBomStructure> updateList = new ArrayList<>();
+ BigDecimal lastProcessDemandedQuantity = orderQuantity;
+ for (ProductionBomStructure structure : structureList) {
+ if (structure == null || structure.getId() == null) {
+ continue;
+ }
+
+ BigDecimal demandedQuantity = lastProcessDemandedQuantity.multiply(defaultDecimal(structure.getUnitQuantity()));
+// if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
+// continue;
+// }
+ ProductionBomStructure update = new ProductionBomStructure();
+ update.setId(structure.getId());
+ update.setDemandedQuantity(demandedQuantity);
+ updateList.add(update);
+ structure.setDemandedQuantity(demandedQuantity);
+ lastProcessDemandedQuantity = demandedQuantity;
+ }
+ if (!updateList.isEmpty()) {
+ this.updateBatchById(updateList);
+ }
+ }
+
+ private void syncTaskPlanQuantity(Long productionOrderId,
+ List<ProductionBomStructure> structureList,
+ BigDecimal orderQuantity,
+ Long rootProductModelId) {
+ List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
+ .orderByAsc(ProductionOperationTask::getId));
+ if (taskList == null || taskList.isEmpty()) {
+ return;
+ }
+ Set<Long> routingOperationIds = taskList.stream()
+ .map(ProductionOperationTask::getProductionOrderRoutingOperationId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ if (routingOperationIds.isEmpty()) {
+ return;
+ }
+ Map<Long, ProductionOrderRoutingOperation> routingOperationMap = productionOrderRoutingOperationMapper
+ .selectBatchIds(routingOperationIds)
+ .stream()
+ .filter(item -> item != null && item.getId() != null)
+ .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left));
+ // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation.
+ Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId);
+ for (ProductionOperationTask task : taskList) {
+ if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
+ continue;
+ }
+ ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId());
+ if (routingOperation == null) {
+ continue;
+ }
+ BigDecimal planQuantity = resolveTaskPlanQuantity(
+ routingOperation,
+ demandedQuantityMap,
+ orderQuantity,
+ rootProductModelId);
+ if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
+ continue;
+ }
+ ProductionOperationTask update = new ProductionOperationTask();
+ update.setId(task.getId());
+ update.setPlanQuantity(planQuantity);
+ productionOperationTaskMapper.updateById(update);
+ }
+ }
+
+ private void syncRoutingOperationsByBom(Long productionOrderId,
+ ProductionOrder productionOrder,
+ ProductionOrderBom orderBom,
+ List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ ProductionOrderRouting orderRouting = getOrCreateOrderRoutingSnapshot(productionOrderId, productionOrder, orderBom, rootProductModelId);
+ List<ProductionOrderRoutingOperation> desiredOperationList = buildDesiredRoutingOperationList(structureList, rootProductModelId);
+ List<ProductionOrderRoutingOperation> existingOperationList = productionOrderRoutingOperationMapper.selectList(
+ Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
+ .eq(ProductionOrderRoutingOperation::getOrderRoutingId, orderRouting.getId())
+ .eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId)
+ .orderByAsc(ProductionOrderRoutingOperation::getDragSort)
+ .orderByAsc(ProductionOrderRoutingOperation::getId));
+ Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = buildExistingRoutingOperationBucketMap(existingOperationList);
+ List<ProductionOrderRoutingOperation> finalOperationList = new ArrayList<>();
+ for (ProductionOrderRoutingOperation desiredOperation : desiredOperationList) {
+ String bucketKey = buildRoutingOperationBucketKey(
+ desiredOperation.getTechnologyOperationId(),
+ desiredOperation.getProductModelId());
+ Deque<ProductionOrderRoutingOperation> matchedQueue = existingBucketMap.get(bucketKey);
+ ProductionOrderRoutingOperation matchedOperation = matchedQueue == null ? null : matchedQueue.pollFirst();
+ if (matchedOperation == null) {
+ matchedOperation = insertRoutingOperationSnapshot(orderRouting.getId(), productionOrderId, desiredOperation);
+ } else {
+ updateRoutingOperationSnapshotIfNecessary(desiredOperation, orderRouting.getId(), productionOrderId, matchedOperation);
+ }
+ finalOperationList.add(matchedOperation);
+ }
+ for (Deque<ProductionOrderRoutingOperation> queue : existingBucketMap.values()) {
+ while (queue != null && !queue.isEmpty()) {
+ removeRoutingOperationSnapshot(queue.pollFirst());
+ }
+ }
+ syncRoutingOperationTasks(productionOrderId, finalOperationList);
+ }
+
+ private ProductionOrderRouting getOrCreateOrderRoutingSnapshot(Long productionOrderId,
+ ProductionOrder productionOrder,
+ ProductionOrderBom orderBom,
+ Long rootProductModelId) {
+ ProductionOrderRouting orderRouting = productionOrderRoutingMapper.selectOne(
+ Wrappers.<ProductionOrderRouting>lambdaQuery()
+ .eq(ProductionOrderRouting::getProductionOrderId, productionOrderId)
+ .orderByDesc(ProductionOrderRouting::getId)
+ .last("limit 1"));
+ if (orderRouting == null) {
+ orderRouting = new ProductionOrderRouting();
+ orderRouting.setProductionOrderId(productionOrderId);
+ orderRouting.setProductModelId(rootProductModelId);
+ orderRouting.setTechnologyRoutingId(productionOrder == null ? null : productionOrder.getTechnologyRoutingId());
+ orderRouting.setBomId(orderBom == null ? null : orderBom.getBomId());
+ orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId());
+ productionOrderRoutingMapper.insert(orderRouting);
+ return orderRouting;
+ }
+ ProductionOrderRouting update = new ProductionOrderRouting();
+ update.setId(orderRouting.getId());
+ boolean changed = false;
+ if (!Objects.equals(orderRouting.getProductModelId(), rootProductModelId)) {
+ update.setProductModelId(rootProductModelId);
+ orderRouting.setProductModelId(rootProductModelId);
+ changed = true;
+ }
+ Long technologyRoutingId = productionOrder == null ? null : productionOrder.getTechnologyRoutingId();
+ if (!Objects.equals(orderRouting.getTechnologyRoutingId(), technologyRoutingId)) {
+ update.setTechnologyRoutingId(technologyRoutingId);
+ orderRouting.setTechnologyRoutingId(technologyRoutingId);
+ changed = true;
+ }
+ Long bomId = orderBom == null ? null : orderBom.getBomId();
+ if (!Objects.equals(orderRouting.getBomId(), bomId)) {
+ update.setBomId(bomId);
+ orderRouting.setBomId(bomId);
+ changed = true;
+ }
+ Long orderBomId = orderBom == null ? null : orderBom.getId();
+ if (!Objects.equals(orderRouting.getOrderBomId(), orderBomId)) {
+ update.setOrderBomId(orderBomId);
+ orderRouting.setOrderBomId(orderBomId);
+ changed = true;
+ }
+ if (changed) {
+ productionOrderRoutingMapper.updateById(update);
+ }
+ return orderRouting;
+ }
+
+ private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ if (structureList == null || structureList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ Map<Long, ProductionBomStructure> structureById = structureList.stream()
+ .filter(item -> item != null && item.getId() != null)
+ .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
+ Map<String, ProductionBomStructure> uniqueOperationMap = new LinkedHashMap<>();
+ for (ProductionBomStructure bomStructure : structureList) {
+ if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+ continue;
+ }
+ Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId);
+ uniqueOperationMap.putIfAbsent(buildBomOperationDedupKey(bomStructure, outputProductModelId), bomStructure);
+ }
+ List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>();
+ int dragSort = 1;
+ for (ProductionBomStructure bomStructure : uniqueOperationMap.values()) {
+ Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId);
+ TechnologyOperation technologyOperation = getTechnologyOperation(bomStructure.getTechnologyOperationId());
+ ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation();
+ routingOperation.setProductModelId(outputProductModelId);
+ routingOperation.setTechnologyOperationId(bomStructure.getTechnologyOperationId());
+ routingOperation.setOperationName(technologyOperation == null ? null : technologyOperation.getName());
+ routingOperation.setIsQuality(technologyOperation == null ? null : technologyOperation.getIsQuality());
+ routingOperation.setIsProduction(technologyOperation == null ? null : technologyOperation.getIsProduction());
+ routingOperation.setType(technologyOperation == null ? null : technologyOperation.getType());
+ routingOperation.setDragSort(dragSort++);
+ desiredOperationList.add(routingOperation);
+ }
+ return desiredOperationList;
+ }
+
+ private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) {
+ Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = new LinkedHashMap<>();
+ if (existingOperationList == null || existingOperationList.isEmpty()) {
+ return existingBucketMap;
+ }
+ for (ProductionOrderRoutingOperation routingOperation : existingOperationList) {
+ String bucketKey = buildRoutingOperationBucketKey(
+ routingOperation.getTechnologyOperationId(),
+ routingOperation.getProductModelId());
+ existingBucketMap.computeIfAbsent(bucketKey, key -> new ArrayDeque<>()).addLast(routingOperation);
+ }
+ return existingBucketMap;
+ }
+
+ private ProductionOrderRoutingOperation insertRoutingOperationSnapshot(Long orderRoutingId,
+ Long productionOrderId,
+ ProductionOrderRoutingOperation desiredOperation) {
+ ProductionOrderRoutingOperation insert = new ProductionOrderRoutingOperation();
+ insert.setOrderRoutingId(orderRoutingId);
+ insert.setProductionOrderId(productionOrderId);
+ insert.setProductModelId(desiredOperation.getProductModelId());
+ insert.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ insert.setOperationName(desiredOperation.getOperationName());
+ insert.setIsQuality(desiredOperation.getIsQuality());
+ insert.setIsProduction(desiredOperation.getIsProduction());
+ insert.setType(desiredOperation.getType());
+ insert.setDragSort(desiredOperation.getDragSort());
+ productionOrderRoutingOperationMapper.insert(insert);
+ syncRoutingOperationParams(insert.getId(), productionOrderId, insert.getTechnologyOperationId());
+ return insert;
+ }
+
+ private void updateRoutingOperationSnapshotIfNecessary(ProductionOrderRoutingOperation currentOperation,
+ Long orderRoutingId,
+ Long productionOrderId,
+ ProductionOrderRoutingOperation desiredOperation) {
+ if (currentOperation == null || currentOperation.getId() == null) {
+ return;
+ }
+ ProductionOrderRoutingOperation update = new ProductionOrderRoutingOperation();
+ update.setId(currentOperation.getId());
+ boolean changed = false;
+ if (!Objects.equals(currentOperation.getOrderRoutingId(), orderRoutingId)) {
+ update.setOrderRoutingId(orderRoutingId);
+ currentOperation.setOrderRoutingId(orderRoutingId);
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getProductionOrderId(), productionOrderId)) {
+ update.setProductionOrderId(productionOrderId);
+ currentOperation.setProductionOrderId(productionOrderId);
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getProductModelId(), desiredOperation.getProductModelId())) {
+ update.setProductModelId(desiredOperation.getProductModelId());
+ currentOperation.setProductModelId(desiredOperation.getProductModelId());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getTechnologyOperationId(), desiredOperation.getTechnologyOperationId())) {
+ update.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ currentOperation.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getOperationName(), desiredOperation.getOperationName())) {
+ update.setOperationName(desiredOperation.getOperationName());
+ currentOperation.setOperationName(desiredOperation.getOperationName());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getIsQuality(), desiredOperation.getIsQuality())) {
+ update.setIsQuality(desiredOperation.getIsQuality());
+ currentOperation.setIsQuality(desiredOperation.getIsQuality());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getIsProduction(), desiredOperation.getIsProduction())) {
+ update.setIsProduction(desiredOperation.getIsProduction());
+ currentOperation.setIsProduction(desiredOperation.getIsProduction());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getType(), desiredOperation.getType())) {
+ update.setType(desiredOperation.getType());
+ currentOperation.setType(desiredOperation.getType());
+ changed = true;
+ }
+ if (!Objects.equals(currentOperation.getDragSort(), desiredOperation.getDragSort())) {
+ update.setDragSort(desiredOperation.getDragSort());
+ currentOperation.setDragSort(desiredOperation.getDragSort());
+ changed = true;
+ }
+ if (changed) {
+ productionOrderRoutingOperationMapper.updateById(update);
+ }
+ }
+
+ private void removeRoutingOperationSnapshot(ProductionOrderRoutingOperation routingOperation) {
+ if (routingOperation == null || routingOperation.getId() == null) {
+ return;
+ }
+ ProductionOperationTask task = productionOperationTaskMapper.selectOne(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperation.getId())
+ .last("limit 1"));
+ if (task != null) {
+ validateTaskCanRemove(task);
+ productionOperationTaskMapper.deleteById(task.getId());
+ }
+ productionOrderRoutingOperationParamMapper.delete(
+ Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+ .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperation.getId()));
+ productionOrderRoutingOperationMapper.deleteById(routingOperation.getId());
+ }
+
+ private void syncRoutingOperationTasks(Long productionOrderId, List<ProductionOrderRoutingOperation> routingOperationList) {
+ if (routingOperationList == null || routingOperationList.isEmpty()) {
+ return;
+ }
+ List<Long> routingOperationIdList = routingOperationList.stream()
+ .map(ProductionOrderRoutingOperation::getId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (routingOperationIdList.isEmpty()) {
+ return;
+ }
+ Map<Long, ProductionOperationTask> taskByRoutingOperationId = productionOperationTaskMapper.selectList(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .in(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperationIdList)
+ .orderByAsc(ProductionOperationTask::getId))
+ .stream()
+ .filter(item -> item != null && item.getProductionOrderRoutingOperationId() != null)
+ .collect(Collectors.toMap(
+ ProductionOperationTask::getProductionOrderRoutingOperationId,
+ item -> item,
+ (left, right) -> left,
+ LinkedHashMap::new));
+ for (int i = 0; i < routingOperationList.size(); i++) {
+ ProductionOrderRoutingOperation routingOperation = routingOperationList.get(i);
+ if (routingOperation == null || routingOperation.getId() == null) {
+ continue;
+ }
+ boolean shouldHaveTask = i == routingOperationList.size() - 1 || Boolean.TRUE.equals(routingOperation.getIsProduction());
+ ProductionOperationTask existingTask = taskByRoutingOperationId.get(routingOperation.getId());
+ if (shouldHaveTask) {
+ if (existingTask == null) {
+ ProductionOperationTask task = new ProductionOperationTask();
+ task.setProductionOrderId(productionOrderId);
+ task.setProductionOrderRoutingOperationId(routingOperation.getId());
+ task.setPlanQuantity(BigDecimal.ZERO);
+ task.setCompleteQuantity(BigDecimal.ZERO);
+ task.setWorkOrderNo(generateNextTaskNo());
+ task.setStatus(2);
+ productionOperationTaskMapper.insert(task);
+ }
+ continue;
+ }
+ if (existingTask != null) {
+ validateTaskCanRemove(existingTask);
+ productionOperationTaskMapper.deleteById(existingTask.getId());
+ }
+ }
+ }
+
+ private void validateTaskCanRemove(ProductionOperationTask task) {
+ if (task == null || task.getId() == null) {
+ return;
+ }
+ if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) {
+ throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ簭蹇収");
+ }
+ long reportCount = productionProductMainMapper.selectCount(
+ Wrappers.<ProductionProductMain>lambdaQuery()
+ .eq(ProductionProductMain::getProductionOperationTaskId, task.getId()));
+ if (reportCount > 0) {
+ throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ崟");
+ }
+ }
+
+ private void syncRoutingOperationParams(Long routingOperationId, Long productionOrderId, Long technologyOperationId) {
+ if (routingOperationId == null || technologyOperationId == null) {
+ return;
+ }
+ List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList(
+ Wrappers.<TechnologyOperationParam>lambdaQuery()
+ .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId)
+ .orderByAsc(TechnologyOperationParam::getId));
+ for (TechnologyOperationParam operationParam : operationParamList) {
+ TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId());
+ if (technologyParam == null) {
+ continue;
+ }
+ ProductionOrderRoutingOperationParam snapshot = new ProductionOrderRoutingOperationParam();
+ snapshot.setProductionOrderId(productionOrderId);
+ snapshot.setProductionOrderRoutingOperationId(routingOperationId);
+ snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId());
+ snapshot.setTechnologyOperationParamId(operationParam.getId());
+ snapshot.setParamId(technologyParam.getId());
+ snapshot.setParamCode(technologyParam.getParamCode());
+ snapshot.setParamName(technologyParam.getParamName());
+ snapshot.setParamType(technologyParam.getParamType());
+ snapshot.setParamFormat(technologyParam.getParamFormat());
+ snapshot.setUnit(technologyParam.getUnit());
+ snapshot.setIsRequired(technologyParam.getIsRequired());
+ snapshot.setRemark(technologyParam.getRemark());
+ snapshot.setStandardValue(operationParam.getStandardValue());
+ productionOrderRoutingOperationParamMapper.insert(snapshot);
+ }
+ }
+
+ private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList,
+ Long rootProductModelId) {
+ if (structureList == null || structureList.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<Long, ProductionBomStructure> structureById = structureList.stream()
+ .filter(item -> item != null && item.getId() != null)
+ .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
+ Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
+ Set<String> mergedOutputNodeKeySet = new HashSet<>();
+ for (ProductionBomStructure bomStructure : structureList) {
+ if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+ continue;
+ }
+ // Resolve the output node first, then read the output node demand for the task plan quantity.
+ ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
+ Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
+ if (outputProductModelId == null) {
+ continue;
+ }
+ String mergedOutputNodeKey = buildOperationOutputNodeKey(
+ bomStructure.getTechnologyOperationId(),
+ outputNode == null ? null : outputNode.getId(),
+ outputProductModelId);
+ if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
+ continue;
+ }
+ // Multiple input rows can point to the same output node, so only count that output demand once.
+ String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+ demandedQuantityMap.merge(key, defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()), BigDecimal::add);
+ }
+ return demandedQuantityMap;
+ }
+
+ private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
+ Map<String, BigDecimal> demandedQuantityMap,
+ BigDecimal orderQuantity,
+ Long rootProductModelId) {
+ if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
+ return orderQuantity;
+ }
+ Long outputProductModelId = routingOperation.getProductModelId() != null
+ ? routingOperation.getProductModelId()
+ : rootProductModelId;
+ String key = buildOperationDemandedQuantityKey(
+ routingOperation.getTechnologyOperationId(),
+ outputProductModelId);
+ BigDecimal planQuantity = demandedQuantityMap.get(key);
+ return planQuantity != null ? planQuantity : orderQuantity;
+ }
+
+ private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private String buildBomOperationDedupKey(ProductionBomStructure bomStructure, Long outputProductModelId) {
+ Long operationId = bomStructure == null ? null : bomStructure.getTechnologyOperationId();
+ Long parentId = bomStructure == null ? null : bomStructure.getParentId();
+ return operationId + "#" + outputProductModelId + "#" + parentId;
+ }
+
+ private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
+ Map<Long, ProductionBomStructure> structureById) {
+ if (bomStructure == null) {
+ return null;
+ }
+ // The root node is the first output node; other rows use their direct parent as the current operation output.
+ if (bomStructure.getParentId() == null) {
+ return bomStructure;
+ }
+ ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
+ return parent != null ? parent : bomStructure;
+ }
+
+ private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
+ Long rootProductModelId) {
+ if (outputNode == null) {
+ return rootProductModelId;
+ }
+ return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+ }
+
+ private TechnologyOperation getTechnologyOperation(Long technologyOperationId) {
+ if (technologyOperationId == null) {
+ return null;
+ }
+ return technologyOperationMapper.selectById(technologyOperationId);
+ }
+
+ private String generateNextTaskNo() {
+ String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+ ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne(
+ Wrappers.<ProductionOperationTask>lambdaQuery()
+ .likeRight(ProductionOperationTask::getWorkOrderNo, "GD" + datePrefix)
+ .orderByDesc(ProductionOperationTask::getWorkOrderNo)
+ .last("limit 1"));
+ int sequenceNumber = 1;
+ if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith("GD" + datePrefix)) {
+ try {
+ sequenceNumber = Integer.parseInt(latestTask.getWorkOrderNo().substring(("GD" + datePrefix).length())) + 1;
+ } catch (NumberFormatException ignored) {
+ sequenceNumber = 1;
+ }
+ }
+ return "GD" + String.format("%s%03d", datePrefix, sequenceNumber);
+ }
+
+ private BigDecimal defaultDecimal(BigDecimal value) {
+ return value == null ? BigDecimal.ZERO : value;
+ }
+
+ private int compareDecimal(BigDecimal left, BigDecimal right) {
+ return defaultDecimal(left).compareTo(defaultDecimal(right));
+ }
+
/**
* 灏嗘爲褰㈢粨鏋勬媿骞虫垚鍒楄〃锛屼究浜庣粺涓�淇濆瓨銆�
*/
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
index 6e9457d..bd6f2df 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
@@ -23,9 +23,11 @@
import com.ruoyi.production.bean.dto.ProductionOperationTaskDto;
import com.ruoyi.production.bean.vo.ProductionOperationTaskVo;
import com.ruoyi.production.mapper.ProductionOrderMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOperationTask;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.service.ProductionOperationTaskService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
@@ -48,6 +50,7 @@
private final SysUserMapper sysUserMapper;
private final ProductionOrderMapper productionOrderMapper;
+ private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
private final FileUtil fileUtil;
@@ -61,6 +64,7 @@
// 鍒嗛〉鏌ヨ鐢熶骇宸ュ簭浠诲姟
Page<ProductionOperationTaskVo> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
IPage<ProductionOperationTaskVo> result = baseMapper.pageProductionOperationTask(voPage, dto);
+ fillOperationTypes(result.getRecords());
fillUserNames(result.getRecords());
return result;
}
@@ -69,6 +73,7 @@
public List<ProductionOperationTaskVo> listProductionOperationTask(ProductionOperationTaskDto dto) {
// 鏌ヨ宸ュ簭浠诲姟鍒楄〃
List<ProductionOperationTaskVo> result = BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOperationTaskVo.class);
+ fillOperationTypes(result);
fillUserNames(result);
return result;
}
@@ -81,6 +86,7 @@
return null;
}
ProductionOperationTaskVo vo = BeanUtil.copyProperties(item, ProductionOperationTaskVo.class);
+ fillOperationTypes(Collections.singletonList(vo));
if (item.getProductionOrderId() != null) {
ProductionOrder productionOrder = productionOrderMapper.selectById(item.getProductionOrderId());
if (productionOrder != null) {
@@ -370,6 +376,38 @@
@Override
public List<ProductionOperationTaskVo> getOperation(ProductionOperationTaskDto dto) {
// 鏌ヨ宸ュ簭浠诲姟鍒楄〃
- return baseMapper.getOperation(dto);
+ List<ProductionOperationTaskVo> result = baseMapper.getOperation(dto);
+ fillOperationTypes(result);
+ return result;
+ }
+
+ private void fillOperationTypes(List<ProductionOperationTaskVo> voList) {
+ // 鍥炲~宸ュ簭绫诲瀷锛�0 璁℃椂 / 1 璁′欢锛�
+ if (voList == null || voList.isEmpty()) {
+ return;
+ }
+ Set<Long> operationIds = voList.stream()
+ .filter(Objects::nonNull)
+ .map(ProductionOperationTaskVo::getProductionOrderRoutingOperationId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ if (operationIds.isEmpty()) {
+ return;
+ }
+ Map<Long, Integer> typeByOperationId = productionOrderRoutingOperationMapper
+ .selectBatchIds(new ArrayList<>(operationIds))
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toMap(
+ ProductionOrderRoutingOperation::getId,
+ ProductionOrderRoutingOperation::getType,
+ (left, right) -> left
+ ));
+ for (ProductionOperationTaskVo vo : voList) {
+ if (vo == null || vo.getType() != null || vo.getProductionOrderRoutingOperationId() == null) {
+ continue;
+ }
+ vo.setType(typeByOperationId.get(vo.getProductionOrderRoutingOperationId()));
+ }
}
}
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
index 344800c..ecdb37c 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -245,6 +245,18 @@
.eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())
.orderByDesc(TechnologyRoutingOperation::getDragSort)
.orderByDesc(TechnologyRoutingOperation::getId));
+ // Build task plan quantities from order BOM snapshot demand instead of recomputing from technology BOM units.
+ Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null
+ ? orderBom.getProductModelId()
+ : productionOrder.getProductModelId();
+ List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null
+ ? Collections.emptyList()
+ : productionBomStructureMapper.selectList(
+ Wrappers.<ProductionBomStructure>lambdaQuery()
+ .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
+ .orderByAsc(ProductionBomStructure::getId));
+ Map<String, BigDecimal> operationDemandedQuantityMap =
+ buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
// 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
routingOperations.stream()
@@ -278,7 +290,11 @@
ProductionOperationTask task = new ProductionOperationTask();
task.setProductionOrderRoutingOperationId(targetOperation.getId());
task.setProductionOrderId(productionOrder.getId());
- task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity()));
+ task.setPlanQuantity(resolveTaskPlanQuantity(
+ sourceOperation,
+ operationDemandedQuantityMap,
+ productionOrder,
+ rootProductModelId));
task.setCompleteQuantity(BigDecimal.ZERO);
task.setWorkOrderNo(generateNextTaskNo());
task.setStatus(2);
@@ -313,6 +329,85 @@
return syncedParamCount;
}
+ private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures,
+ Long rootProductModelId) {
+ if (bomStructures == null || bomStructures.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<Long, ProductionBomStructure> structureById = bomStructures.stream()
+ .filter(item -> item != null && item.getId() != null)
+ .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
+ Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
+ Set<String> mergedOutputNodeKeySet = new HashSet<>();
+ for (ProductionBomStructure bomStructure : bomStructures) {
+ if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+ continue;
+ }
+ // The BOM row points to the producing operation; task quantity should come from that operation's output node.
+ ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
+ Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
+ if (outputProductModelId == null) {
+ continue;
+ }
+ String mergedOutputNodeKey = buildOperationOutputNodeKey(
+ bomStructure.getTechnologyOperationId(),
+ outputNode == null ? null : outputNode.getId(),
+ outputProductModelId);
+ if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
+ continue;
+ }
+ // demandedQuantity is already the order-level required output quantity for the current output node.
+ BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity());
+ String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+ demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add);
+ }
+ return demandedQuantityMap;
+ }
+
+ private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation,
+ Map<String, BigDecimal> operationDemandedQuantityMap,
+ ProductionOrder productionOrder,
+ Long rootProductModelId) {
+ if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
+ return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+ }
+ Long outputProductModelId = sourceOperation.getProductModelId() != null
+ ? sourceOperation.getProductModelId()
+ : rootProductModelId;
+ String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId);
+ BigDecimal planQuantity = operationDemandedQuantityMap.get(key);
+ return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+ }
+
+ private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
+ return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
+ }
+
+ private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
+ Map<Long, ProductionBomStructure> structureById) {
+ if (bomStructure == null) {
+ return null;
+ }
+ // The root node is the first output node; child rows use their direct parent as the current operation output.
+ if (bomStructure.getParentId() == null) {
+ return bomStructure;
+ }
+ ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
+ return parent != null ? parent : bomStructure;
+ }
+
+ private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
+ Long rootProductModelId) {
+ if (outputNode == null) {
+ return rootProductModelId;
+ }
+ return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+ }
+
private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
// 鍚屾璁㈠崟BOM蹇収缁撴瀯
if (technologyRouting.getBomId() == null) {
@@ -342,6 +437,7 @@
productionOrderBomMapper.insert(orderBom);
Map<Long, Long> idMap = new HashMap<>();
+ BigDecimal lastProcessDemandedQuantity = orderQuantity;
for (TechnologyBomStructure source : structureList) {
// 瀛愯妭鐐� parentId 闇�瑕佹槧灏勬垚鏂板揩鐓ц妭鐐� id锛屾墠鑳戒繚鐣欏師濮� BOM 灞傜骇銆�
ProductionBomStructure target = new ProductionBomStructure();
@@ -351,10 +447,11 @@
target.setProductModelId(source.getProductModelId());
target.setTechnologyOperationId(source.getOperationId());
target.setUnitQuantity(source.getUnitQuantity());
- target.setDemandedQuantity(source.getUnitQuantity().multiply(orderQuantity));
+ target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity()));
target.setUnit(source.getUnit());
productionBomStructureMapper.insert(target);
idMap.put(source.getId(), target.getId());
+ lastProcessDemandedQuantity = target.getDemandedQuantity();
}
return orderBom;
}
@@ -788,12 +885,10 @@
if (reportOutput == null) {
continue;
}
- Long reportMainId = reportOutput.getProductionProductMainId() != null
- ? reportOutput.getProductionProductMainId()
- : reportOutput.getProductMainId();
- if (reportMainId == null) {
+ if (reportOutput.getProductionProductMainId() == null) {
continue;
}
+ Long reportMainId = reportOutput.getProductionProductMainId();
reportOutputMap.computeIfAbsent(reportMainId, k -> new ArrayList<>()).add(reportOutput);
}
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index 20f4bfc..2e1e578 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -26,6 +26,8 @@
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.quality.mapper.*;
import com.ruoyi.quality.pojo.*;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
@@ -77,6 +79,7 @@
private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
private final TechnologyOperationMapper technologyOperationMapper;
private final StockUtils stockUtils;
+ private final StockInRecordService stockInRecordService;
private final StockInventoryService stockInventoryService;
@Override
@@ -291,7 +294,6 @@
// 褰撳墠瀹炵幇鎸夊伐搴忔垚鍝佺洿鎺ヤ綔涓烘姇鍏ワ紝鍚庣画鑻ユ帴鍏ラ鏂欒褰曞彲鍦ㄨ繖閲屾浛鎹㈡潵婧愩��
ProductionProductInput productionProductInput = new ProductionProductInput();
productionProductInput.setProductionProductMainId(productionProductMain.getId());
- productionProductInput.setProductMainId(productionProductMain.getId());
productionProductInput.setProductModelId(item.getProductModelId());
productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity())));
productionProductInput.setQuantity(productionProductInput.getInputQuantity());
@@ -300,13 +302,14 @@
ProductionProductOutput productionProductOutput = new ProductionProductOutput();
productionProductOutput.setProductionProductMainId(productionProductMain.getId());
- productionProductOutput.setProductMainId(productionProductMain.getId());
productionProductOutput.setProductModelId(productModel.getId());
productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity()));
productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty()));
productionProductOutputMapper.insert(productionProductOutput);
BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity());
+ BigDecimal scrapQty = defaultDecimal(productionProductOutput.getScrapQty());
BigDecimal productQty = reportQty;
+ String qualifiedBatchNo = null;
List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
@@ -352,6 +355,11 @@
stockInventoryDto.setQualitity(productQty);
stockInventoryDto.setProductModelId(productModel.getId());
stockInventoryService.addStockInRecordOnly(stockInventoryDto);
+ qualifiedBatchNo = resolveLatestStockInBatchNo(
+ productionProductMain.getId(),
+ StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+ productModel.getId(),
+ "0");
}
productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty));
@@ -398,9 +406,39 @@
productionAccount.setSchedulingDate(LocalDateTime.now());
productionAccountMapper.insert(productionAccount);
}
+ if (scrapQty.compareTo(BigDecimal.ZERO) > 0) {
+ stockUtils.addUnStockWithBatchNo(
+ productModel.getId(),
+ scrapQty,
+ StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(),
+ productionProductMain.getId(),
+ qualifiedBatchNo);
+ }
return true;
}
+ private String resolveLatestStockInBatchNo(Long recordId,
+ String recordType,
+ Long productModelId,
+ String stockType) {
+ if (recordId == null || productModelId == null) {
+ return null;
+ }
+ StockInRecord stockInRecord = stockInRecordService.getOne(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .eq(StockInRecord::getRecordId, recordId)
+ .eq(StockInRecord::getRecordType, recordType)
+ .eq(StockInRecord::getProductModelId, productModelId)
+ .eq(StockInRecord::getType, stockType)
+ .orderByDesc(StockInRecord::getId)
+ .last("limit 1"),
+ false);
+ if (stockInRecord == null) {
+ throw new ServiceException("鏈壘鍒板搴旂殑鍏ュ簱鐢宠璁板綍");
+ }
+ return stockInRecord.getBatchNo();
+ }
+
private void syncOperationParamInputValue(ProductionProductMainDto dto,
Long productionOrderRoutingOperationId,
Long productionProductMainId) {
diff --git a/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java b/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java
index e08c368..e5c5bfa 100644
--- a/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java
+++ b/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java
@@ -12,10 +12,10 @@
import com.ruoyi.project.system.domain.SysMenu;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.domain.vo.SysUserDeptVo;
-import com.ruoyi.project.system.mapper.SysDeptMapper;
-import com.ruoyi.project.system.service.ISysMenuService;
-import com.ruoyi.project.system.service.ISysUserDeptService;
-import com.ruoyi.project.system.service.ISysUserService;
+import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.service.ISysMenuService;
+import com.ruoyi.project.system.service.ISysUserDeptService;
+import com.ruoyi.project.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
@@ -41,11 +41,11 @@
{
private SysLoginService loginService;
private ISysMenuService menuService;
- private SysPermissionService permissionService;
- private TokenService tokenService;
- private ISysUserDeptService userDeptService;
- private ISysUserService userService;
- private SysDeptMapper sysDeptMapper;
+ private SysPermissionService permissionService;
+ private TokenService tokenService;
+ private ISysUserDeptService userDeptService;
+ private ISysUserService userService;
+ private SysDeptMapper sysDeptMapper;
/**
* 鐧诲綍鏂规硶
@@ -73,17 +73,7 @@
public AjaxResult getInfo()
{
LoginUser loginUser = SecurityUtils.getLoginUser();
- SysUser user = userService.selectUserById(loginUser.getUserId());
- if (user == null)
- {
- user = loginUser.getUser();
- }
- else
- {
- loginUser.setUser(user);
- loginUser.setAiEnabled(user.getAiEnabled());
- tokenService.setLoginUser(loginUser);
- }
+ SysUser user = loginUser.getUser();
// 鑾峰彇褰撳墠鐧诲綍鍏徃
Long tenantId = loginUser.getTenantId();
if(null != tenantId){
@@ -102,12 +92,12 @@
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
- AjaxResult ajax = AjaxResult.success();
- ajax.put("user", user);
- ajax.put("aiEnabled", loginUser.getAiEnabled());
- ajax.put("roles", roles);
- ajax.put("permissions", permissions);
- return ajax;
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("user", user);
+ ajax.put("aiEnabled", loginUser.getAiEnabled());
+ ajax.put("roles", roles);
+ ajax.put("permissions", permissions);
+ return ajax;
}
/**
diff --git a/src/main/java/com/ruoyi/projectManagement/service/impl/PlanServiceImpl.java b/src/main/java/com/ruoyi/projectManagement/service/impl/PlanServiceImpl.java
index fc40f40..d999843 100644
--- a/src/main/java/com/ruoyi/projectManagement/service/impl/PlanServiceImpl.java
+++ b/src/main/java/com/ruoyi/projectManagement/service/impl/PlanServiceImpl.java
@@ -7,6 +7,7 @@
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.service.CustomerFollowUpFileService;
import com.ruoyi.basic.utils.FileUtil;
@@ -53,13 +54,13 @@
@Transactional(rollbackFor = Exception.class)
public void savePlan(SavePlanVo savePlanVo) {
Plan plan = BeanUtil.copyProperties(savePlanVo, Plan.class);
- // 闄勪欢澶勭悊
- fileUtil.saveStorageAttachmentByRecordTypeAndRecordId(null, RecordTypeEnum.PLAN, savePlanVo.getId(), savePlanVo.getStorageBlobDTOs());
if (savePlanVo.getId() == null) {
planMapper.insert(plan);
} else {
planMapper.updateById(plan);
}
+ // 闄勪欢澶勭悊
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PLAN, plan.getId(), savePlanVo.getStorageBlobDTOs());
planService.savePlanNode(plan.getId(), savePlanVo.getSavePlanNodeList());
}
diff --git a/src/main/java/com/ruoyi/projectManagement/service/impl/handle/InfoStageHandleService.java b/src/main/java/com/ruoyi/projectManagement/service/impl/handle/InfoStageHandleService.java
index 06f207d..76c7617 100644
--- a/src/main/java/com/ruoyi/projectManagement/service/impl/handle/InfoStageHandleService.java
+++ b/src/main/java/com/ruoyi/projectManagement/service/impl/handle/InfoStageHandleService.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.service.CustomerFollowUpFileService;
import com.ruoyi.basic.utils.FileUtil;
@@ -48,18 +49,12 @@
@Transactional
public void save(@NotNull SaveInfoStageVo saveInfoStageVo) {
InfoStage infoStage = BeanUtil.copyProperties(saveInfoStageVo, InfoStage.class);
- // 闄勪欢澶勭悊
- String attachmentIds = StrUtil.join(",", Optional.ofNullable(saveInfoStageVo.getAttachmentIds()).orElse(Collections.emptyList()));
- infoStage.setAttachment(attachmentIds);
-
- fileUtil.saveStorageAttachmentByRecordTypeAndRecordId(null, RecordTypeEnum.INFO_STAGE, infoStage.getProjectManagementInfoId(), saveInfoStageVo.getStorageBlobDTOs());
-
if (infoStage.getId() == null) {
infoStageMapper.insert(infoStage);
} else {
infoStageMapper.updateById(infoStage);
}
-
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.INFO_STAGE, infoStage.getId(), saveInfoStageVo.getStorageBlobDTOs());
infoStageHandleService.syncInfoStage(infoStage.getProjectManagementInfoId());
}
diff --git a/src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java b/src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java
index 16449e8..bb669b6 100644
--- a/src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java
+++ b/src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java
@@ -8,9 +8,13 @@
import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
+import com.ruoyi.purchase.vo.PurchaseStockInProductVo;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
/**
* <p>
@@ -56,5 +60,12 @@
return AjaxResult.success();
}
+ @GetMapping("/getByPurchaseLedgerId")
+ @Operation(summary = "閲囪喘閫�璐�-鏍规嵁閲囪喘璁㈠崟id鏌ヨ閲囪喘璁㈠崟瀵瑰簲鐨勫叆搴撲骇鍝佷俊鎭�")
+ public AjaxResult getByPurchaseLedgerId(Long purchaseLedgerId) {
+ List<PurchaseStockInProductVo> purchaseStockInProductVos = purchaseReturnOrdersService.getByPurchaseLedgerId(purchaseLedgerId);
+ return AjaxResult.success(purchaseStockInProductVos);
+ }
+
}
diff --git a/src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderProductsDto.java b/src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderProductsDto.java
index c89c151..928d8bf 100644
--- a/src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderProductsDto.java
+++ b/src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderProductsDto.java
@@ -9,5 +9,7 @@
private String productName;
private String model;
private String unit;
+ //鎵规鍙�
+ private String batchNo;
}
diff --git a/src/main/java/com/ruoyi/purchase/dto/SimpleReturnOrderGroupDto.java b/src/main/java/com/ruoyi/purchase/dto/SimpleReturnOrderGroupDto.java
index dc9220a..e66382e 100644
--- a/src/main/java/com/ruoyi/purchase/dto/SimpleReturnOrderGroupDto.java
+++ b/src/main/java/com/ruoyi/purchase/dto/SimpleReturnOrderGroupDto.java
@@ -16,6 +16,6 @@
@AllArgsConstructor
@NoArgsConstructor
public class SimpleReturnOrderGroupDto implements Serializable {
- private Long salesLedgerProductId;
+ private Long productModelId;
private BigDecimal sumReturnQuantity;
}
diff --git a/src/main/java/com/ruoyi/purchase/mapper/PurchaseReturnOrdersMapper.java b/src/main/java/com/ruoyi/purchase/mapper/PurchaseReturnOrdersMapper.java
index f3a5fdf..9d28354 100644
--- a/src/main/java/com/ruoyi/purchase/mapper/PurchaseReturnOrdersMapper.java
+++ b/src/main/java/com/ruoyi/purchase/mapper/PurchaseReturnOrdersMapper.java
@@ -1,16 +1,20 @@
package com.ruoyi.purchase.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.bean.dto.PurchaseReturnDto;
import com.ruoyi.account.bean.vo.PurchaseReturnVo;
import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
-import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto;
+import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
+import com.ruoyi.purchase.vo.PurchaseReturnOrderProductsDetailVo;
+import com.ruoyi.purchase.vo.PurchaseStockInProductVo;
import jakarta.validation.constraints.NotNull;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
/**
* <p>
@@ -27,4 +31,9 @@
PurchaseReturnOrderHasAllInfoDto getPurchaseReturnOrderHasAllInfoById(@Param("id") @NotNull Long id);
IPage<PurchaseReturnVo> listPageAccountPurchaseReturn(Page page, @Param("req") PurchaseReturnDto purchaseReturnDto);
+
+ //鏍规嵁閲囪喘璁㈠崟id鏌ヨ閲囪喘璁㈠崟瀵瑰簲鐨勫叆搴撲骇鍝佷俊鎭�
+ List<PurchaseStockInProductVo> getByPurchaseLedgerId(@Param("purchaseLedgerId") Long purchaseLedgerId);
+
+ List<PurchaseReturnOrderProductsDetailVo> getPurchaseReturnOrderProductsDetailById(@Param("id") Long id);
}
diff --git a/src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java b/src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java
index fdd7503..5003107 100644
--- a/src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java
+++ b/src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java
@@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
+import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@@ -16,7 +17,7 @@
*/
@Data
@TableName("product_record")
-public class ProductRecord {
+public class ProductRecord implements Serializable {
private static final long serialVersionUID = 1L;
/**
diff --git a/src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrderProducts.java b/src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrderProducts.java
index 2a1b172..7738a34 100644
--- a/src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrderProducts.java
+++ b/src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrderProducts.java
@@ -1,20 +1,17 @@
package com.ruoyi.purchase.pojo;
-import com.baomidou.mybatisplus.annotation.FieldFill;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
/**
* <p>
- *
+ *
* </p>
*
* @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
@@ -34,7 +31,7 @@
@Schema(description = "閫�璐у崟id")
private Long purchaseReturnOrderId;
- @Schema(description = "閲囪喘浜у搧id")
+ @Schema(description = "閿�鍞彴璐︿骇鍝乮d")
private Long salesLedgerProductId;
@Schema(description = "閫�璐ф暟閲�")
@@ -54,4 +51,7 @@
@TableField(fill = FieldFill.INSERT)
private Long deptId;
+ @Schema(description = "鍏宠仈鍏ュ簱鍗昳d")
+ private Long stockInRecordId;
+
}
diff --git a/src/main/java/com/ruoyi/purchase/pojo/SalesLedgerProductTemplate.java b/src/main/java/com/ruoyi/purchase/pojo/SalesLedgerProductTemplate.java
index 3f1690c..793fba6 100644
--- a/src/main/java/com/ruoyi/purchase/pojo/SalesLedgerProductTemplate.java
+++ b/src/main/java/com/ruoyi/purchase/pojo/SalesLedgerProductTemplate.java
@@ -69,10 +69,10 @@
private Integer type;
@Schema(description = "浜у搧id")
- private Integer productId;
+ private Long productId;
@Schema(description = "鍨嬪彿id")
- private Integer productModelId;
+ private Long productModelId;
private String register;
diff --git a/src/main/java/com/ruoyi/purchase/service/PurchaseReturnOrdersService.java b/src/main/java/com/ruoyi/purchase/service/PurchaseReturnOrdersService.java
index 2038e09..14e040e 100644
--- a/src/main/java/com/ruoyi/purchase/service/PurchaseReturnOrdersService.java
+++ b/src/main/java/com/ruoyi/purchase/service/PurchaseReturnOrdersService.java
@@ -8,7 +8,10 @@
import com.ruoyi.purchase.vo.PurchaseReturnDetailsVo;
import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto;
+import com.ruoyi.purchase.vo.PurchaseStockInProductVo;
import jakarta.validation.constraints.NotNull;
+
+import java.util.List;
/**
* <p>
@@ -27,4 +30,7 @@
PurchaseReturnDetailsVo getPurchaseReturnOrderDtoById(@NotNull Long id);
void deleteById(@NotNull Long id);
+
+ List<PurchaseStockInProductVo> getByPurchaseLedgerId(Long purchaseLedgerId);
+
}
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java
index b5003ae..9da4a11 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java
@@ -1,7 +1,6 @@
package com.ruoyi.purchase.service.impl;
import cn.hutool.core.bean.BeanUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -9,36 +8,32 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.pojo.AccountIncome;
import com.ruoyi.account.service.AccountIncomeService;
-import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
+import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto;
import com.ruoyi.purchase.dto.PurchaseReturnOrderProductsDto;
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;
import com.ruoyi.purchase.vo.PurchaseReturnDetailsVo;
+import com.ruoyi.purchase.vo.PurchaseReturnOrderProductsDetailVo;
+import com.ruoyi.purchase.vo.PurchaseStockInProductVo;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
-import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerService;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
-import com.ruoyi.stock.pojo.StockOutRecord;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
/**
* <p>
@@ -76,13 +71,13 @@
purchaseReturnOrderProductsDto.setSalesLedgerProductId(purchaseReturnOrderProductsDto.getSalesLedgerProductId());
purchaseReturnOrderProductsDto.setPurchaseReturnOrderId(purchaseReturnOrderDto.getId());
purchaseReturnOrderProductsDto.setReturnQuantity(purchaseReturnOrderProductsDto.getReturnQuantity());
+ purchaseReturnOrderProductsDto.setStockInRecordId(purchaseReturnOrderProductsDto.getStockInRecordId());
// 杩欓噷涓烘柊澧炲洜姝d涓簄ull
purchaseReturnOrderProductsDto.setId(null);
purchaseReturnOrderProductsMapper.insert(purchaseReturnOrderProductsDto);
- //搴撳瓨闇�瑕佸嚭搴�(閲囪喘閫�璐�)
- PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseReturnOrderDto.getPurchaseLedgerId());
+ //搴撳瓨闇�瑕佸嚭搴�(閲囪喘閫�璐�,甯︽壒娆″彿)
SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(purchaseReturnOrderProductsDto.getSalesLedgerProductId());
- stockUtils.substractStock(salesLedgerProduct.getProductModelId(), purchaseReturnOrderProductsDto.getReturnQuantity(), StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode(), purchaseReturnOrderDto.getId(), purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId());
+ stockUtils.substractStock(salesLedgerProduct.getProductModelId(), purchaseReturnOrderProductsDto.getReturnQuantity(), StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode(), purchaseReturnOrderProductsDto.getId(), purchaseReturnOrderProductsDto.getBatchNo());
}
}else {
throw new RuntimeException("璇烽�夋嫨閫�璐у晢鍝�");
@@ -107,25 +102,12 @@
@Override
public PurchaseReturnDetailsVo getPurchaseReturnOrderDtoById(Long id) {
+ //鏌ヤ富浣�
PurchaseReturnOrderHasAllInfoDto purchaseReturnOrders = purchaseReturnOrdersMapper.getPurchaseReturnOrderHasAllInfoById(id);
PurchaseReturnDetailsVo purchaseReturnOrderDto = BeanUtil.copyProperties(purchaseReturnOrders, PurchaseReturnDetailsVo.class);
- // 鏌ヨ鍑轰粬鍏蜂綋瀵瑰簲鐨勯��璐�
- LambdaQueryWrapper<PurchaseReturnOrderProducts> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(PurchaseReturnOrderProducts::getPurchaseReturnOrderId, purchaseReturnOrders.getId());
-
- List<PurchaseReturnOrderProducts> purchaseReturnOrderProducts = purchaseReturnOrderProductsMapper.selectList(queryWrapper);
- List<PurchaseReturnDetailsVo.PurchaseReturnOrderProductsDetailVo> purchaseReturnOrderProductsDetailVos = BeanUtil.copyToList(purchaseReturnOrderProducts, PurchaseReturnDetailsVo.PurchaseReturnOrderProductsDetailVo.class);
- // 鏌ヨ鍑哄搴旂殑鍟嗗搧淇℃伅
- List<Long> productIds = purchaseReturnOrderProductsDetailVos.stream().map(PurchaseReturnDetailsVo.PurchaseReturnOrderProductsDetailVo::getSalesLedgerProductId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
- List<SalesLedgerProduct> salesLedgerProducts = salesLedgerService.getSalesLedgerProductListByIds(productIds, SaleEnum.PURCHASE);
- Map<Long, SalesLedgerProduct> productmap = salesLedgerProducts.stream().collect(Collectors.toMap(SalesLedgerProduct::getId, product -> product));
- purchaseReturnOrderProductsDetailVos.forEach(purchaseReturnOrderProductsDetailVo -> {
- purchaseReturnOrderProductsDetailVo.setSalesLedgerProduct(productmap.get(purchaseReturnOrderProductsDetailVo.getSalesLedgerProductId()));
- });
-
+ //鏌ユ槑缁�
+ List<PurchaseReturnOrderProductsDetailVo> purchaseReturnOrderProductsDetailVos = purchaseReturnOrdersMapper.getPurchaseReturnOrderProductsDetailById(id);
purchaseReturnOrderDto.setPurchaseReturnOrderProductsDetailVoList(purchaseReturnOrderProductsDetailVos);
-
-
return purchaseReturnOrderDto;
}
@@ -133,13 +115,14 @@
@Transactional
public void deleteById(Long id) {
purchaseReturnOrdersMapper.deleteById(id);
+ List<PurchaseReturnOrderProducts> purchaseReturnOrderProducts = purchaseReturnOrderProductsMapper.selectList(Wrappers.<PurchaseReturnOrderProducts>lambdaQuery().eq(PurchaseReturnOrderProducts::getPurchaseReturnOrderId, id));
LambdaUpdateWrapper<PurchaseReturnOrderProducts> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(PurchaseReturnOrderProducts::getPurchaseReturnOrderId, id);
purchaseReturnOrderProductsMapper.delete(updateWrapper);
//(閲囪喘閫�璐х殑鏁版嵁闇�瑕佸垹鎺�)
- stockOutRecordMapper.delete(Wrappers.<StockOutRecord>lambdaQuery()
- .eq(StockOutRecord::getRecordType,StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode())
- .eq(StockOutRecord::getRecordId, id));
+ purchaseReturnOrderProducts.stream().forEach(purchaseReturnOrderProducts1 -> {
+ stockUtils.deleteStockOutRecord(purchaseReturnOrderProducts1.getId(),StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode());
+ });
// 璐㈠姟
LambdaUpdateWrapper<AccountIncome> updateWrapperAccountIncome = new LambdaUpdateWrapper<>();
updateWrapperAccountIncome.eq(AccountIncome::getBusinessId, id);
@@ -147,4 +130,9 @@
updateWrapperAccountIncome.eq(AccountIncome::getIncomeType, 4);
accountIncomeService.remove(updateWrapperAccountIncome);
}
+
+ @Override
+ public List<PurchaseStockInProductVo> getByPurchaseLedgerId(Long purchaseLedgerId) {
+ return purchaseReturnOrdersMapper.getByPurchaseLedgerId(purchaseLedgerId);
+ }
}
diff --git a/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnDetailsVo.java b/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnDetailsVo.java
index 975cd5c..6496b4a 100644
--- a/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnDetailsVo.java
+++ b/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnDetailsVo.java
@@ -1,14 +1,12 @@
package com.ruoyi.purchase.vo;
import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto;
-import com.ruoyi.sales.pojo.SalesLedgerProduct;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serializable;
-import java.math.BigDecimal;
import java.util.List;
/**
@@ -24,15 +22,4 @@
private List<PurchaseReturnOrderProductsDetailVo> purchaseReturnOrderProductsDetailVoList;
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public static class PurchaseReturnOrderProductsDetailVo implements Serializable {
- private Long id;
- private BigDecimal returnQuantity;
- private Long salesLedgerProductId;
- private Long purchaseReturnOrderId;
-
- private SalesLedgerProduct salesLedgerProduct;
- }
}
diff --git a/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java b/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java
new file mode 100644
index 0000000..dcd58b5
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java
@@ -0,0 +1,62 @@
+package com.ruoyi.purchase.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class PurchaseReturnOrderProductsDetailVo {
+
+ @Schema(description = "閫�璐ф槑缁唅d")
+ private Long id;
+
+ @Schema(description = "閿�鍞彴璐︾殑浜у搧id")
+ private Long salesLedgerProductId;
+
+ @Schema(description = "浜у搧瑙勬牸id")
+ private Long productModelId;
+
+ @Schema(description = "浜у搧澶х被")
+ private String productCategory;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String specificationModel;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "鍏ュ簱鍗曞彿")
+ private String inboundBatches;
+
+ @Schema(description = "鍏ュ簱鏁伴噺")
+ private BigDecimal stockInNum;
+
+ @Schema(description = "鎵规鍙�")
+ private String batchNo;
+
+ @Schema(description = "鏈��璐ф暟")
+ private BigDecimal unQuantity;
+
+ @Schema(description = "宸查��璐ф暟閲�")
+ private BigDecimal totalReturnNum;
+
+ @Schema(description = "鍚◣鍗曚环")
+ private BigDecimal taxInclusiveUnitPrice;
+
+ @Schema(description = "閫�璐ф暟閲�")
+ private BigDecimal returnQuantity;
+
+ @Schema(description = "閫�璐у崟id")
+ private Long purchaseReturnOrderId;
+
+ @Schema(description = "鏄惁璐ㄦ")
+ private Boolean isChecked;
+
+}
diff --git a/src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java b/src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java
new file mode 100644
index 0000000..3f8e0ec
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java
@@ -0,0 +1,50 @@
+package com.ruoyi.purchase.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@Schema(name = "PurchaseStockInProductVo", description = "閲囪喘绠$悊--閲囪喘璁㈠崟涓嬪叆搴撲骇鍝佸垪琛�")
+public class PurchaseStockInProductVo {
+
+ @Schema(description = "鍏ュ簱鍗昳d")
+ private Long id;
+
+ @Schema(description = "閿�鍞彴璐︾殑浜у搧id")
+ private Long salesLedgerProductId;
+
+ @Schema(description = "浜у搧瑙勬牸id")
+ private Long productModelId;
+
+ @Schema(description = "浜у搧澶х被")
+ private String productCategory;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String specificationModel;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "鍏ュ簱鍗曞彿")
+ private String inboundBatches;
+
+ @Schema(description = "鍏ュ簱鏁伴噺")
+ private BigDecimal stockInNum;
+
+ @Schema(description = "鎵规鍙�")
+ private String batchNo;
+
+ @Schema(description = "鍙��璐ф暟")
+ private BigDecimal unQuantity;
+
+ @Schema(description = "閫�璐ф�绘暟")
+ private BigDecimal totalReturnNum;
+
+ @Schema(description = "鍚◣鍗曚环")
+ private BigDecimal taxInclusiveUnitPrice;
+
+ @Schema(description = "鏄惁璐ㄦ")
+ private Boolean isChecked;
+}
diff --git a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
index 9d8a07d..62b047a 100644
--- a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
+++ b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -96,9 +96,17 @@
/**
* 鏁伴噺
*/
- @Excel(name = "鏁伴噺")
+ @Excel(name = "鎬绘暟閲�")
private BigDecimal quantity;
+ @Excel(name = "鍚堟牸鏁伴噺")
+ @TableField("qualified_quantity")
+ private BigDecimal qualifiedQuantity;
+
+ @Excel(name = "涓嶅悎鏍兼暟閲�")
+ @TableField("unqualified_quantity")
+ private BigDecimal unqualifiedQuantity;
+
/**
* 妫�娴嬪崟浣�
*/
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 81034bc..625ff8b 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.quality.service.impl;
+import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -20,6 +21,8 @@
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityInspectParam;
import com.ruoyi.quality.pojo.QualityUnqualified;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.quality.service.IQualityInspectParamService;
import com.ruoyi.quality.service.IQualityInspectService;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
@@ -33,7 +36,9 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.math.BigDecimal;
import java.net.URLEncoder;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
@@ -45,6 +50,7 @@
private final StockUtils stockUtils;
private final StockInventoryService stockInventoryService;
+ private final StockInRecordService stockInRecordService;
private QualityInspectMapper qualityInspectMapper;
private IQualityInspectParamService qualityInspectParamService;
@@ -88,18 +94,10 @@
if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
throw new RuntimeException("璇峰厛鍒ゆ柇鏄惁鍚堟牸");
}
- /*鍒ゆ柇涓嶅悎鏍�*/
- if (qualityInspect.getCheckResult().equals("涓嶅悎鏍�")) {
- QualityUnqualified qualityUnqualified = new QualityUnqualified();
- BeanUtils.copyProperties(qualityInspect, qualityUnqualified);
- qualityUnqualified.setInspectState(0);//寰呭鐞�
- qualityUnqualified.setProductModelId(qualityInspect.getProductModelId());
- List<QualityInspectParam> inspectParams = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
- String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(","));
- qualityUnqualified.setDefectivePhenomena(text + "杩欎簺鎸囨爣涓瓨鍦ㄤ笉鍚堟牸");//涓嶅悎鏍肩幇璞�
- qualityUnqualified.setInspectId(qualityInspect.getId());
- qualityUnqualifiedMapper.insert(qualityUnqualified);
- } else {
+
+ // 鍖哄垎鍚堟牸鏁伴噺浠ュ強涓嶅悎鏍煎鐞嗚繘琛屽搴旂殑澶勭悊
+ Assert.isTrue(qualityInspect.getQuantity().compareTo(qualityInspect.getQualifiedQuantity().add(qualityInspect.getUnqualifiedQuantity())) == 0,"璇锋鏌ュ悎鏍兼暟閲忓拰涓嶅悎鏍兼暟閲忥紝闇�瑕佸悎鏍兼暟閲�+涓嶅悎鏍兼暟閲忎笌鎬绘暟淇濇寔涓�鑷�");
+ if(qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0){
//鍚堟牸鐩存帴鍏ュ簱
// stockUtils.addStock(qualityInspect.getProductModelId(), qualityInspect.getQuantity(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId());
//浠呮坊鍔犲叆搴撹褰�
@@ -111,13 +109,66 @@
}
stockInventoryDto.setRecordId(qualityInspect.getId());
stockInventoryDto.setProductModelId(qualityInspect.getProductModelId());
- stockInventoryDto.setQualitity(qualityInspect.getQuantity());
+ stockInventoryDto.setQualitity(qualityInspect.getQualifiedQuantity());
+ stockInventoryDto.setBatchNo(resolveProductionBatchNo(
+ qualityInspect.getProductMainId(),
+ qualityInspect.getId(),
+ qualityInspect.getProductModelId()));
stockInventoryService.addStockInRecordOnly(stockInventoryDto);
}
+ if(qualityInspect.getUnqualifiedQuantity().compareTo(BigDecimal.ZERO) > 0){
+ QualityUnqualified qualityUnqualified = new QualityUnqualified();
+ BeanUtils.copyProperties(qualityInspect, qualityUnqualified);
+ qualityUnqualified.setInspectState(0);//寰呭鐞�
+ qualityUnqualified.setQuantity(qualityInspect.getUnqualifiedQuantity());
+ List<QualityInspectParam> inspectParams = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
+ String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(","));
+ qualityUnqualified.setDefectivePhenomena(text + "杩欎簺鎸囨爣涓瓨鍦ㄤ笉鍚堟牸");//涓嶅悎鏍肩幇璞�
+ qualityUnqualified.setInspectId(qualityInspect.getId());
+ qualityUnqualifiedMapper.insert(qualityUnqualified);
+ }
+
qualityInspect.setInspectState(1);//宸叉彁浜�
return qualityInspectMapper.updateById(qualityInspect);
}
+ private String resolveProductionBatchNo(Long productionProductMainId,
+ Long qualityInspectId,
+ Long productModelId) {
+ if (productModelId == null) {
+ return null;
+ }
+ if (productionProductMainId != null) {
+ StockInRecord productionRecord = stockInRecordService.getOne(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .eq(StockInRecord::getRecordId, productionProductMainId)
+ .eq(StockInRecord::getProductModelId, productModelId)
+ .in(StockInRecord::getRecordType, Arrays.asList(
+ StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+ StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()))
+ .isNotNull(StockInRecord::getBatchNo)
+ .orderByDesc(StockInRecord::getId)
+ .last("limit 1"),
+ false);
+ if (productionRecord != null) {
+ return productionRecord.getBatchNo();
+ }
+ }
+ if (qualityInspectId == null) {
+ return null;
+ }
+ StockInRecord inspectRecord = stockInRecordService.getOne(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .eq(StockInRecord::getRecordId, qualityInspectId)
+ .eq(StockInRecord::getProductModelId, productModelId)
+ .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode())
+ .isNotNull(StockInRecord::getBatchNo)
+ .orderByDesc(StockInRecord::getId)
+ .last("limit 1"),
+ false);
+ return inspectRecord == null ? null : inspectRecord.getBatchNo();
+ }
+
/*鐢熸垚妫�楠屾姤鍛�*/
@Override
public void down(HttpServletResponse response, QualityInspect qualityInspect) {
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
index c82886e..b3d93b8 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -23,6 +23,8 @@
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityUnqualified;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.quality.service.IQualityInspectService;
import com.ruoyi.quality.service.IQualityUnqualifiedService;
import com.ruoyi.stock.service.StockUninventoryService;
@@ -33,6 +35,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -50,6 +53,7 @@
private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
private final ProductionOperationTaskMapper productionOperationTaskMapper;
private final StockUninventoryService stockUninventoryService;
+ private final StockInRecordService stockInRecordService;
@Override
public IPage<QualityUnqualified> qualityUnqualifiedListPage(Page page, QualityUnqualified qualityUnqualified) {
@@ -67,6 +71,8 @@
public int deal(QualityUnqualified qualityUnqualified) {
QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId());
QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
+ String batchNo = qualityInspect == null ? null
+ : resolveProductionBatchNo(qualityInspect.getProductMainId(), qualityInspect.getId(), qualityInspect.getProductModelId());
if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType() != 0) {
switch (qualityUnqualified.getDealResult()) {
case "杩斾慨":
@@ -80,12 +86,12 @@
}
break;
case "鎶ュ簾":
- stockUtils.addUnStock(qualityInspect.getProductModelId(), unqualified.getQuantity(),
- StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
+ stockUtils.addUnStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
+ StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId(), batchNo);
break;
case "璁╂鏀捐":
- stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(),
- StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
+ stockUtils.addStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
+ StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId(), batchNo);
break;
default:
break;
@@ -219,4 +225,41 @@
private BigDecimal defaultDecimal(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
+
+ private String resolveProductionBatchNo(Long productionProductMainId,
+ Long qualityInspectId,
+ Long productModelId) {
+ if (productModelId == null) {
+ return null;
+ }
+ if (productionProductMainId != null) {
+ StockInRecord productionRecord = stockInRecordService.getOne(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .eq(StockInRecord::getRecordId, productionProductMainId)
+ .eq(StockInRecord::getProductModelId, productModelId)
+ .in(StockInRecord::getRecordType, Arrays.asList(
+ StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+ StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()))
+ .isNotNull(StockInRecord::getBatchNo)
+ .orderByDesc(StockInRecord::getId)
+ .last("limit 1"),
+ false);
+ if (productionRecord != null) {
+ return productionRecord.getBatchNo();
+ }
+ }
+ if (qualityInspectId == null) {
+ return null;
+ }
+ StockInRecord inspectRecord = stockInRecordService.getOne(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .eq(StockInRecord::getRecordId, qualityInspectId)
+ .eq(StockInRecord::getProductModelId, productModelId)
+ .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode())
+ .isNotNull(StockInRecord::getBatchNo)
+ .orderByDesc(StockInRecord::getId)
+ .last("limit 1"),
+ false);
+ return inspectRecord == null ? null : inspectRecord.getBatchNo();
+ }
}
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java b/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
index a4c1106..9485919 100644
--- a/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
@@ -71,9 +71,9 @@
if (CollUtil.isEmpty(list)) {
return AjaxResult.success(list);
}
- List<Long> productIds = list.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList());
+ 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::getSalesLedgerProductId, item -> item.getSumReturnQuantity()));
+ Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, item -> item.getSumReturnQuantity()));
list.forEach(item -> {
if (item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
@@ -90,7 +90,7 @@
}
}
// 缁熻閫�璐ф暟閲�
- BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+ BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
item.setReturnQuality(returnQuality);
item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
});
diff --git a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
index 0257f2a..f707b5b 100644
--- a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
+++ b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -116,7 +116,7 @@
@GetMapping("/getByCustomerName")
- @Operation(summary = "閫氳繃瀹㈡埛鍚嶇О鏌ヨ")
+ @Operation(summary = "閫氳繃瀹㈡埛鍚嶇О鏌ヨ鍏宠仈鐨勫彂璐у崟鍙�")
public AjaxResult getByCustomerName(String customerName) {
return AjaxResult.success(shippingInfoService.getShippingInfoByCustomerName(customerName));
}
diff --git a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
index e12149f..43409bc 100644
--- a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -8,6 +8,7 @@
import com.ruoyi.sales.pojo.ShippingProductDetail;
import lombok.Data;
+import java.math.BigDecimal;
import java.util.List;
/**
@@ -40,6 +41,11 @@
private List<Long> batchNo;
private List<ShippingProductDetail> batchNoDetailList;
+ //鍏宠仈鐨勫嚭搴撳崟鍙�
+ private String outboundBatches;
+
+ //鍙戣揣鏁伴噺
+ private BigDecimal totalQuantity;
}
diff --git a/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
index 7f3c701..07bddbb 100644
--- a/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
@@ -3,7 +3,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.sales.dto.SalesLedgerProductDto;
+import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.pojo.ShippingInfo;
import org.apache.ibatis.annotations.Param;
@@ -19,7 +19,7 @@
List<ShippingInfo> listAll();
- List<SalesLedgerProductDto> getReturnManagementDtoById(@Param("shippingId")Long shippingId);
+ List<ShippingProductVo> getReturnManagementDtoById(@Param("shippingId")Long shippingId);
List<ShippingInfo> getShippingInfoByCustomerName(String customerName);
}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java b/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
index 3e64d15..2e55e79 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
@@ -14,7 +14,12 @@
private Long id;
@Schema(description = "閿�鍞姤浠峰崟id")
private Long salesQuotationId;
-
+ @Schema(description = "浜у搧Id")
+ @TableField(value = "product_id")
+ private Long productId;
+ @Schema(description = "浜у搧瑙勬牸Id")
+ @TableField(value = "product_model_id")
+ private Long productModelId;
@Schema(description = "鍟嗗搧鍚嶇О")
private String product;
@Schema(description = "鍟嗗搧瑙勬牸")
diff --git a/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java b/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
index 69e8245..aa7bad5 100644
--- a/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
+++ b/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
@@ -3,7 +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.sales.dto.SalesLedgerProductDto;
+import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.sales.dto.ShippingApproveDto;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.dto.ShippingProductDetailDto;
@@ -22,7 +22,7 @@
boolean delete(List<Long> ids);
- List<SalesLedgerProductDto> getReturnManagementDtoById( Long shippingId);
+ List<ShippingProductVo> getReturnManagementDtoById(Long shippingId);
List<ShippingInfo> getShippingInfoByCustomerName(String customerName);
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index 9803611..a2f918b 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -9,10 +9,13 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.service.AccountIncomeService;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.exception.base.BaseException;
@@ -102,6 +105,8 @@
private final ProductionProductInputMapper productionProductInputMapper;
private final QualityInspectMapper qualityInspectMapper;
private final RedisTemplate<String, String> redisTemplate;
+ private final FileUtil fileUtil;
+
@Autowired
private SysDeptMapper sysDeptMapper;
@Value("${file.upload-dir}")
@@ -142,14 +147,14 @@
List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(productWrapper);
if (type.equals(SaleEnum.PURCHASE)) {
// 鏌ヨ閫�璐т俊鎭�
- List<Long> productIds = salesLedgerProducts.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList());
+ 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::getSalesLedgerProductId, SimpleReturnOrderGroupDto::getSumReturnQuantity));
+ Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, SimpleReturnOrderGroupDto::getSumReturnQuantity));
salesLedgerProducts.forEach(item -> {
- BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+ BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
item.setReturnQuality(returnQuality);
item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
});
@@ -582,125 +587,44 @@
@Override
@Transactional(rollbackFor = Exception.class)
public int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto) {
- try {
- // 1. 鏍¢獙瀹㈡埛淇℃伅
- Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId());
- if (customer == null) {
- throw new BaseException("瀹㈡埛涓嶅瓨鍦�");
- }
-
- // 2. DTO杞珽ntity
- SalesLedger salesLedger = convertToEntity(salesLedgerDto);
- salesLedger.setCustomerName(customer.getCustomerName());
- salesLedger.setTenantId(customer.getTenantId());
- // 3. 鏂板鎴栨洿鏂颁富琛�
- if (salesLedger.getId() == null) {
- String contractNo = generateSalesContractNo();
- salesLedger.setSalesContractNo(contractNo);
- salesLedgerMapper.insert(salesLedger);
- } else {
- salesLedgerMapper.updateById(salesLedger);
- }
-
- // 4. 澶勭悊瀛愯〃鏁版嵁
- List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
- if (productList != null && !productList.isEmpty()) {
- handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class, salesLedgerDto.getType()));
- updateMainContractAmount(
- salesLedger.getId(),
- productList,
- SalesLedgerProduct::getTaxInclusiveTotalPrice,
- salesLedgerMapper,
- SalesLedger.class
- );
- }
-
- // 5. 杩佺Щ涓存椂鏂囦欢鍒版寮忕洰褰�
- if (salesLedgerDto.getTempFileIds() != null && !salesLedgerDto.getTempFileIds().isEmpty()) {
- migrateTempFilesToFormal(salesLedger.getId(), salesLedgerDto.getTempFileIds());
- }
- return 1;
- } catch (IOException e) {
- throw new BaseException("鏂囦欢杩佺Щ澶辫触: " + e.getMessage());
+ // 1. 鏍¢獙瀹㈡埛淇℃伅
+ Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId());
+ if (customer == null) {
+ throw new BaseException("瀹㈡埛涓嶅瓨鍦�");
}
+
+ // 2. DTO杞珽ntity
+ SalesLedger salesLedger = convertToEntity(salesLedgerDto);
+ salesLedger.setCustomerName(customer.getCustomerName());
+ salesLedger.setTenantId(customer.getTenantId());
+ // 3. 鏂板鎴栨洿鏂颁富琛�
+ if (salesLedger.getId() == null) {
+ String contractNo = generateSalesContractNo();
+ salesLedger.setSalesContractNo(contractNo);
+ salesLedgerMapper.insert(salesLedger);
+ } else {
+ salesLedgerMapper.updateById(salesLedger);
+ }
+
+
+ // 4. 澶勭悊瀛愯〃鏁版嵁
+ List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
+ if (productList != null && !productList.isEmpty()) {
+ handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class, salesLedgerDto.getType()));
+ updateMainContractAmount(
+ salesLedger.getId(),
+ productList,
+ SalesLedgerProduct::getTaxInclusiveTotalPrice,
+ salesLedgerMapper,
+ SalesLedger.class
+ );
+ }
+
+ // 5. 淇濆瓨閿�鍞彴璐﹂檮浠�
+ fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs());
+
+ return 1;
}
-
- /**
- * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
- *
- * @param businessId 涓氬姟ID锛堥攢鍞彴璐D锛�
- * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
- * @throws IOException 鏂囦欢鎿嶄綔寮傚父
- */
- private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
- if (CollectionUtils.isEmpty(tempFileIds)) {
- return;
- }
-
- // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
- String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
- Path formalDirPath = Paths.get(formalDir);
-
- // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
- if (!Files.exists(formalDirPath)) {
- Files.createDirectories(formalDirPath);
- }
-
- for (String tempFileId : tempFileIds) {
- // 鏌ヨ涓存椂鏂囦欢璁板綍
- TempFile tempFile = tempFileMapper.selectById(tempFileId);
- if (tempFile == null) {
- log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
- continue;
- }
-
- // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟ID鍜屾椂闂存埑锛岄伩鍏嶅啿绐侊級
- String originalFilename = tempFile.getOriginalName();
- String fileExtension = FilenameUtils.getExtension(originalFilename);
- String formalFilename = businessId + "_" +
- System.currentTimeMillis() + "_" +
- UUID.randomUUID().toString().substring(0, 8) +
- (StringUtils.hasText(fileExtension) ? "." + fileExtension : "");
-
- Path formalFilePath = formalDirPath.resolve(formalFilename);
-
- try {
- // 鎵ц鏂囦欢杩佺Щ锛堜娇鐢ㄥ師瀛愭搷浣滅‘淇濆畨鍏ㄦ�э級
-// Files.move(
-// Paths.get(tempFile.getTempPath()),
-// formalFilePath,
-// StandardCopyOption.REPLACE_EXISTING,
-// StandardCopyOption.ATOMIC_MOVE
-// );
- // 鍘熷瓙绉诲姩澶辫触锛屼娇鐢ㄥ鍒�+鍒犻櫎
- Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING);
- Files.deleteIfExists(Paths.get(tempFile.getTempPath()));
- log.info("鏂囦欢杩佺Щ鎴愬姛: {} -> {}", tempFile.getTempPath(), formalFilePath);
-
- // 鏇存柊鏂囦欢璁板綍锛堝叧鑱斿埌涓氬姟ID锛�
- CommonFile fileRecord = new CommonFile();
- fileRecord.setCommonId(businessId);
- fileRecord.setName(originalFilename);
- fileRecord.setUrl(formalFilePath.toString());
- fileRecord.setCreateTime(LocalDateTime.now());
- //閿�鍞�
- fileRecord.setType(FileNameType.SALE.getValue());
- commonFileMapper.insert(fileRecord);
-
- // 鍒犻櫎涓存椂鏂囦欢璁板綍
- tempFileMapper.deleteById(tempFile);
-
- log.info("鏂囦欢杩佺Щ鎴愬姛: {} -> {}", tempFile.getTempPath(), formalFilePath);
- } catch (IOException e) {
- log.error("鏂囦欢杩佺Щ澶辫触: {}", tempFile.getTempPath(), e);
- // 鍙�夋嫨鍥炴粴浜嬪姟鎴栬褰曞け璐ユ枃浠�
- throw new IOException("鏂囦欢杩佺Щ寮傚父", e);
- }
- }
- }
-
- // 鏂囦欢杩佺Щ鏂规硶
@Override
public void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type) {
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
index f06cad0..de86ad0 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
@@ -13,6 +13,7 @@
import com.ruoyi.approve.bean.vo.ApproveProcessVO;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.common.enums.IsDeleteEnum;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
@@ -138,6 +139,7 @@
// 鍒犻櫎鎶ヤ环瀹℃壒
ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
.eq(ApproveProcess::getApproveType, 6)
+ .eq(ApproveProcess::getApproveDelete, IsDeleteEnum.NOT_DELETED)
.eq(ApproveProcess::getApproveReason, salesQuotation.getQuotationNo()));
if(one != null){
approveProcessService.delByIds(Collections.singletonList(one.getId()));
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index 5f8788d..8d16029 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -11,8 +11,8 @@
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
+import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.procurementrecord.utils.StockUtils;
-import com.ruoyi.sales.dto.SalesLedgerProductDto;
import com.ruoyi.sales.dto.ShippingApproveDto;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.dto.ShippingProductDetailDto;
@@ -95,12 +95,6 @@
if (CollectionUtils.isEmpty(shippingInfos)) return false;
// 鍒犻櫎闄勪欢
commonFileService.deleteByBusinessIds(ids, FileNameType.SHIP.getValue());
- // 鎵e凡鍙戣揣搴撳瓨
- for (ShippingInfo shippingInfo : shippingInfos) {
- if ("宸插彂璐�".equals(shippingInfo.getStatus())) {
- stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
- }
- }
// 鍒犻櫎鍙戣揣瀹℃壒
if (CollectionUtils.isNotEmpty(shippingInfos)) {
for (ShippingInfo shippingInfo : shippingInfos) {
@@ -110,6 +104,8 @@
List<Long> list = one.stream().map(ApproveProcess::getId).toList();
approveProcessService.delByIds(list);
}
+ // 鎵e凡鍙戣揣搴撳瓨
+ stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
}
}
//鍒犻櫎鍙戣揣鏄庣粏
@@ -119,7 +115,7 @@
}
@Override
- public List<SalesLedgerProductDto> getReturnManagementDtoById(Long shippingId) {
+ public List<ShippingProductVo> getReturnManagementDtoById(Long shippingId) {
return shippingInfoMapper.getReturnManagementDtoById(shippingId);
}
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
index 44fe885..a0a9fa5 100644
--- a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -51,6 +51,19 @@
return R.ok(stockInventoryDtoIPage);
}
+ /**
+ * 鏌ヨ瀵瑰簲鎵瑰彿鍜屾暟閲�
+ * @param page
+ * @param stockInventoryDto
+ * @return
+ */
+ @GetMapping("/getBatchNoQty")
+ @Operation(summary = "鏌ヨ瀵瑰簲鎵瑰彿鍜屾暟閲�")
+ public R getBatchNoQty(Page page, StockInventoryDto stockInventoryDto) {
+ IPage<StockInventoryDto> stockInventoryDtoIPage = stockInventoryService.getBatchNoQty(page, stockInventoryDto);
+ return R.ok(stockInventoryDtoIPage);
+ }
+
@PostMapping("/addstockInventory")
@Operation(summary = "鏂板搴撳瓨")
public R addstockInventory(@RequestBody StockInventoryDto stockInventoryDto) {
@@ -85,7 +98,7 @@
}
- @PostMapping("importStockInventory")
+ @PostMapping("/importStockInventory")
@Operation(summary = "瀵煎叆搴撳瓨")
public R importStockInventory(MultipartFile file) {
return stockInventoryService.importStockInventory(file);
@@ -105,13 +118,13 @@
stockInventoryService.exportStockInventory(response, stockInventoryDto);
}
- @GetMapping("stockInventoryPage")
+ @GetMapping("/stockInventoryPage")
@Operation(summary = "搴撳瓨鎶ヨ〃鏌ヨ")
public R stockInventoryPage(Page page, StockInventoryDto stockInventoryDto) {
return R.ok(stockInventoryService.stockInventoryPage(stockInventoryDto,page));
}
- @GetMapping("stockInAndOutRecord")
+ @GetMapping("/stockInAndOutRecord")
@Operation(summary = "缁熻鍚勪釜浜у搧鐨勫叆搴撳拰鍑哄簱璁板綍")
public R stockInAndOutRecord(StockInventoryDto stockInventoryDto,Page page) {
return R.ok(stockInventoryService.stockInAndOutRecord(stockInventoryDto,page));
@@ -128,7 +141,6 @@
public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
return R.ok(stockInventoryService.thawStock(stockInventoryDto));
}
-
@GetMapping("/getByModelId")
@Operation(summary = "鏍规嵁浜у搧瑙勬牸ID鑾峰彇鍏ュ簱璁板綍")
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
index 2be6512..bc08eee 100644
--- a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
+++ b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -76,4 +76,7 @@
@Schema(description = "涓嶅悎鏍煎簱瀛業D")
private Long unQualifiedId;
+
+ @Schema(description = "浜у搧id")
+ private Long productId;
}
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
index d705110..a5a9642 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
@@ -18,11 +18,13 @@
private String model;
@Excel(name = "鍗曚綅")
private String unit;
+ @Excel(name = "鎵瑰彿")
+ private String batchNo;
@Excel(name = "鍏ュ簱鏉ユ簮")
private String recordType;
@Excel(name = "鍏ュ簱鏁伴噺")
private String stockInNum;
- @Excel(name = "鍏ュ簱鏃堕棿")
+ @Excel(name = "鍏ュ簱鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
index a5cec35..a608401 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
@@ -19,6 +19,10 @@
@Excel(name = "鍗曚綅")
private String unit;
+ @Excel(name = "鎵瑰彿")
+ private String batchNo;
+
+
@Excel(name = "鍚堟牸搴撳瓨鏁伴噺")
private BigDecimal qualifiedQuantity;
diff --git a/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java b/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
index f120817..c0f6b25 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
@@ -17,11 +17,13 @@
private String model;
@Excel(name = "鍗曚綅")
private String unit;
+ @Excel(name = "鎵瑰彿")
+ private String batchNo;
@Excel(name = "鍑哄簱鏉ユ簮")
private String recordType;
@Excel(name = "鍑哄簱鏁伴噺")
- private String stockInNum;
- @Excel(name = "鍑哄簱鏃堕棿")
+ private String stockOutNum;
+ @Excel(name = "鍑哄簱鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/stock/execl/StockUnInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockUnInventoryExportData.java
index 7fd6e36..6f90ec4 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockUnInventoryExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockUnInventoryExportData.java
@@ -19,6 +19,8 @@
@Excel(name = "鍗曚綅")
private String unit;
+ @Excel(name = "鎵瑰彿")
+ private String batchNo;
@Excel(name = "搴撳瓨鏁伴噺")
private BigDecimal qualitity;
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
index a31cf5b..dbc271b 100644
--- a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -56,4 +56,6 @@
List<StockInventory> listSelectableBatchNoByProductModelIds(@Param("productModelIds") List<Long> productModelIds);
List<StockInventory> getByModelId(@Param("productModelId") Long productModelId);
+
+ IPage<StockInventoryDto> getBatchNoQty(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
}
diff --git a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
index d47cc4b..5d534d5 100644
--- a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
+++ b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -47,4 +47,6 @@
Boolean thawStock(StockInventoryDto stockInventoryDto);
List<StockInventory> getByModelId(Long modelId);
+
+ IPage<StockInventoryDto> getBatchNoQty(Page page, StockInventoryDto stockInventoryDto);
}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
index ab34aa8..7051075 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -120,7 +120,7 @@
public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
List<StockInRecordExportData> list = stockInRecordMapper.listStockInRecordExportData(stockInRecordDto);
for (StockInRecordExportData stockInRecordExportData : list) {
- if (stockInRecordExportData.getType().equals("0")) {
+ if (!stockInRecordExportData.getType().equals("0")) {
stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockOutQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
}else {
stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
index 3808916..24605ba 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -29,7 +29,7 @@
import com.ruoyi.stock.service.StockOutRecordService;
import com.ruoyi.stock.service.StockUninventoryService;
import jakarta.servlet.http.HttpServletResponse;
-import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@@ -51,15 +51,16 @@
* @since 2026-01-21 04:16:36
*/
@Service
-@AllArgsConstructor
+@RequiredArgsConstructor
public class StockInventoryServiceImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryService {
- private StockInventoryMapper stockInventoryMapper;
- private StockInRecordService stockInRecordService;
- private StockOutRecordService stockOutRecordService;
- private StockUninventoryService stockUninventoryService;
- private SalesLedgerProductMapper salesLedgerProductMapper;
- private ProductModelMapper productModelMapper;
+ private final StockInventoryMapper stockInventoryMapper;
+ private final StockInRecordService stockInRecordService;
+ private final StockOutRecordService stockOutRecordService;
+ private final StockUninventoryService stockUninventoryService;
+ private final SalesLedgerProductMapper salesLedgerProductMapper;
+ private final ProductModelMapper productModelMapper;
+
@Override
public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
@@ -105,8 +106,8 @@
newStockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
newStockInventory.setWarnNum(stockInventoryDto.getWarnNum());
stockInventoryMapper.insert(newStockInventory);
- }else {
- stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
+ } else {
+ stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
}
return true;
}
@@ -116,7 +117,7 @@
@Transactional(rollbackFor = Exception.class)
public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) {
LambdaQueryWrapper<StockInventory> eq = new QueryWrapper<StockInventory>().lambda()
- .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId());
+ .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId());
if (StringUtils.isEmpty(stockInventoryDto.getBatchNo())) {
eq.isNull(StockInventory::getBatchNo);
stockInventoryDto.setBatchNo(null);
@@ -336,7 +337,7 @@
}
stockInventoryDto.setProductModelId(matchedProduct.getProductModelId());
- this.addstockInventory(stockInventoryDto);
+ this.addStockInRecordOnly(stockInventoryDto);
successCount++;
}
@@ -392,28 +393,28 @@
List<StockInventoryExportData> list = stockInventoryMapper.listStockInventoryExportData(stockInventoryDto);
ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
- util.exportExcel(response,list, "搴撳瓨淇℃伅");
+ util.exportExcel(response, list, "搴撳瓨淇℃伅");
}
@Override
public IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto, Page page) {
- return stockInventoryMapper.stockInventoryPage(stockInventoryDto,page);
+ return stockInventoryMapper.stockInventoryPage(stockInventoryDto, page);
}
@Override
public IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto, Page page) {
- return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto,page);
+ return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto, page);
}
@Override
public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
- if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+ if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity()) < 0) {
throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃搴撳瓨鏁伴噺");
}
if (ObjectUtils.isEmpty(stockInventory.getLockedQuantity())) {
stockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
- }else {
+ } else {
stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
}
return this.updateById(stockInventory);
@@ -422,7 +423,7 @@
@Override
public Boolean thawStock(StockInventoryDto stockInventoryDto) {
StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
- if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+ if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity()) < 0) {
throw new RuntimeException("瑙e喕鏁伴噺涓嶈兘瓒呰繃鍐荤粨鏁伴噺");
}
stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
@@ -433,4 +434,9 @@
public List<StockInventory> getByModelId(Long modelId) {
return stockInventoryMapper.getByModelId(modelId);
}
+
+ @Override
+ public IPage<StockInventoryDto> getBatchNoQty(Page page, StockInventoryDto stockInventoryDto) {
+ return stockInventoryMapper.getBatchNoQty(page, stockInventoryDto);
+ }
}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
index 4835b09..4c7d157 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -4,8 +4,12 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -14,21 +18,22 @@
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockUnInventoryExportData;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.pojo.StockUninventory;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.service.StockOutRecordService;
import com.ruoyi.stock.service.StockUninventoryService;
-import lombok.AllArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import jakarta.servlet.http.HttpServletResponse;
-
import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.List;
/**
@@ -47,6 +52,8 @@
private final StockUninventoryMapper stockUninventoryMapper;
private final StockOutRecordService stockOutRecordService;
private final StockInRecordService stockInRecordService;
+ private final ProductModelMapper productModelMapper;
+ private final StockInventoryMapper stockInventoryMapper;
@Override
public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
@@ -120,7 +127,11 @@
stockInRecordDto.setRecordId(stockUninventoryDto.getRecordId());
stockInRecordDto.setRecordType(stockUninventoryDto.getRecordType());
stockInRecordDto.setStockInNum(stockUninventoryDto.getQualitity());
- stockInRecordDto.setBatchNo(stockUninventoryDto.getBatchNo());
+ String batchNo = StringUtils.trim(stockUninventoryDto.getBatchNo());
+ if (StringUtils.isEmpty(batchNo)) {
+ batchNo = generateAutoBatchNo(stockUninventoryDto.getProductModelId());
+ }
+ stockInRecordDto.setBatchNo(batchNo);
stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
stockInRecordDto.setType("1");
stockInRecordDto.setRemark(stockUninventoryDto.getRemark());
@@ -200,4 +211,80 @@
stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
return this.updateById(stockUninventory);
}
+
+ //瑙勫垯鐢熸垚锛�20260424-浜у搧缂栧彿-001
+ private String generateAutoBatchNo(Long productModelId) {
+ if (productModelId == null) {
+ throw new ServiceException("浜у搧瑙勬牸ID涓嶈兘涓虹┖");
+ }
+ ProductModel productModel = productModelMapper.selectById(productModelId);
+ if (productModel == null) {
+ throw new ServiceException("浜у搧瑙勬牸涓嶅瓨鍦紝ID=" + productModelId);
+ }
+ String productCode = StringUtils.trim(productModel.getProductCode());
+ if (StringUtils.isEmpty(productCode)) {
+ throw new ServiceException("浜у搧瑙勬牸鏈淮鎶や骇鍝佺紪鐮侊紝ID=" + productModelId);
+ }
+
+ String dateText = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
+ String prefix = dateText + "-" + productCode + "-";
+ int maxSequence = resolveMaxSequence(prefix);
+ int sequence = maxSequence + 1;
+ while (sequence < 1_000_000) {
+ String batchNo = prefix + String.format("%03d", sequence);
+ if (!isBatchNoExists(batchNo)) {
+ return batchNo;
+ }
+ sequence++;
+ }
+ throw new ServiceException("鎵瑰彿搴忓彿瓒呭嚭鑼冨洿锛岃妫�鏌ユ壒鍙锋暟鎹�");
+ }
+
+ private int resolveMaxSequence(String prefix) {
+ int maxSequence = 0;
+ List<StockInventory> stockInventoryList = stockInventoryMapper.selectList(
+ Wrappers.<StockInventory>lambdaQuery()
+ .select(StockInventory::getBatchNo)
+ .likeRight(StockInventory::getBatchNo, prefix));
+ for (StockInventory stockInventory : stockInventoryList) {
+ maxSequence = Math.max(maxSequence, parseSequence(stockInventory.getBatchNo(), prefix));
+ }
+
+ List<StockInRecord> stockInRecordList = stockInRecordService.list(
+ Wrappers.<StockInRecord>lambdaQuery()
+ .select(StockInRecord::getBatchNo)
+ .likeRight(StockInRecord::getBatchNo, prefix));
+ for (StockInRecord stockInRecord : stockInRecordList) {
+ maxSequence = Math.max(maxSequence, parseSequence(stockInRecord.getBatchNo(), prefix));
+ }
+ return maxSequence;
+ }
+
+ private int parseSequence(String batchNo, String prefix) {
+ if (StringUtils.isEmpty(batchNo) || StringUtils.isEmpty(prefix) || !batchNo.startsWith(prefix)) {
+ return 0;
+ }
+ String sequenceText = batchNo.substring(prefix.length());
+ if (StringUtils.isEmpty(sequenceText) || !sequenceText.matches("\\d+")) {
+ return 0;
+ }
+ try {
+ return Integer.parseInt(sequenceText);
+ } catch (NumberFormatException ignored) {
+ return 0;
+ }
+ }
+
+ private boolean isBatchNoExists(String batchNo) {
+ if (StringUtils.isEmpty(batchNo)) {
+ return false;
+ }
+ Long inventoryCount = stockInventoryMapper.selectCount(
+ Wrappers.<StockInventory>lambdaQuery().eq(StockInventory::getBatchNo, batchNo));
+ if (inventoryCount != null && inventoryCount > 0) {
+ return true;
+ }
+ return stockInRecordService.count(
+ Wrappers.<StockInRecord>lambdaQuery().eq(StockInRecord::getBatchNo, batchNo)) > 0;
+ }
}
diff --git a/src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java b/src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java
index e41540d..d3719d6 100644
--- a/src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java
+++ b/src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java
@@ -23,5 +23,8 @@
@Schema(description = "瑙勬牸鍨嬪彿")
private String model;
+ @Schema(description = "浜у搧缂栫爜")
+ private String productCode;
+
private List<TechnologyBomStructureVo> children;
}
diff --git a/src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java b/src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java
index 226973f..9e35143 100644
--- a/src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java
+++ b/src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java
@@ -76,7 +76,7 @@
@PreAuthorize("@ss.hasPermi('product:bom:export')")
@Operation(summary = "瀵煎嚭BOM鏂囦欢")
@Log(title = "瀵煎嚭BOM鏂囦欢", businessType = BusinessType.EXPORT)
- public void exportBom(HttpServletResponse response, @RequestParam Integer bomId) {
+ public void exportBom(HttpServletResponse response, @RequestParam Long bomId) {
technologyBomService.exportBom(response, bomId);
}
diff --git a/src/main/java/com/ruoyi/technology/service/TechnologyBomService.java b/src/main/java/com/ruoyi/technology/service/TechnologyBomService.java
index 31fd335..d4aa3d3 100644
--- a/src/main/java/com/ruoyi/technology/service/TechnologyBomService.java
+++ b/src/main/java/com/ruoyi/technology/service/TechnologyBomService.java
@@ -26,7 +26,7 @@
R uploadBom(MultipartFile file);
- void exportBom(HttpServletResponse response, Integer bomId);
+ void exportBom(HttpServletResponse response, Long bomId);
R copy(TechnologyBom technologyBom);
}
diff --git a/src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java b/src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java
index bac4f37..8894288 100644
--- a/src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java
+++ b/src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java
@@ -15,8 +15,7 @@
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.R;
-import com.ruoyi.production.bean.dto.BomImportDto;
-import com.ruoyi.production.bean.dto.ProductStructureDto;
+import com.ruoyi.technology.bean.dto.BomImportDto;
import com.ruoyi.technology.bean.dto.TechnologyBomDto;
import com.ruoyi.technology.bean.dto.TechnologyBomStructureDto;
import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo;
@@ -263,70 +262,72 @@
@Override
- public void exportBom(HttpServletResponse response, Integer bomId) {
+ public void exportBom(HttpServletResponse response, Long bomId) {
if (bomId == null) {
+ throw new ServiceException("BOM ID涓嶈兘涓虹┖");
+ }
+
+ List<TechnologyBomStructureVo> treeData = technologyBomStructureService.listByBomId(bomId);
+ if (treeData == null || treeData.isEmpty()) {
return;
}
-// List<ProductStructureDto> treeData = productStructureService.listBybomId(bomId);
-// if (treeData == null || treeData.isEmpty()) {
-// return;
-// }
-//
-// // 灏嗘爲褰㈢粨鏋勬墎骞冲寲 浣跨敤 BFS绠楁硶 瀵煎嚭,鎸夊眰绾ч『搴�
-// List<BomImportDto> exportList = new ArrayList<>();
-//
-// // Map<ID, Node> idMap 鐢ㄤ簬鏌ユ壘鐖惰妭鐐�
-// Map<Long, ProductStructureDto> idMap = new HashMap<>();
-// populateMap(treeData, idMap);
-//
-// // treeData 鐨勭涓�涓槸鏍硅妭鐐�
-// for (ProductStructureDto root : treeData) {
-// // 娣诲姞鏍硅妭鐐�
-// BomImportDto rootRow = new BomImportDto();
-// rootRow.setParentName(root.getProductName());
-// rootRow.setParentSpec(root.getModel());
-// rootRow.setUnitQty(root.getUnitQuantity());
-// rootRow.setRemark("");
-// exportList.add(rootRow);
-//
-// // BFS 閬嶅巻-闃熷垪
-// Queue<ProductStructureDto> queue = new LinkedList<>();
-// if (root.getChildren() != null) {
-// queue.addAll(root.getChildren());
-// }
-//
-// while (!queue.isEmpty()) {
-// ProductStructureDto child = queue.poll();
-//
-// // 鏌ユ壘鐖惰妭鐐�
-// ProductStructureDto parent = idMap.get(child.getParentId());
-// if (parent == null) {
-// // 闄や簡鏈�澶栧眰鑺傜偣,鍏朵粬鑺傜偣鐨勭埗绫昏偗瀹氭槸涓嶄細涓虹┖鐨�
-// continue;
-// }
-//
-// BomImportDto row = new BomImportDto();
-// // 鐖剁被淇℃伅
-// row.setParentName(parent.getProductName());
-// row.setParentSpec(parent.getModel());
-// // 瀛愮被淇℃伅
-// row.setChildName(child.getProductName());
-// row.setChildSpec(child.getModel());
-// row.setUnitQty(child.getUnitQuantity());
-// row.setProcess(child.getProcessName());
-//
-// exportList.add(row);
-//
-// // 灏嗗瓙鑺傜偣鐨勫瓙鑺傜偣鍔犲叆闃熷垪-涓嬩竴灞�
-// if (child.getChildren() != null && !child.getChildren().isEmpty()) {
-// queue.addAll(child.getChildren());
-// }
-// }
-// }
+ // 灏嗘爲褰㈢粨鏋勬墎骞冲寲 浣跨敤 BFS绠楁硶 瀵煎嚭,鎸夊眰绾ч『搴�
+ List<BomImportDto> exportList = new ArrayList<>();
+
+ // Map<ID, Node> idMap 鐢ㄤ簬鏌ユ壘鐖惰妭鐐�
+ Map<Long, TechnologyBomStructureVo> idMap = new HashMap<>();
+ populateMap(treeData, idMap);
+
+ // treeData 鐨勭涓�涓槸鏍硅妭鐐�
+ for (TechnologyBomStructureVo root : treeData) {
+ // 娣诲姞鏍硅妭鐐�
+ BomImportDto rootRow = new BomImportDto();
+ rootRow.setParentName(root.getProductName());
+ rootRow.setParentSpec(root.getModel());
+ rootRow.setUnitQty(root.getUnitQuantity());
+ rootRow.setParentCode(root.getProductCode());
+ rootRow.setRemark("");
+ exportList.add(rootRow);
+
+ // BFS 閬嶅巻-闃熷垪
+ Queue<TechnologyBomStructureVo> queue = new LinkedList<>();
+ if (root.getChildren() != null) {
+ queue.addAll(root.getChildren());
+ }
+
+ while (!queue.isEmpty()) {
+ TechnologyBomStructureVo child = queue.poll();
+
+ // 鏌ユ壘鐖惰妭鐐�
+ TechnologyBomStructureVo parent = idMap.get(child.getParentId());
+ if (parent == null) {
+ // 闄や簡鏈�澶栧眰鑺傜偣,鍏朵粬鑺傜偣鐨勭埗绫昏偗瀹氭槸涓嶄細涓虹┖鐨�
+ continue;
+ }
+
+ BomImportDto row = new BomImportDto();
+ // 鐖剁被淇℃伅
+ row.setParentName(parent.getProductName());
+ row.setParentSpec(parent.getModel());
+ // 瀛愮被淇℃伅
+ row.setChildName(child.getProductName());
+ row.setChildSpec(child.getModel());
+ row.setUnitQty(child.getUnitQuantity());
+ row.setProcess(child.getOperationName());
+ row.setChildCode(child.getProductCode());
+
+ exportList.add(row);
+
+ // 灏嗗瓙鑺傜偣鐨勫瓙鑺傜偣鍔犲叆闃熷垪-涓嬩竴灞�
+ if (child.getChildren() != null && !child.getChildren().isEmpty()) {
+ queue.addAll(child.getChildren());
+ }
+ }
+ }
ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
-// util.exportExcel(response, exportList, "BOM缁撴瀯瀵煎嚭");
+ util.exportExcel(response, exportList, "BOM缁撴瀯瀵煎嚭");
}
@Override
@@ -408,11 +409,11 @@
return s.replaceAll("[\\u00A0\\u3000]", "").trim();
}
- private void populateMap(List<ProductStructureDto> nodes, Map<Long, ProductStructureDto> map) {
+ private void populateMap(List<TechnologyBomStructureVo> nodes, Map<Long, TechnologyBomStructureVo> map) {
if (nodes == null || nodes.isEmpty()) {
return;
}
- for (ProductStructureDto node : nodes) {
+ for (TechnologyBomStructureVo node : nodes) {
map.put(node.getId(), node);
populateMap(node.getChildren(), map);
}
diff --git a/src/main/java/com/ruoyi/warehouse/mapper/DocumentationMapper.java b/src/main/java/com/ruoyi/warehouse/mapper/DocumentationMapper.java
index 33278bf..85cceec 100644
--- a/src/main/java/com/ruoyi/warehouse/mapper/DocumentationMapper.java
+++ b/src/main/java/com/ruoyi/warehouse/mapper/DocumentationMapper.java
@@ -2,7 +2,6 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
import com.ruoyi.warehouse.dto.DocumentationDto;
import com.ruoyi.warehouse.pojo.Documentation;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 334e5d7..6d34f6b 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -28,7 +28,7 @@
# 寮�鍙戠幆澧冮厤缃�
server:
# 鏈嶅姟鍣ㄧ殑HTTP绔彛锛岄粯璁や负8080
- port: 7005
+ port: 7006
servlet:
# 搴旂敤鐨勮闂矾寰�
context-path: /
@@ -260,7 +260,7 @@
upload-dir: D:/ruoyi/prod/uploads # 姝e紡鐩綍 鍚庢湡鍒犻櫎
path: D:/ruoyi/prod/uploads # 涓婁紶鐩綍
urlPrefix: /common # 閾炬帴鍓嶇紑
- domain: http://127.0.0.1:7005 # 鍩熷悕鍓嶇紑
+ domain: http://127.0.0.1:7006 # 鍩熷悕鍓嶇紑
expired: 120 # 杩囨湡鏃堕棿(鍗曚綅:鍒嗛挓)
useLimit: 10 # 浣跨敤娆℃暟
compress: true # 鏄惁鍘嬬缉
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index d69a572..9bffe41 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 鏃ュ織瀛樻斁璺緞 -->
- <property name="log.path" value="/home/ruoyi/logs" />
+ <property name="log.path" value="./logs" />
<!-- 鏃ュ織杈撳嚭鏍煎紡 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
@@ -11,7 +11,7 @@
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
-
+
<!-- 绯荤粺鏃ュ織杈撳嚭 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
@@ -34,7 +34,7 @@
<onMismatch>DENY</onMismatch>
</filter>
</appender>
-
+
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村垱寤烘棩蹇楁枃浠� -->
@@ -56,7 +56,7 @@
<onMismatch>DENY</onMismatch>
</filter>
</appender>
-
+
<!-- 鐢ㄦ埛璁块棶鏃ュ織杈撳嚭 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
@@ -70,7 +70,7 @@
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
-
+
<!-- 绯荤粺妯″潡鏃ュ織绾у埆鎺у埗 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring鏃ュ織绾у埆鎺у埗 -->
@@ -79,15 +79,15 @@
<root level="info">
<appender-ref ref="console" />
</root>
-
+
<!--绯荤粺鎿嶄綔鏃ュ織-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
-
+
<!--绯荤粺鐢ㄦ埛鎿嶄綔鏃ュ織-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/src/main/resources/manufacturing-agent-prompt.txt b/src/main/resources/manufacturing-agent-prompt.txt
new file mode 100644
index 0000000..c1a30c8
--- /dev/null
+++ b/src/main/resources/manufacturing-agent-prompt.txt
@@ -0,0 +1,8 @@
+浣犳槸浼佷笟鍒堕�犳櫤鑳藉姪鎵嬶紝瑕嗙洊鐢熶骇鐜板満銆佽鍒掋�佸伐鍗曘�佽澶囥�佽川閲忋�佺墿鏂欍�佸紓甯稿鐞嗕竷涓煙銆�
+
+宸ヤ綔瑙勫垯锛�
+1. 鐢ㄦ埛鎻愬嚭鈥滄煡銆侀棶銆侀璀︺�佸垎鏋愨�濋渶姹傛椂锛屼紭鍏堣皟鐢ㄥ伐鍏锋嬁缁撴瀯鍖栫粨鏋滐紝涓嶈鑷嗛�犱笟鍔℃暟鎹��
+2. 鐢ㄦ埛鎻愬嚭鈥滃姙鈥濋渶姹傛椂锛屼紭鍏堣緭鍑哄姙鐞嗗缓璁姩浣滃崱锛堟帴鍙c�佸繀濉瓧娈点�佺ず渚嬶級锛屾槑纭渶瑕佸墠绔簩娆$‘璁ゃ��
+3. 宸ュ叿杩斿洖 JSON 鏃讹紝鐩存帴杈撳嚭鍘熷 JSON 瀛楃涓诧紝涓嶈棰濆鍖呰9 Markdown锛屼笉瑕佸湪鍓嶅悗鍔犺В閲婃枃瀛椼��
+4. 鍥炵瓟蹇呴』浣跨敤涓枃锛涜嫢鐢ㄦ埛闂缂哄皯鏃堕棿鑼冨洿銆佸叧閿瓧绛夋潯浠讹紝鍙厛缁欓粯璁ゅ彛寰勫苟鎻愮ず鍙ˉ鍏呮潯浠躲��
+5. 鑻ユ棤娉曚粠宸ュ叿缁撴灉寰楀埌缁撹锛屾槑纭鏄庣己灏戠殑绛涢�夋潯浠舵垨涓氬姟瀛楁銆�
diff --git a/src/main/resources/mapper/account/AccountSubjectMapper.xml b/src/main/resources/mapper/account/AccountSubjectMapper.xml
index 179d858..95f450f 100644
--- a/src/main/resources/mapper/account/AccountSubjectMapper.xml
+++ b/src/main/resources/mapper/account/AccountSubjectMapper.xml
@@ -5,6 +5,7 @@
<!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
<resultMap id="BaseResultMap" type="com.ruoyi.account.pojo.AccountSubject">
<id column="id" property="id" />
+ <result column="parent_id" property="parentId" />
<result column="subject_code" property="subjectCode" />
<result column="subject_name" property="subjectName" />
<result column="subject_type" property="subjectType" />
@@ -18,4 +19,13 @@
<result column="dept_id" property="deptId" />
</resultMap>
+ <select id="countReferencedBySubjectCodes" resultType="java.lang.Long">
+ SELECT COUNT(1)
+ FROM fin_voucher_entry
+ WHERE subject_code IN
+ <foreach collection="subjectCodes" item="item" open="(" separator="," close=")">
+ #{item}
+ </foreach>
+ </select>
+
</mapper>
diff --git a/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml b/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml
new file mode 100644
index 0000000..56633ba
--- /dev/null
+++ b/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.account.mapper.financial.FinVoucherEntryMapper">
+
+ <resultMap id="BaseResultMap" type="com.ruoyi.account.pojo.financial.FinVoucherEntry">
+ <id column="id" property="id"/>
+ <result column="voucher_id" property="voucherId"/>
+ <result column="row_no" property="rowNo"/>
+ <result column="subject_code" property="subjectCode"/>
+ <result column="subject_name" property="subjectName"/>
+ <result column="summary" property="summary"/>
+ <result column="debit" property="debit"/>
+ <result column="credit" property="credit"/>
+ <result column="auxiliary_type" property="auxiliaryType"/>
+ <result column="auxiliary_id" property="auxiliaryId"/>
+ <result column="auxiliary_name" property="auxiliaryName"/>
+ <result column="create_user" property="createUser"/>
+ <result column="create_time" property="createTime"/>
+ <result column="update_user" property="updateUser"/>
+ <result column="update_time" property="updateTime"/>
+ <result column="dept_id" property="deptId"/>
+ </resultMap>
+
+ <select id="listPostedEntries" resultType="com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo">
+ SELECT
+ v.voucher_date AS voucherDate,
+ v.voucher_no AS voucherNo,
+ CASE
+ WHEN e.summary IS NOT NULL AND e.summary != '' THEN e.summary
+ ELSE v.summary
+ END AS summary,
+ e.debit AS debit,
+ e.credit AS credit,
+ e.row_no AS rowNo
+ FROM fin_voucher_entry e
+ INNER JOIN fin_voucher v ON e.voucher_id = v.id
+ WHERE v.status = 'posted'
+ AND (e.subject_code = #{subjectCode} OR e.subject_code LIKE CONCAT(#{subjectCode}, '%'))
+ AND v.voucher_date <![CDATA[>=]]> #{startDate}
+ AND v.voucher_date <![CDATA[<=]]> #{endDate}
+ <if test="auxiliaryType != null and auxiliaryType != ''">
+ AND e.auxiliary_type = #{auxiliaryType}
+ </if>
+ <if test="auxiliaryId != null and auxiliaryId != ''">
+ AND e.auxiliary_id = #{auxiliaryId}
+ </if>
+ ORDER BY v.voucher_date ASC, v.id ASC, e.row_no ASC, e.id ASC
+ </select>
+
+ <select id="listPostedEntriesBefore" resultType="com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo">
+ SELECT
+ v.voucher_date AS voucherDate,
+ v.voucher_no AS voucherNo,
+ CASE
+ WHEN e.summary IS NOT NULL AND e.summary != '' THEN e.summary
+ ELSE v.summary
+ END AS summary,
+ e.debit AS debit,
+ e.credit AS credit,
+ e.row_no AS rowNo
+ FROM fin_voucher_entry e
+ INNER JOIN fin_voucher v ON e.voucher_id = v.id
+ WHERE v.status = 'posted'
+ AND (e.subject_code = #{subjectCode} OR e.subject_code LIKE CONCAT(#{subjectCode}, '%'))
+ AND v.voucher_date <![CDATA[<]]> #{beforeDate}
+ <if test="auxiliaryType != null and auxiliaryType != ''">
+ AND e.auxiliary_type = #{auxiliaryType}
+ </if>
+ <if test="auxiliaryId != null and auxiliaryId != ''">
+ AND e.auxiliary_id = #{auxiliaryId}
+ </if>
+ </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/basic/CustomerMapper.xml b/src/main/resources/mapper/basic/CustomerMapper.xml
index 57dea6c..20aaefc 100644
--- a/src/main/resources/mapper/basic/CustomerMapper.xml
+++ b/src/main/resources/mapper/basic/CustomerMapper.xml
@@ -26,6 +26,7 @@
from customer c
left join sys_user u on c.usage_user = u.user_id
<where>
+ and c.usage_status = 1
<if test="c.customerName != null and c.customerName != ''">
and customer_name like concat('%', #{c.customerName}, '%')
</if>
@@ -107,4 +108,4 @@
</if>
</where>
</select>
-</mapper>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml b/src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml
index 5da6dff..d8a5956 100644
--- a/src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml
+++ b/src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml
@@ -4,13 +4,27 @@
<select id="listPage" resultType="com.ruoyi.collaborativeApproval.dto.SealApplicationManagementDTO">
- select sam.*, su.user_name as create_user_name, d.dept_name as department,
- su1.nick_name as approveUserName
- from seal_application_management sam
- left join sys_user su on sam.create_user = su.user_id
- left join sys_user su1 on sam.approve_user_id = su1.user_id
- left join sys_user_dept sud on su.user_id = sud.user_id
- left join sys_dept d on sud.dept_id = d.dept_id
+ SELECT
+ sam.*,
+ su.user_name AS create_user_name,
+
+ GROUP_CONCAT(DISTINCT d.dept_name ORDER BY d.dept_id SEPARATOR ',') AS department,
+
+ su1.nick_name AS approveUserName
+
+ FROM seal_application_management sam
+
+ LEFT JOIN sys_user su
+ ON sam.create_user = su.user_id
+
+ LEFT JOIN sys_user su1
+ ON sam.approve_user_id = su1.user_id
+
+ LEFT JOIN sys_user_dept sud
+ ON su.user_id = sud.user_id
+
+ LEFT JOIN sys_dept d
+ ON sud.dept_id = d.dept_id
<where>
<if test="ew.applicationNum != null and ew.applicationNum != ''">
and sam.application_num like concat('%',#{ew.applicationNum},'%')
@@ -22,5 +36,6 @@
and sam.status = #{ew.status}
</if>
</where>
+ GROUP BY sam.id
</select>
</mapper>
diff --git a/src/main/resources/mapper/device/DeviceRepairMapper.xml b/src/main/resources/mapper/device/DeviceRepairMapper.xml
index 2eb1d29..713e346 100644
--- a/src/main/resources/mapper/device/DeviceRepairMapper.xml
+++ b/src/main/resources/mapper/device/DeviceRepairMapper.xml
@@ -11,10 +11,13 @@
dr.repair_time,
dr.repair_name,
dr.remark,
- dr.maintenance_name,
- dr.maintenance_time,
- dr.maintenance_result,
- dr.status,
+ dr.maintenance_name,
+ dr.maintenance_time,
+ dr.maintenance_result,
+ dr.acceptance_name,
+ dr.acceptance_time,
+ dr.acceptance_remark,
+ dr.status,
dr.create_time,
dr.update_time,
dr.create_user,
@@ -60,6 +63,9 @@
dr.maintenance_name,
dr.maintenance_time,
dr.maintenance_result,
+ dr.acceptance_name,
+ dr.acceptance_time,
+ dr.acceptance_remark,
dr.status,
dr.create_time,
dr.update_time,
diff --git a/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml b/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml
index e4200af..65741f5 100644
--- a/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml
+++ b/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml
@@ -15,7 +15,9 @@
next_date,
record_date,
CASE
- WHEN most_date >= DATE_FORMAT(now(),'%Y-%m-%d') THEN 1
+ WHEN most_date IS NOT NULL
+ AND valid IS NOT NULL
+ AND DATE_ADD(most_date, INTERVAL valid DAY) >= CURDATE() THEN 1
ELSE 2
END AS status,
create_user,
@@ -40,10 +42,16 @@
<if test="req.status != null">
<choose>
<when test="req.status == 1">
- AND most_date >= DATE_FORMAT(now(),'%Y-%m-%d')
+ AND most_date IS NOT NULL
+ AND valid IS NOT NULL
+ AND DATE_ADD(most_date, INTERVAL valid DAY) >= CURDATE()
</when>
<when test="req.status == 2">
- AND most_date < DATE_FORMAT(now(),'%Y-%m-%d')
+ AND (
+ most_date IS NULL
+ OR valid IS NULL
+ OR DATE_ADD(most_date, INTERVAL valid DAY) < CURDATE()
+ )
</when>
</choose>
</if>
diff --git a/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml b/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
index b0166ca..f1a87ca 100644
--- a/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
@@ -2,7 +2,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper">
- <select id="listProcurementBySalesLedgerId" resultType="com.ruoyi.procurementrecord.dto.ProcurementDto">
+ <select id="listProcurementBySalesLedgerId" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementDto">
select
t1.supplier_name,
t2.product_category,
@@ -28,7 +28,7 @@
</if>
group by t2.id
</select>
- <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
+ <select id="listPage" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
select
t3.supplier_name,
t3.purchase_contract_number,
@@ -68,7 +68,7 @@
</where>
order by t1.create_time desc
</select>
- <select id="list" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
+ <select id="list" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
select
t3.supplier_name,
t3.purchase_contract_number,
@@ -92,7 +92,7 @@
left join purchase_ledger t3 on t3.id = t2.sales_ledger_id
where t1.type = 1
</select>
- <select id="listOne" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
+ <select id="listOne" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
select
t3.customer_contract_no,
t3.sales_contract_no,
@@ -117,7 +117,7 @@
left join sales_ledger t3 on t3.id = t2.sales_ledger_id
where t1.type = 2
</select>
- <select id="listPageCopy" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy">
+ <select id="listPageCopy" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy">
select
t3.supplier_name,
t3.purchase_contract_number,
@@ -175,7 +175,7 @@
group by t3.supplier_name,t2.product_category,t2.specification_model,t1.unit_price
order by t1.create_time desc
</select>
- <select id="listCopy" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy">
+ <select id="listCopy" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy">
select
t3.supplier_name,
t3.purchase_contract_number,
@@ -203,7 +203,7 @@
where t1.type = 1
group by t3.supplier_name,t2.product_category,t2.specification_model
</select>
- <select id="listCopyOne" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy">
+ <select id="listCopyOne" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy">
select
t3.customer_contract_no,
t3.sales_contract_no,
@@ -232,7 +232,7 @@
where t1.type = 2
group by t3.customer_name,t2.product_category,t2.specification_model
</select>
- <select id="listPageByProduction" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
+ <select id="listPageByProduction" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
select
t3.customer_contract_no,
t3.sales_contract_no,
@@ -274,7 +274,7 @@
</where>
order by t1.create_time desc
</select>
- <select id="listPageCopyByProduction" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy">
+ <select id="listPageCopyByProduction" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy">
select
t3.customer_contract_no,
t3.sales_contract_no,
@@ -336,7 +336,7 @@
group by t2.product_category,t2.specification_model,t1.unit_price
order by t1.create_time desc
</select>
- <select id="listPagePRS" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDtoCopy">
+ <select id="listPagePRS" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDtoCopy">
select *
from procurement_record_storage t1
left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id
@@ -358,7 +358,7 @@
from procurement_record_storage
where product_model_id = #{productModelId}
</select>
- <select id="listPageByProductProduction" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
+ <select id="listPageByProductProduction" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
select
t1.*,
t1.inbound_num as inboundNum0,
@@ -380,4 +380,4 @@
</where>
order by t1.create_time desc
</select>
-</mapper>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml b/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
index adc64dd..46925aa 100644
--- a/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
@@ -2,7 +2,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper">
- <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listPage" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select
t3.supplier_name,
t2.product_category,
@@ -38,7 +38,7 @@
</where>
order by t1.create_time desc
</select>
- <select id="list" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="list" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select t3.supplier_name,
t2.product_category,
t1.id,
@@ -58,7 +58,7 @@
where t1.type = 1
</select>
- <select id="listOne" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listOne" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select t3.customer_contract_no,
t3.sales_contract_no,
t3.customer_name,
@@ -80,7 +80,7 @@
where t1.type = 2
</select>
- <select id="listTwo" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listTwo" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select t1.supplier_name,
t1.product_category,
t1.id,
@@ -97,7 +97,7 @@
from procurement_record_out t1
where t1.type = 3
</select>
- <select id="listPageByProduct" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listPageByProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select
t3.customer_contract_no,
t3.sales_contract_no,
@@ -134,7 +134,7 @@
</where>
order by t1.create_time desc
</select>
- <select id="listPageByCustom" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listPageByCustom" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select
t2.supplier_name,
t2.product_category,
@@ -180,7 +180,7 @@
order by id desc
limit 1
</select>
- <select id="listPageBySemiProduct" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
+ <select id="listPageBySemiProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select
t1.id,
t1.code,
@@ -206,4 +206,4 @@
order by t1.create_time desc
</select>
-</mapper>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml b/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
index a06b4f0..d348a6b 100644
--- a/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
@@ -2,7 +2,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.procurementrecord.mapper.ReturnManagementMapper">
- <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ReturnManagementDto">
+ <select id="listPage" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto">
select rm.*,
c.customer_name,
si.shipping_no,
@@ -40,7 +40,7 @@
</if>
</where>
</select>
- <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.dto.ReturnManagementDto">
+ <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto">
select rm.*,
c.customer_name,
si.shipping_no,
@@ -53,7 +53,7 @@
left join sales_ledger sl on si.sales_ledger_id = sl.id
where rm.id = #{id}
</select>
- <select id="listPageBySalesReturn" resultType="com.ruoyi.account.bean.vo.SalesReturnVo">
+ <select id="listPageAccountSalesReturn" resultType="com.ruoyi.account.bean.vo.SalesReturnVo">
select rm.id,
rm.return_no,
c.customer_name,
@@ -75,7 +75,7 @@
and c.customer_name like concat('%',#{req.customerName},'%')
</if>
<if test="req.startDate != null and req.endDate != null">
- AND DATE_FORMAT(rm.make_time, '%Y-%m-%d') BETWEEN #{startDate} AND #{endDate}
+ AND DATE_FORMAT(rm.make_time, '%Y-%m-%d') BETWEEN #{req.startDate} AND #{req.endDate}
</if>
order by rm.id DESC
</select>
diff --git a/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml b/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
index 52345e3..6b15d56 100644
--- a/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
@@ -6,35 +6,51 @@
<resultMap id="BaseResultMap" type="com.ruoyi.procurementrecord.pojo.ReturnSaleProduct">
<id column="id" property="id" />
<result column="return_management_id" property="returnManagementId" />
- <result column="return_sales_ledger_product_id" property="returnsalesLedgerProductId" />
+ <result column="stock_out_record_id" property="stockOutRecordId" />
<result column="num" property="num" />
<result column="status" property="status" />
</resultMap>
- <select id="listReturnSaleProductDto" resultType="com.ruoyi.procurementrecord.dto.ReturnSaleProductDto">
- SELECT p.product_name as product_name,
+ <select id="listReturnSaleProductDto" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto">
+ SELECT distinct
+ p.product_name as product_name,
pm.model as model,
pm.unit as unit,
rsp.*,
- GREATEST(slp.quantity - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
+ sor.outbound_batches,
+ sor.stock_out_num,
+ sor.batch_no,
+ GREATEST(sor.stock_out_num - COALESCE(rs1.total_return_num1, 0), 0) AS un_quantity,
COALESCE(rs.total_return_num, 0) AS total_return_num
FROM return_sale_product rsp
LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
LEFT JOIN shipping_info si ON si.id = rm.shipping_id
+ LEFT JOIN shipping_product_detail spd ON spd.shipping_info_id = si.id
+ LEFT JOIN stock_out_record sor ON rsp.stock_out_record_id = sor.id and sor.record_type = '13'
LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
left join product_model pm on slp.product_model_id = pm.id
LEFT JOIN product p on pm.product_id = p.id
- LEFT JOIN (SELECT return_sales_ledger_product_id,
-
+ LEFT JOIN (SELECT stock_out_record_id,
SUM(num) AS total_return_num
FROM return_sale_product
- WHERE 1 = 1 and return_management_id != #{returnManagementId}
- GROUP BY return_sales_ledger_product_id) rs ON rs.return_sales_ledger_product_id = slp.id
+ WHERE 1 = 1 and return_management_id = #{returnManagementId}
+ GROUP BY stock_out_record_id) rs ON rs.stock_out_record_id = sor.id
+ LEFT JOIN (SELECT stock_out_record_id,
+ SUM(num) AS total_return_num1
+ FROM return_sale_product
+ WHERE 1 = 1
+ GROUP BY stock_out_record_id) rs1 ON rs1.stock_out_record_id = sor.id
where rm.id =#{returnManagementId}
</select>
- <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.dto.ReturnSaleProductDto">
- select rsp.*,slp.tax_inclusive_unit_price ,slp.tax_inclusive_total_price*rsp.num as price
+ <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto">
+ select rsp.*,
+ sor.batch_no,
+ slp.tax_inclusive_unit_price ,
+ slp.tax_inclusive_total_price*rsp.num as price
from return_sale_product rsp
- left join sales_ledger_product slp on slp.id = rsp.return_sales_ledger_product_id
+ LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
+ LEFT JOIN shipping_info si ON si.id = rm.shipping_id
+ LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
+ LEFT JOIN stock_out_record sor ON rsp.stock_out_record_id = sor.id
where rsp.return_management_id = #{returnManagementId}
</select>
diff --git a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
index 6db5455..f029ef7 100644
--- a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -159,6 +159,7 @@
<select id="getOperation" resultType="com.ruoyi.production.bean.vo.ProductionOperationTaskVo">
select poro.operation_name as operationName,
+ max(poro.type) as type,
count(pot.id) as productionTaskCount,
sum(ifnull(pot.plan_quantity, 0)) as planQuantity,
sum(ifnull(pot.complete_quantity, 0)) as completeQuantity,
diff --git a/src/main/resources/mapper/purchase/PurchaseReturnOrderProductsMapper.xml b/src/main/resources/mapper/purchase/PurchaseReturnOrderProductsMapper.xml
index cd13f3c..ec1efe1 100644
--- a/src/main/resources/mapper/purchase/PurchaseReturnOrderProductsMapper.xml
+++ b/src/main/resources/mapper/purchase/PurchaseReturnOrderProductsMapper.xml
@@ -13,7 +13,9 @@
</resultMap>
<select id="getReturnOrderGroupListByProductIds" resultType="com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto"
parameterType="java.util.List">
- select t1.sales_ledger_product_id,sum(t1.return_quantity) as sum_return_quantity from purchase_return_order_products as t1
+ select t1.sales_ledger_product_id,
+ sum(t1.return_quantity) as sum_return_quantity
+ from purchase_return_order_products as t1
inner join purchase_return_orders as t2 on t1.purchase_return_order_id = t2.id
WHERE t1.sales_ledger_product_id IN
<foreach item="id" collection="productIds" separator="," open="(" close=")">
diff --git a/src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml b/src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml
index dca2c47..6732a66 100644
--- a/src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml
+++ b/src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml
@@ -57,16 +57,22 @@
resultType="com.ruoyi.account.bean.vo.PurchaseReturnVo">
select pro.id,
pro.no returnNo,
+ t.inboundBatches,
sm.supplier_name,
-
pro.prepared_at,
pro.total_amount,
CASE pro.return_type WHEN 0 THEN '閫�璐ч��娆�' WHEN 1 THEN '鎷掓敹' END AS returnType,
pl.purchase_contract_number
from purchase_return_orders pro
- left join supplier_manage sm on pro.supplier_id = sm.id
- left join purchase_ledger pl on pro.purchase_ledger_id = pl.id
- where rm.status=1
+ left join
+ (select prop.purchase_return_order_id,
+ GROUP_CONCAT(sir.inbound_batches SEPARATOR ',') AS inboundBatches
+ from purchase_return_order_products prop
+ left join stock_in_record sir on prop.stock_in_record_id = sir.id
+ GROUP BY prop.purchase_return_order_id) t on t.purchase_return_order_id = pro.id
+ left join supplier_manage sm on pro.supplier_id = sm.id
+ left join purchase_ledger pl on pro.purchase_ledger_id = pl.id
+ where 1=1
<if test="req.returnNo != null and req.returnNo != ''">
and pro.no like concat('%',#{req.returnNo},'%')
</if>
@@ -74,8 +80,74 @@
and sm.supplier_name like concat('%',#{req.supplierName},'%')
</if>
<if test="req.startDate != null and req.endDate != null">
- AND DATE_FORMAT(pro.prepared_at, '%Y-%m-%d') BETWEEN #{startDate} AND #{endDate}
+ AND DATE_FORMAT(pro.prepared_at, '%Y-%m-%d') BETWEEN #{req.startDate} AND #{req.endDate}
</if>
order by pro.id DESC
</select>
+ <select id="getByPurchaseLedgerId" resultType="com.ruoyi.purchase.vo.PurchaseStockInProductVo">
+ SELECT
+ sir.id,
+ sir.product_model_id,
+ slp.id salesLedgerProductId,
+ slp.product_category,
+ slp.specification_model,
+ slp.unit,
+ slp.is_checked,
+ sir.inbound_batches,
+ sir.stock_in_num,
+ sir.batch_no,
+ slp.tax_inclusive_unit_price,
+ GREATEST(sir.stock_in_num - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
+ COALESCE(rs.total_return_num, 0) AS total_return_num
+ FROM stock_in_record sir
+ LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
+ LEFT JOIN purchase_ledger pl
+ ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
+ LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id
+ LEFT JOIN (
+ SELECT
+ stock_in_record_id,
+ SUM(return_quantity) AS total_return_num
+ FROM purchase_return_order_products prop
+ left join purchase_return_orders pro on pro.id = prop.purchase_return_order_id
+ WHERE 1=1
+ GROUP BY stock_in_record_id
+ ) rs ON rs.stock_in_record_id = sir.id
+ WHERE sir.approval_status = 1 AND slp.type = 2
+ AND sir.record_type IN ('7','10')
+ and pl.id = #{purchaseLedgerId}
+ </select>
+ <select id="getPurchaseReturnOrderProductsDetailById"
+ resultType="com.ruoyi.purchase.vo.PurchaseReturnOrderProductsDetailVo">
+ select prop.id,
+ prop.sales_ledger_product_id,
+ slp.product_model_id,
+ slp.product_category,
+ slp.specification_model,
+ slp.is_checked,
+ slp.unit,
+ sir.inbound_batches,
+ sir.stock_in_num,
+ sir.batch_no,
+ slp.tax_inclusive_unit_price,
+ prop.return_quantity,
+ prop.purchase_return_order_id,
+ GREATEST(sir.stock_in_num - COALESCE(rs1.total_return_num1, 0), 0) AS un_quantity,
+ COALESCE(rs.total_return_num, 0) AS total_return_num
+ from purchase_return_order_products prop
+ left join purchase_return_orders pro on prop.purchase_return_order_id = pro.id
+ LEFT JOIN stock_in_record sir ON prop.stock_in_record_id = sir.id and sir.record_type in ('7','10')
+ LEFT JOIN sales_ledger_product slp ON prop.sales_ledger_product_id = slp.id and slp.type = 2
+ LEFT JOIN (SELECT stock_in_record_id,
+ SUM(return_quantity) AS total_return_num
+ FROM purchase_return_order_products
+ WHERE 1 = 1 and purchase_return_order_id = #{id}
+ GROUP BY stock_in_record_id) rs ON rs.stock_in_record_id = sir.id
+ LEFT JOIN (SELECT stock_in_record_id,
+ SUM(return_quantity) AS total_return_num1
+ FROM purchase_return_order_products
+ WHERE 1 = 1 and purchase_return_order_id = #{id}
+ GROUP BY stock_in_record_id) rs1 ON rs1.stock_in_record_id = sir.id
+ where pro.id = #{id}
+ </select>
</mapper>
diff --git a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
index f3fa32d..7e352d8 100644
--- a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -48,7 +48,8 @@
END as has_sufficient_stock,
(IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) as no_quantity,
CASE
- WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '寰呭彂璐�'
+ WHEN IFNULL(t3.shipped_quantity, 0) = 0 THEN '寰呭彂璐�'
+ WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '閮ㄥ垎鍙戣揣'
ELSE '宸插彂璐�'
END as shippingStatus
FROM
@@ -62,6 +63,7 @@
SELECT sales_ledger_product_id, IFNULL(SUM(spd.quantity), 0) as shipped_quantity
FROM shipping_info si
LEFT JOIN shipping_product_detail spd ON si.id = spd.shipping_info_id
+ where si.status != '瀹℃牳鎷掔粷'
GROUP BY sales_ledger_product_id
) t3 ON t3.sales_ledger_product_id = T1.id
left join product_model pm ON T1.product_model_id = pm.id
diff --git a/src/main/resources/mapper/sales/SalesQuotationMapper.xml b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
index cf15b63..3c93850 100644
--- a/src/main/resources/mapper/sales/SalesQuotationMapper.xml
+++ b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -20,4 +20,4 @@
AND t1.status = #{salesQuotationDto.status}
</if>
</select>
-</mapper>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/sales/ShippingInfoMapper.xml b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
index ee384ed..762c2d4 100644
--- a/src/main/resources/mapper/sales/ShippingInfoMapper.xml
+++ b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -22,12 +22,19 @@
pm.model as specification_model,
pm.unit,
p.product_name,
- sl.customer_name
+ sl.customer_name,
+ spd.totalQuantity,
+ sor.outboundBatches
FROM shipping_info s
+ LEFT JOIN (select shipping_info_id,sum(quantity) totalQuantity from shipping_product_detail GROUP BY shipping_info_id) spd ON spd.shipping_info_id = s.id
LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id and slp.type = 1
left join product_model pm on slp.product_model_id = pm.id
left join product p on pm.product_id = p.id
+ left join (select record_id,GROUP_CONCAT(outbound_batches SEPARATOR ',') AS outboundBatches
+ from stock_out_record
+ where record_type='13'and approval_status=1
+ group by record_id)sor on sor.record_id= s.id
WHERE 1=1
<if test="req.salesContractNo != null and req.salesContractNo != ''">
AND sl.sales_contract_no LIKE CONCAT('%',#{req.salesContractNo},'%')
@@ -59,33 +66,43 @@
FROM shipping_info s
LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
</select>
- <select id="getReturnManagementDtoById" resultType="com.ruoyi.sales.dto.SalesLedgerProductDto">
- SELECT
- slp.*,
- si.shipping_no,
- GREATEST(slp.quantity - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
- COALESCE(rs.total_return_num, 0) AS total_return_num
+ <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.vo.ShippingProductVo">
+ SELECT distinct
+ sor.id,
+ slp.product_category,
+ slp.specification_model,
+ slp.unit,
+ slp.product_model_id,
+ sor.outbound_batches,
+ sor.stock_out_num,
+ sor.batch_no,
+ slp.tax_inclusive_unit_price,
+ GREATEST(sor.stock_out_num - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
+ COALESCE(rs.total_return_num, 0) AS total_return_num
FROM shipping_info si
+ LEFT JOIN shipping_product_detail spd ON spd.shipping_info_id = si.id
+ LEFT JOIN stock_out_record sor ON sor.record_id = si.id and sor.record_type = '13'
LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
LEFT JOIN (
SELECT
- return_sales_ledger_product_id,
+ stock_out_record_id,
SUM(num) AS total_return_num
FROM return_sale_product rsp
left join return_management rm on rm.id = rsp.return_management_id
left join shipping_info si on si.id = rm.shipping_id
WHERE 1=1
- GROUP BY return_sales_ledger_product_id
- ) rs ON rs.return_sales_ledger_product_id = slp.id
+ GROUP BY stock_out_record_id
+ ) rs ON rs.stock_out_record_id = sor.id
<where>
<if test="shippingId != null">
si.id = #{shippingId}
</if>
</where>
+ order by sor.id
</select>
<select id="getShippingInfoByCustomerName" resultType="com.ruoyi.sales.pojo.ShippingInfo">
select * from shipping_info si
left join sales_ledger sl on si.sales_ledger_id = sl.id
- where si.status = '宸插彂璐�' and sl.customer_name = #{customerName}
+ where sl.customer_name = #{customerName}
</select>
</mapper>
diff --git a/src/main/resources/mapper/stock/StockInRecordMapper.xml b/src/main/resources/mapper/stock/StockInRecordMapper.xml
index 05f8c89..579a464 100644
--- a/src/main/resources/mapper/stock/StockInRecordMapper.xml
+++ b/src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -78,7 +78,7 @@
DATE(sir.create_time) AS inboundDate,
p.product_name,
pm.model as specification_model,
- sor.stock_in_num * slp.tax_inclusive_unit_price AS InboundAmount,
+ sir.stock_in_num * slp.tax_inclusive_unit_price AS InboundAmount,
pl.purchase_contract_number
FROM stock_in_record sir
-- 10 绫诲瀷鎵嶅叧鑱旇川妫�琛�
@@ -87,11 +87,11 @@
LEFT JOIN purchase_ledger pl
ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
-- 浜у搧鍏宠仈涓嶅姩
- LEFT JOIN sales_ledger_product slp ON slp.type = 2 AND pl.id = slp.product_id
- LEFT JOIN product_model pm ON slp.product_model_id = pm.id
+ LEFT JOIN sales_ledger_product slp ON pl.id = slp.product_id
+ LEFT JOIN product_model pm ON sir.product_model_id = pm.id
LEFT JOIN product p ON pm.product_id = p.id
-- 鏉′欢
- WHERE sir.approval_status = 1
+ WHERE sir.approval_status = 1 AND slp.type = 2
AND sir.record_type IN ('7','10')
<if test="req.inboundBatches != null and req.inboundBatches != ''">
AND sir.inbound_batches LIKE CONCAT('%',#{req.inboundBatches},'%')
@@ -100,7 +100,7 @@
AND pl.supplier_name LIKE CONCAT('%',#{req.supplierName},'%')
</if>
<if test="req.startDate != null and req.endDate != null">
- AND DATE(sir.create_time) BETWEEN #{startDate} AND #{endDate}
+ AND DATE(sir.create_time) BETWEEN #{req.startDate} AND #{req.endDate}
</if>
order by sir.id DESC
</select>
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
index 9612933..c71ce31 100644
--- a/src/main/resources/mapper/stock/StockInventoryMapper.xml
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -99,6 +99,138 @@
INNER JOIN product_tree pt ON p.parent_id = pt.id
)
select
+ GROUP_CONCAT(DISTINCT batch_no ORDER BY batch_no SEPARATOR ',') as batch_no,
+ MAX(qualifiedId) as qualifiedId,
+ MAX(unQualifiedId) as unQualifiedId,
+ SUM(qualifiedQuantity) as qualifiedQuantity,
+ SUM(unQualifiedQuantity) as unQualifiedQuantity,
+ SUM(qualifiedLockedQuantity) as qualifiedLockedQuantity,
+ SUM(unQualifiedLockedQuantity) as unQualifiedLockedQuantity,
+ SUM(qualifiedQuantity - qualifiedLockedQuantity - IFNULL(qualifiedPendingOut, 0)) as qualifiedUnLockedQuantity,
+ SUM(unQualifiedQuantity - unQualifiedLockedQuantity - IFNULL(unQualifiedPendingOut, 0)) as unQualifiedUnLockedQuantity,
+ SUM(IFNULL(qualifiedPendingOut, 0)) as qualifiedPendingOutQuantity,
+ SUM(IFNULL(unQualifiedPendingOut, 0)) as unQualifiedPendingOutQuantity,
+ product_model_id,
+ MAX(create_time) as create_time,
+ MAX(update_time) as update_time,
+ MAX(warn_num) as warn_num,
+ MAX(version) as version,
+ model,
+ MAX(remark) as remark,
+ unit,
+ product_name,
+ product_id,
+ 'combined' as stockType
+ from (
+ select
+ si.batch_no,
+ si.id as qualifiedId,
+ null as unQualifiedId,
+ si.qualitity as qualifiedQuantity,
+ 0 as unQualifiedQuantity,
+ COALESCE(si.locked_quantity, 0) as locked_quantity,
+ COALESCE(si.locked_quantity, 0) as qualifiedLockedQuantity,
+ 0 as unQualifiedLockedQuantity,
+ si.product_model_id,
+ si.create_time,
+ si.update_time,
+ COALESCE(si.warn_num, 0) as warn_num,
+ si.version,
+ (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
+ pm.model,
+ si.remark,
+ pm.unit,
+ p.product_name,
+ p.id as product_id,
+ (
+ select IFNULL(SUM(sor.stock_out_num), 0)
+ from stock_out_record sor
+ where sor.product_model_id = si.product_model_id
+ and (
+ (si.batch_no is null and sor.batch_no is null)
+ or si.batch_no = sor.batch_no
+ )
+ and sor.type = '0'
+ and sor.approval_status = 0
+ ) as qualifiedPendingOut,
+ 0 as unQualifiedPendingOut
+ from stock_inventory si
+ left join product_model pm on si.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+
+ union all
+
+ select
+ su.batch_no,
+ null as qualifiedId,
+ su.id as unQualifiedId,
+ 0 as qualifiedQuantity,
+ su.qualitity as unQualifiedQuantity,
+ COALESCE(su.locked_quantity, 0) as locked_quantity,
+ 0 as qualifiedLockedQuantity,
+ COALESCE(su.locked_quantity, 0) as unQualifiedLockedQuantity,
+ su.product_model_id,
+ su.create_time,
+ su.update_time,
+ 0 as warn_num,
+ su.version,
+ (su.qualitity - COALESCE(su.locked_quantity, 0)) as un_locked_quantity,
+ pm.model,
+ su.remark,
+ pm.unit,
+ p.product_name,
+ p.id as product_id,
+ 0 as qualifiedPendingOut,
+ (
+ select IFNULL(SUM(sor.stock_out_num), 0)
+ from stock_out_record sor
+ where sor.product_model_id = su.product_model_id
+ and (
+ (su.batch_no is null and sor.batch_no is null)
+ or su.batch_no = sor.batch_no
+ )
+ and sor.type = '1'
+ and sor.approval_status = 0
+ ) as unQualifiedPendingOut
+ from stock_uninventory su
+ left join product_model pm on su.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+ ) as combined
+ <where>
+ <if test="ew.productName != null and ew.productName !=''">
+ and combined.product_name in (
+ select distinct p.product_name
+ from product p
+ left join product_model pm on p.id = pm.product_id
+ where p.product_name like concat('%',#{ew.productName},'%')
+ or pm.model like concat('%',#{ew.productName},'%')
+ )
+ </if>
+ <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
+ and combined.product_id in (select id from product_tree)
+ </if>
+ </where>
+ group by
+ product_model_id,
+ model,
+ unit,
+ product_name,
+ product_id
+ </select>
+
+ <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
+ WITH RECURSIVE product_tree AS (
+ SELECT id
+ FROM product
+ WHERE id = #{ew.topParentProductId}
+
+ UNION ALL
+
+ SELECT p.id
+ FROM product p
+ INNER JOIN product_tree pt ON p.parent_id = pt.id
+ )
+ select
batch_no,
MAX(qualifiedId) as qualifiedId,
MAX(unQualifiedId) as unQualifiedId,
@@ -204,84 +336,6 @@
</if>
</where>
group by batch_no, product_model_id, model, unit, product_name, product_id
- </select>
-
- <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
- WITH RECURSIVE product_tree AS (
- SELECT id
- FROM product
- WHERE id = #{ew.topParentProductId}
-
- UNION ALL
-
- SELECT p.id
- FROM product p
- INNER JOIN product_tree pt ON p.parent_id = pt.id
- )
- select
- SUM(qualifiedQuantity) as qualifiedQuantity,
- SUM(unQualifiedQuantity) as unQualifiedQuantity,
- SUM(qualifiedLockedQuantity) as qualifiedLockedQuantity,
- SUM(unQualifiedLockedQuantity) as unQualifiedLockedQuantity,
- model,
- unit,
- product_name,
- MAX(warn_num) as warn_num,
- MAX(remark) as remark,
- MAX(update_time) as update_time
- from (
- select
- si.qualitity as qualifiedQuantity,
- 0 as unQualifiedQuantity,
- COALESCE(si.locked_quantity, 0) as qualifiedLockedQuantity,
- 0 as unQualifiedLockedQuantity,
- si.product_model_id,
- si.create_time,
- si.update_time,
- COALESCE(si.warn_num, 0) as warn_num,
- si.remark,
- pm.model,
- pm.unit,
- p.product_name,
- p.id as product_id
- from stock_inventory si
- left join product_model pm on si.product_model_id = pm.id
- left join product p on pm.product_id = p.id
-
- union all
-
- select
- 0 as qualifiedQuantity,
- su.qualitity as unQualifiedQuantity,
- 0 as qualifiedLockedQuantity,
- COALESCE(su.locked_quantity, 0) as unQualifiedLockedQuantity,
- su.product_model_id,
- su.create_time,
- su.update_time,
- 0 as warn_num,
- su.remark,
- pm.model,
- pm.unit,
- p.product_name,
- p.id as product_id
- from stock_uninventory su
- left join product_model pm on su.product_model_id = pm.id
- left join product p on pm.product_id = p.id
- ) as combined
- <where>
- <if test="ew.productName != null and ew.productName !=''">
- and combined.product_name in (
- select distinct p.product_name
- from product p
- left join product_model pm on p.id = pm.product_id
- where p.product_name like concat('%',#{ew.productName},'%') or pm.model like concat('%',#{ew.productName},'%')
- )
- </if>
- <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
- and combined.product_id in (select id from product_tree)
- </if>
- </where>
- group by product_model_id, model, unit, product_name
</select>
<select id="stockInventoryPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
select sir.*,si.qualitity as current_stock,
@@ -471,6 +525,141 @@
group by spd.stock_inventory_id
) as sd on sd.stock_inventory_id = si.id
where si.product_model_id = #{productModelId}
+ and si.qualitity > IFNULL(sd.qualitity, 0)
+ </select>
+
+ <select id="getBatchNoQty" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+ select
+ batch_no,
+ MAX(qualifiedId) as qualifiedId,
+ MAX(unQualifiedId) as unQualifiedId,
+
+ SUM(qualifiedQuantity) as qualifiedQuantity,
+ SUM(unQualifiedQuantity) as unQualifiedQuantity,
+
+ SUM(qualifiedLockedQuantity) as qualifiedLockedQuantity,
+ SUM(unQualifiedLockedQuantity) as unQualifiedLockedQuantity,
+
+ SUM(IFNULL(qualifiedPendingOut, 0)) as qualifiedPendingOutQuantity,
+ SUM(IFNULL(unQualifiedPendingOut, 0)) as unQualifiedPendingOutQuantity,
+
+ SUM(qualifiedQuantity - qualifiedLockedQuantity - IFNULL(qualifiedPendingOut, 0)) as qualifiedUnLockedQuantity,
+ SUM(unQualifiedQuantity - unQualifiedLockedQuantity - IFNULL(unQualifiedPendingOut, 0)) as unQualifiedUnLockedQuantity,
+
+ product_model_id,
+ model,
+ unit,
+ product_name,
+ product_id,
+
+ MAX(create_time) as create_time,
+ MAX(update_time) as update_time,
+ MAX(warn_num) as warn_num,
+ MAX(version) as version,
+ MAX(remark) as remark,
+
+ 'combined' as stockType
+ from (
+ select
+ si.batch_no,
+ si.id as qualifiedId,
+ null as unQualifiedId,
+
+ si.qualitity as qualifiedQuantity,
+ 0 as unQualifiedQuantity,
+
+ COALESCE(si.locked_quantity, 0) as qualifiedLockedQuantity,
+ 0 as unQualifiedLockedQuantity,
+
+ si.product_model_id,
+ pm.model,
+ pm.unit,
+ p.product_name,
+ p.id as product_id,
+
+ si.create_time,
+ si.update_time,
+ COALESCE(si.warn_num, 0) as warn_num,
+ si.version,
+ si.remark,
+
+ (
+ select IFNULL(SUM(sor.stock_out_num), 0)
+ from stock_out_record sor
+ where sor.product_model_id = si.product_model_id
+ and (
+ (si.batch_no is null and sor.batch_no is null)
+ or si.batch_no = sor.batch_no
+ )
+ and sor.type = '0'
+ and sor.approval_status = 0
+ ) as qualifiedPendingOut,
+
+ 0 as unQualifiedPendingOut
+ from stock_inventory si
+ left join product_model pm on si.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+
+ union all
+
+ select
+ su.batch_no,
+ null as qualifiedId,
+ su.id as unQualifiedId,
+
+ 0 as qualifiedQuantity,
+ su.qualitity as unQualifiedQuantity,
+
+ 0 as qualifiedLockedQuantity,
+ COALESCE(su.locked_quantity, 0) as unQualifiedLockedQuantity,
+
+ su.product_model_id,
+ pm.model,
+ pm.unit,
+ p.product_name,
+ p.id as product_id,
+
+ su.create_time,
+ su.update_time,
+ 0 as warn_num,
+ su.version,
+ su.remark,
+
+ 0 as qualifiedPendingOut,
+
+ (
+ select IFNULL(SUM(sor.stock_out_num), 0)
+ from stock_out_record sor
+ where sor.product_model_id = su.product_model_id
+ and (
+ (su.batch_no is null and sor.batch_no is null)
+ or su.batch_no = sor.batch_no
+ )
+ and sor.type = '1'
+ and sor.approval_status = 0
+ ) as unQualifiedPendingOut
+ from stock_uninventory su
+ left join product_model pm on su.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+ ) as combined
+ <where>
+ <if test="ew.productModelId != null and ew.productModelId > 0">
+ and combined.product_model_id = #{ew.productModelId}
+ </if>
+
+ <if test="ew.productId != null and ew.productId > 0">
+ and combined.product_id = #{ew.productId}
+ </if>
+ </where>
+ group by
+ batch_no,
+ product_model_id,
+ model,
+ unit,
+ product_name,
+ product_id
+ order by
+ batch_no
</select>
</mapper>
diff --git a/src/main/resources/mapper/stock/StockOutRecordMapper.xml b/src/main/resources/mapper/stock/StockOutRecordMapper.xml
index 9e32e21..35421c9 100644
--- a/src/main/resources/mapper/stock/StockOutRecordMapper.xml
+++ b/src/main/resources/mapper/stock/StockOutRecordMapper.xml
@@ -91,7 +91,7 @@
sor.id,
sor.outbound_batches,
sl.customer_name,
- s.shipping_date,
+ sor.create_time as shippingDate,
p.product_name,
pm.model as specification_model,
sor.stock_out_num * slp.tax_inclusive_unit_price as outboundAmount,
@@ -103,7 +103,7 @@
LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id and slp.type = 1
left join product_model pm on slp.product_model_id = pm.id
left join product p on pm.product_id = p.id
- WHERE s.status='宸插彂璐�' and sor.record_type='13'
+ WHERE sor.record_type='13' and sor.approval_status=1
<if test="req.outboundBatches != null and req.outboundBatches != ''">
AND sor.outbound_batches LIKE CONCAT('%',#{req.outboundBatches},'%')
</if>
@@ -111,7 +111,7 @@
AND sl.customer_name LIKE CONCAT('%',#{req.customerName},'%')
</if>
<if test="req.startDate != null and req.endDate != null">
- AND s.shipping_date BETWEEN #{startDate} AND #{endDate}
+ AND s.shipping_date BETWEEN #{req.startDate} AND #{req.endDate}
</if>
order by sor.id DESC
</select>
diff --git a/src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml b/src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml
index 2030715..536cdba 100644
--- a/src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml
+++ b/src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml
@@ -21,7 +21,8 @@
p.product_name as productName,
pm.product_id as productId,
pm.model,
- top1.name as operationName
+ top1.name as operationName,
+ pm.product_code as productCode
from technology_bom_structure tbs
left join product_model pm on tbs.product_model_id = pm.id
left join product p on pm.product_id = p.id
--
Gitblit v1.9.3