From 804b34fb9d6735c434ff0ee69fe3d2c7c4292298 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 14 五月 2026 09:12:34 +0800
Subject: [PATCH] Merge branch 'dev_New_pro' of http://114.132.189.42:9002/r/product-inventory-management-after into dev_西宁_青铝绿行

---
 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/account/bean/dto/financial/FinLedgerQueryDto.java                      |   25 
 src/main/java/com/ruoyi/sales/service/ShippingInfoService.java                                 |    4 
 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               |  206 ++
 src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java                                |   56 
 src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java                            |    2 
 src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java                       |    3 
 src/main/resources/mapper/sales/SalesLedgerProductMapper.xml                                   |    3 
 doc/20260512_AccountSubject树形改造前端修改文档.md                                                       |  185 ++
 src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementPageDtoCopy.java                 |    2 
 src/main/resources/application-dev.yml                                                         |    2 
 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/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/account/bean/vo/financial/FinLedgerRowVo.java                          |   53 
 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/resources/mapper/procurementrecord/ProcurementRecordMapper.xml                        |   24 
 src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java                      |   21 
 src/main/java/com/ruoyi/account/controller/AccountSubjectController.java                       |    8 
 src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java             |   42 
 src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java                   |   38 
 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/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/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 
 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         |  181 ++
 src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java                           |    3 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java                          |   20 
 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/resources/mapper/account/financial/FinVoucherEntryMapper.xml                          |   74 
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java                    |   45 
 src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java                         |   12 
 src/main/java/com/ruoyi/production/pojo/ProductionProductInput.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/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java              |   63 
 src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java                          |   15 
 src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java              |  299 +++
 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                        |    6 
 src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java      |  250 +++
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java                |   75 
 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/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/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                        |   20 
 src/main/resources/mapper/stock/StockInRecordMapper.xml                                        |   10 
 src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java               |   26 
 src/main/java/com/ruoyi/technology/controller/TechnologyBomController.java                     |    2 
 src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java                       |    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/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 
 doc/20260512_财务管理模块前端联调文档.md                                                                   |  288 +++
 src/main/resources/mapper/account/AccountSubjectMapper.xml                                     |   10 
 doc/20260512_add_parent_id_to_account_subject.sql                                              |    5 
 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/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 
 doc/20260512_create_financial_management_tables.sql                                            |  104 +
 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/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/basic/controller/CustomerController.java                               |    4 
 src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java                 |   69 
 src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java                    |   10 
 136 files changed, 4,310 insertions(+), 561 deletions(-)

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/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..c7c6258
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java
@@ -0,0 +1,20 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉淇濆瓨 DTO锛堜富琛� + 鍒嗗綍锛夈��
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDto extends FinVoucher {
+
+    /**
+     * 鍑瘉鏄庣粏鍒嗗綍銆�
+     */
+    private List<FinVoucherEntryDto> entries;
+}
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..d01baf5
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java
@@ -0,0 +1,53 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 绉戠洰璐﹁鏁版嵁杩斿洖瀵硅薄銆�
+ */
+@Data
+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..d1eab48
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java
@@ -0,0 +1,21 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉璇︽儏杩斿洖瀵硅薄銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDetailVo extends FinVoucher {
+
+    /**
+     * 鍑瘉鍒嗗綍鍒楄〃銆�
+     */
+    private List<FinVoucherEntry> entries;
+}
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..a489c67
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
@@ -0,0 +1,206 @@
+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 buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, null, null);
+    }
+
+    @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 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..9e09020
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
@@ -0,0 +1,299 @@
+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.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;
+
+    @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);
+        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);
+        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);
+        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/approve/service/impl/ApproveNodeServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
index 34e1aed..b0046d7 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
@@ -216,6 +216,7 @@
             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)) {
diff --git a/src/main/java/com/ruoyi/basic/controller/CustomerController.java b/src/main/java/com/ruoyi/basic/controller/CustomerController.java
index 9090d34..2130007 100644
--- a/src/main/java/com/ruoyi/basic/controller/CustomerController.java
+++ b/src/main/java/com/ruoyi/basic/controller/CustomerController.java
@@ -148,8 +148,8 @@
      * 绉佹捣瀹㈡埛娴佸洖鍏捣
      */
     @Log(title = "瀹㈡埛妗f", businessType = BusinessType.OTHER)
-    @PostMapping("/back")
-    public R back(Long id) {
+    @PostMapping("/back/{id}")
+    public R back(@PathVariable("id") Long id) {
         return R.ok(customerService.back(id));
     }
 }
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/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/CustomerServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
index 52d01ec..09c2a46 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -375,7 +375,7 @@
         //灏嗗鎴风殑type鏀逛负1 涓旂洿鎺ュ垎閰嶇粰褰撳墠鐢ㄦ埛
         Customer customer = customerMapper.selectById(id);
         customer.setType(1);
-        customer.setIsAssigned(1);
+        customer.setIsAssigned(0);
         return this.updateById(customer);
     }
 
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..51e5441 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
@@ -47,6 +47,11 @@
 
     @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();
@@ -57,6 +62,21 @@
         }
     }
 
+    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 + "宸茬粡瀛樺湪");
+        }
+    }
+
 
     @Override
     public int delProductModel(Long[] 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/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..8e004e4 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,24 +139,23 @@
     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());
+            }
         }
 
     }
@@ -147,15 +163,13 @@
     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..d0dab50 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,29 @@
 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.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.ProductionOrderRoutingOperationMapper;
 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.ProductionOrderRoutingOperation;
 import com.ruoyi.production.service.ProductionBomStructureService;
 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.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -26,7 +37,11 @@
 @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 ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+    private final ProductionOperationTaskMapper productionOperationTaskMapper;
 
     /**
      * 鏍规嵁BOM鏌ヨ骞剁粍瑁呯粨鏋勬爲銆�
@@ -136,9 +151,173 @@
         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);
+        syncTaskPlanQuantity(
+                currentProductionOrderId,
+                structureList,
+                orderQuantity,
+                orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId());
+    }
+
+    private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) {
+        if (structureList == null || structureList.isEmpty()) {
+            return;
+        }
+        List<ProductionBomStructure> updateList = new ArrayList<>();
+        for (ProductionBomStructure structure : structureList) {
+            if (structure == null || structure.getId() == null) {
+                continue;
+            }
+            BigDecimal demandedQuantity = defaultDecimal(structure.getUnitQuantity()).multiply(orderQuantity);
+            if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
+                continue;
+            }
+            ProductionBomStructure update = new ProductionBomStructure();
+            update.setId(structure.getId());
+            update.setDemandedQuantity(demandedQuantity);
+            updateList.add(update);
+            structure.setDemandedQuantity(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));
+        Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId, orderQuantity);
+
+        for (ProductionOperationTask task : taskList) {
+            if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
+                continue;
+            }
+            ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId());
+            if (routingOperation == null || routingOperation.getTechnologyRoutingOperationId() == null) {
+                continue;
+            }
+            BigDecimal planQuantity = resolveTaskPlanQuantity(routingOperation, demandedQuantityMap, orderQuantity);
+            if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
+                continue;
+            }
+            ProductionOperationTask update = new ProductionOperationTask();
+            update.setId(task.getId());
+            update.setPlanQuantity(planQuantity);
+            productionOperationTaskMapper.updateById(update);
+        }
+    }
+
+    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList,
+                                                                      Long rootProductModelId,
+                                                                      BigDecimal orderQuantity) {
+        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<>();
+        for (ProductionBomStructure bomStructure : structureList) {
+            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null || bomStructure.getUnitQuantity() == null) {
+                continue;
+            }
+            Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, rootProductModelId);
+            String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+            demandedQuantityMap.merge(key, bomStructure.getUnitQuantity().multiply(orderQuantity), BigDecimal::add);
+        }
+        return demandedQuantityMap;
+    }
+
+    private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
+                                               Map<String, BigDecimal> demandedQuantityMap,
+                                               BigDecimal orderQuantity) {
+        if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
+            return orderQuantity;
+        }
+        String key = buildOperationDemandedQuantityKey(
+                routingOperation.getTechnologyOperationId(),
+                routingOperation.getProductModelId());
+        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 Long resolveOutputProductModelId(ProductionBomStructure bomStructure,
+                                             Map<Long, ProductionBomStructure> structureById,
+                                             Long rootProductModelId) {
+        if (bomStructure == null) {
+            return rootProductModelId;
+        }
+        Long parentId = bomStructure.getParentId();
+        if (parentId == null) {
+            return rootProductModelId != null ? rootProductModelId : bomStructure.getProductModelId();
+        }
+        ProductionBomStructure parent = structureById.get(parentId);
+        if (parent != null && parent.getProductModelId() != null) {
+            return parent.getProductModelId();
+        }
+        return rootProductModelId != null ? rootProductModelId : bomStructure.getProductModelId();
+    }
+
+    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/ProductionOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
index 344800c..5a8ea9c 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,7 @@
                         .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())
                         .orderByDesc(TechnologyRoutingOperation::getDragSort)
                         .orderByDesc(TechnologyRoutingOperation::getId));
+        Map<String, BigDecimal> operationDemandedQuantityMap = buildOperationDemandedQuantityMap(technologyRouting, productionOrder);
         Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
         // 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
                         routingOperations.stream()
@@ -278,7 +279,7 @@
                 ProductionOperationTask task = new ProductionOperationTask();
                 task.setProductionOrderRoutingOperationId(targetOperation.getId());
                 task.setProductionOrderId(productionOrder.getId());
-                task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity()));
+                task.setPlanQuantity(resolveTaskPlanQuantity(sourceOperation, operationDemandedQuantityMap, productionOrder));
                 task.setCompleteQuantity(BigDecimal.ZERO);
                 task.setWorkOrderNo(generateNextTaskNo());
                 task.setStatus(2);
@@ -311,6 +312,72 @@
             }
         }
         return syncedParamCount;
+    }
+
+    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(TechnologyRouting technologyRouting,
+                                                                      ProductionOrder productionOrder) {
+        if (technologyRouting == null || technologyRouting.getBomId() == null) {
+            return Collections.emptyMap();
+        }
+        BigDecimal orderQuantity = defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+        List<TechnologyBomStructure> bomStructures = technologyBomStructureMapper.selectList(
+                Wrappers.<TechnologyBomStructure>lambdaQuery()
+                        .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId())
+                        .isNotNull(TechnologyBomStructure::getOperationId)
+                        .orderByAsc(TechnologyBomStructure::getId));
+        if (bomStructures.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<Long, TechnologyBomStructure> structureById = bomStructures.stream()
+                .filter(item -> item != null && item.getId() != null)
+                .collect(Collectors.toMap(TechnologyBomStructure::getId, item -> item, (left, right) -> left));
+        Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
+        for (TechnologyBomStructure bomStructure : bomStructures) {
+            if (bomStructure == null || bomStructure.getOperationId() == null) {
+                continue;
+            }
+            BigDecimal unitQuantity = bomStructure.getUnitQuantity();
+            if (unitQuantity == null) {
+                continue;
+            }
+            Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId());
+            String key = buildOperationDemandedQuantityKey(bomStructure.getOperationId(), outputProductModelId);
+            demandedQuantityMap.merge(key, unitQuantity.multiply(orderQuantity), BigDecimal::add);
+        }
+        return demandedQuantityMap;
+    }
+
+    private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation,
+                                               Map<String, BigDecimal> operationDemandedQuantityMap,
+                                               ProductionOrder productionOrder) {
+        if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
+            return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+        }
+        String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), sourceOperation.getProductModelId());
+        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 Long resolveOutputProductModelId(TechnologyBomStructure bomStructure,
+                                             Map<Long, TechnologyBomStructure> structureById,
+                                             Long routingProductModelId) {
+        if (bomStructure == null) {
+            return routingProductModelId;
+        }
+        Long parentId = bomStructure.getParentId();
+        if (parentId == null) {
+            return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
+        }
+        TechnologyBomStructure parent = structureById.get(parentId);
+        if (parent != null && parent.getProductModelId() != null) {
+            return parent.getProductModelId();
+        }
+        return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
     }
 
     private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
@@ -788,12 +855,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/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/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..24e3405 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,24 +8,24 @@
 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;
@@ -36,8 +35,6 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
-import java.util.Map;
-import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -76,13 +73,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 +104,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 +117,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));
+                .in(StockOutRecord::getRecordId, purchaseReturnOrderProducts.stream().map(PurchaseReturnOrderProducts::getId).collect(Collectors.toList())));
         // 璐㈠姟
         LambdaUpdateWrapper<AccountIncome> updateWrapperAccountIncome = new LambdaUpdateWrapper<>();
         updateWrapperAccountIncome.eq(AccountIncome::getBusinessId, id);
@@ -147,4 +132,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/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 86eb0ab..b23bb67 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -20,6 +20,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;
@@ -34,6 +36,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -45,6 +48,7 @@
 
     private final StockUtils stockUtils;
     private final StockInventoryService stockInventoryService;
+    private final StockInRecordService stockInRecordService;
     private QualityInspectMapper qualityInspectMapper;
 
     private IQualityInspectParamService qualityInspectParamService;
@@ -111,12 +115,53 @@
             stockInventoryDto.setRecordId(qualityInspect.getId());
             stockInventoryDto.setProductModelId(qualityInspect.getProductModelId());
             stockInventoryDto.setQualitity(qualityInspect.getQuantity());
+            stockInventoryDto.setBatchNo(resolveProductionBatchNo(
+                    qualityInspect.getProductMainId(),
+                    qualityInspect.getId(),
+                    qualityInspect.getProductModelId()));
             stockInventoryService.addStockInRecordOnly(stockInventoryDto);
         }
         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/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/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index 5f8788d..f8bd358 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;
@@ -97,7 +97,7 @@
         commonFileService.deleteByBusinessIds(ids, FileNameType.SHIP.getValue());
         // 鎵e凡鍙戣揣搴撳瓨
         for (ShippingInfo shippingInfo : shippingInfos) {
-            if ("宸插彂璐�".equals(shippingInfo.getStatus())) {
+            if ("瀹℃牳閫氳繃".equals(shippingInfo.getStatus())) {
                 stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
             }
         }
@@ -119,7 +119,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/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..bda6bcf 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: /
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/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/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..ec1124a 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
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/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