From 5f73eb1fb9d04040fe6ba377d5ae582024429f2f Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期日, 20 九月 2026 14:15:02 +0800
Subject: [PATCH] feat(sales): 添加客户交易详情中的付款金额字段
---
src/main/resources/mapper/basic/CustomerMapper.xml | 9
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java | 6
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java | 58 +
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java | 12
src/main/java/com/ruoyi/account/service/impl/PaymentRecordServiceImpl.java | 36
src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java | 61
src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java | 10
src/main/java/com/ruoyi/account/service/PaymentRecordService.java | 14
src/main/java/com/ruoyi/account/mapper/PaymentRecordMapper.java | 10
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java | 4
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java | 3
src/main/resources/mapper/measuringinstrumentledger/SparePartsRecordMapper.xml | 32
src/main/java/com/ruoyi/measuringinstrumentledger/mapper/SparePartsRecordMapper.java | 15
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java | 29
src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java | 12
src/main/java/com/ruoyi/purchase/service/PurchaseApplicationService.java | 43
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java | 4
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java | 12
doc/20260920_rename_purchase_review_menu.sql | 7
src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRecordService.java | 14
src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java | 5
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java | 9
src/main/java/com/ruoyi/purchase/pojo/PurchaseApplication.java | 107 ++
src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRecordServiceImpl.java | 27
src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRecordController.java | 49 +
src/main/java/com/ruoyi/account/pojo/PaymentRecord.java | 74 +
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java | 5
src/main/java/com/ruoyi/purchase/pojo/PurchaseApplicationProduct.java | 89 +
src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java | 7
doc/20260920_采购申请模块前端联调文档.md | 322 ++++++
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java | 73 +
src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml | 17
doc/20260920_create_purchase_application.sql | 57 +
doc/20260920_出库即发货与往来备件前端联调文档.md | 235 +++++
src/main/java/com/ruoyi/purchase/controller/PurchaseApplicationController.java | 90 +
src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SparePartsRecord.java | 80 +
src/main/resources/mapper/basic/SupplierManageMapper.xml | 9
src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SpareParts.java | 8
src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationProductMapper.java | 12
doc/20260920_stock_out_shipping_link.sql | 8
src/main/java/com/ruoyi/account/controller/PaymentRecordController.java | 82 +
src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml | 15
doc/20260920_spare_parts_product_and_record.sql | 29
src/main/java/com/ruoyi/basic/pojo/OilDepot.java | 3
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java | 5
doc/20260920_储罐号带入库存前端联调文档.md | 202 ++++
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java | 7
sql/DbTool.java | 193 ++++
sql/db.sh | 23
src/main/java/com/ruoyi/purchase/dto/PurchaseApplicationDto.java | 79 +
src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationMapper.java | 12
src/main/resources/mapper/stock/StockInventoryMapper.xml | 19
src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java | 11
src/main/java/com/ruoyi/purchase/service/impl/PurchaseApplicationServiceImpl.java | 346 +++++++
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java | 5
doc/20260920_create_payment_record.sql | 23
src/main/java/com/ruoyi/common/enums/PurchaseApplicationStatusEnum.java | 40
src/main/java/com/ruoyi/procurementrecord/bean/vo/BatchNoOptionVo.java | 25
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java | 3
src/main/java/com/ruoyi/basic/pojo/TankInfo.java | 3
60 files changed, 2,761 insertions(+), 38 deletions(-)
diff --git a/doc/20260920_create_payment_record.sql b/doc/20260920_create_payment_record.sql
new file mode 100644
index 0000000..12056c2
--- /dev/null
+++ b/doc/20260920_create_payment_record.sql
@@ -0,0 +1,23 @@
+# 寰�鏉ヤ粯娆惧洖娆炬祦姘磋〃
+# 瀹㈡埛寰�鏉ュ彇 party_type=1 and direction=1 浣滀负銆屼粯娆俱�嶅垪
+# 渚涘簲鍟嗗線鏉ュ彇 party_type=2 and direction=2 浣滀负銆屽洖娆俱�嶅垪
+drop table if exists payment_record;
+create table payment_record
+(
+ id bigint auto_increment primary key,
+ party_type tinyint not null comment '寰�鏉ユ柟绫诲瀷 1瀹㈡埛 2渚涘簲鍟�',
+ party_id bigint not null comment '瀹㈡埛id鎴栦緵搴斿晢id',
+ direction tinyint not null comment '鏂瑰悜 1浠樻 2鍥炴',
+ amount decimal(18, 2) not null default 0 comment '閲戦',
+ record_date date null comment '鍙戠敓鏃ユ湡',
+ payment_method varchar(64) null comment '浠樻/鍥炴鏂瑰紡',
+ remark varchar(500) null comment '澶囨敞',
+ tenant_id bigint null comment '绉熸埛ID',
+ dept_id bigint null comment '閮ㄩ棬ID',
+ create_user int null comment '鍒涘缓浜篒D',
+ create_time datetime null comment '鍒涘缓鏃堕棿',
+ update_user int null comment '鏇存柊浜篒D',
+ update_time datetime null comment '鏇存柊鏃堕棿',
+ index idx_party (party_type, party_id),
+ index idx_direction (direction)
+);
diff --git a/doc/20260920_create_purchase_application.sql b/doc/20260920_create_purchase_application.sql
new file mode 100644
index 0000000..1fce1c6
--- /dev/null
+++ b/doc/20260920_create_purchase_application.sql
@@ -0,0 +1,57 @@
+#閲囪喘鐢宠鍗曡〃
+drop table if exists purchase_application;
+create table purchase_application
+(
+ id bigint auto_increment primary key,
+ application_no varchar(50) not null comment '鐢宠鍗曞彿',
+ supplier_id bigint null comment '渚涘簲鍟唅d',
+ supplier_name varchar(100) null comment '渚涘簲鍟嗗悕绉�',
+ project_name varchar(100) null comment '椤圭洰鍚嶇О',
+ sales_contract_no varchar(50) null comment '閿�鍞悎鍚屽彿',
+ sales_ledger_id int null comment '鍏宠仈閿�鍞彴璐︿富琛ㄤ富閿�',
+ applicant_id bigint null comment '鐢宠浜篿d',
+ applicant_name varchar(50) null comment '鐢宠浜哄鍚�',
+ apply_date date null comment '鐢宠鏃ユ湡',
+ expected_arrival_date date null comment '鏈熸湜鍒拌揣鏃ユ湡',
+ contract_amount decimal(10, 2) null comment '鐢宠閲戦锛堝惈绋庢�讳环锛�',
+ payment_method varchar(255) null comment '浠樻鏂瑰紡',
+ remarks text null comment '澶囨敞',
+ status tinyint not null default 0 comment '鐘舵�� 0鑽夌 1宸茬敓鎴愯鍗�',
+ purchase_ledger_id bigint null comment '杞鍗曞悗鐢熸垚鐨勯噰璐彴璐d',
+ convert_time datetime null comment '杞鍗曟椂闂�',
+ tenant_id bigint null comment '绉熸埛ID',
+ dept_id bigint null comment '閮ㄩ棬ID',
+ create_user bigint null comment '鍒涘缓浜篒D',
+ create_time datetime null comment '鍒涘缓鏃堕棿',
+ update_time datetime null comment '鏇存柊鏃堕棿',
+ index idx_application_no (application_no),
+ index idx_supplier_id (supplier_id),
+ index idx_purchase_ledger_id (purchase_ledger_id)
+);
+
+#閲囪喘鐢宠浜у搧鏄庣粏琛�
+drop table if exists purchase_application_product;
+create table purchase_application_product
+(
+ id bigint auto_increment primary key,
+ purchase_application_id bigint not null comment '閲囪喘鐢宠鍗昳d',
+ product_id bigint null comment '浜у搧id',
+ product_model_id bigint null comment '浜у搧瑙勬牸id',
+ product_category varchar(100) null comment '浜у搧澶х被',
+ specification_model varchar(100) null comment '瑙勬牸鍨嬪彿',
+ unit varchar(20) null comment '鍗曚綅',
+ quantity decimal(18, 2) not null default 0 comment '鏁伴噺',
+ tax_rate decimal(5, 0) null comment '绋庣巼',
+ tax_inclusive_unit_price decimal(18, 2) null comment '鍚◣鍗曚环',
+ tax_inclusive_total_price decimal(18, 2) null comment '鍚◣鎬讳环',
+ tax_exclusive_total_price decimal(18, 2) null comment '涓嶅惈绋庢�讳环',
+ invoice_type varchar(50) null comment '鍙戠エ绫诲瀷',
+ is_checked tinyint(1) not null default 1 comment '鏄惁鎺ㄩ�佽川妫�',
+ remark varchar(255) null comment '澶囨敞',
+ tenant_id bigint null comment '绉熸埛ID',
+ dept_id bigint null comment '閮ㄩ棬ID',
+ create_user bigint null comment '鍒涘缓浜篒D',
+ create_time datetime null comment '鍒涘缓鏃堕棿',
+ update_time datetime null comment '鏇存柊鏃堕棿',
+ index idx_purchase_application_id (purchase_application_id)
+);
diff --git a/doc/20260920_rename_purchase_review_menu.sql b/doc/20260920_rename_purchase_review_menu.sql
new file mode 100644
index 0000000..297c46a
--- /dev/null
+++ b/doc/20260920_rename_purchase_review_menu.sql
@@ -0,0 +1,7 @@
+-- 閲囪喘瀹℃牳 鑿滃崟鏀瑰悕涓� 閲囪喘鐢宠锛堝幓鎺夊鏍歌涔夛級
+-- 鍓嶇闇�瑕佸悓姝ユ柊澧為〉闈細procurementManagement/purchaseApplication/index
+UPDATE sys_menu
+SET menu_name = '閲囪喘鐢宠',
+ path = 'purchaseApplication',
+ component = 'procurementManagement/purchaseApplication/index'
+WHERE menu_id = 2355;
diff --git a/doc/20260920_spare_parts_product_and_record.sql b/doc/20260920_spare_parts_product_and_record.sql
new file mode 100644
index 0000000..8cacfbe
--- /dev/null
+++ b/doc/20260920_spare_parts_product_and_record.sql
@@ -0,0 +1,29 @@
+# 澶囦欢鍏宠仈浜у搧鏁版嵁锛堜骇鍝佸ぇ绫� + 瑙勬牸鍨嬪彿锛夛紝浣垮浠跺彲璧伴噰璐彴璐︽槑缁�
+alter table spare_parts
+ add column product_id bigint null comment '鍏宠仈浜у搧澶х被id',
+ add column product_model_id bigint null comment '鍏宠仈浜у搧瑙勬牸id';
+
+alter table spare_parts
+ add index idx_product_model_id (product_model_id);
+
+# 澶囦欢鍑哄叆搴撴祦姘�
+drop table if exists spare_parts_record;
+create table spare_parts_record
+(
+ id bigint auto_increment primary key,
+ spare_parts_id bigint not null comment '澶囦欢id',
+ direction tinyint not null comment '鏂瑰悜 1鍏ュ簱 2鍑哄簱',
+ quantity decimal(18, 2) not null default 0 comment '鏁伴噺',
+ source_type tinyint null comment '鏉ユ簮 1閲囪喘鍏ュ簱 2缁翠慨棰嗙敤 3淇濆吇棰嗙敤',
+ source_id bigint null comment '鏉ユ簮鍗曟嵁id',
+ source_no varchar(64) null comment '鏉ユ簮鍗曞彿',
+ remark varchar(500) null comment '澶囨敞',
+ tenant_id bigint null comment '绉熸埛ID',
+ dept_id bigint null comment '閮ㄩ棬ID',
+ create_user int null comment '鍒涘缓浜篒D',
+ create_time datetime null comment '鍒涘缓鏃堕棿',
+ update_user int null comment '鏇存柊浜篒D',
+ update_time datetime null comment '鏇存柊鏃堕棿',
+ index idx_spare_parts_id (spare_parts_id),
+ index idx_source (source_type, source_id)
+);
diff --git a/doc/20260920_stock_out_shipping_link.sql b/doc/20260920_stock_out_shipping_link.sql
new file mode 100644
index 0000000..92c6dec
--- /dev/null
+++ b/doc/20260920_stock_out_shipping_link.sql
@@ -0,0 +1,8 @@
+# 鍑哄簱鍙拌处涓庡彂璐у彴璐﹀叧鑱旓細娌瑰搧鍑哄簱鍗冲彂璐�
+# procurement_record_out 澧炲姞鍙戣揣鍗曞叧鑱斾笌鍑哄簱鎵规锛堟墸搴撳瓨蹇呴渶锛�
+alter table procurement_record_out
+ add column shipping_info_id bigint null comment '鑷姩鐢熸垚鐨勫彂璐у彴璐d',
+ add column batch_no varchar(64) null comment '鍑哄簱鎵规锛堟墸搴撳瓨鐢級';
+
+alter table procurement_record_out
+ add index idx_shipping_info_id (shipping_info_id);
diff --git "a/doc/20260920_\345\202\250\347\275\220\345\217\267\345\270\246\345\205\245\345\272\223\345\255\230\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260920_\345\202\250\347\275\220\345\217\267\345\270\246\345\205\245\345\272\223\345\255\230\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..9bb95b3
--- /dev/null
+++ "b/doc/20260920_\345\202\250\347\275\220\345\217\267\345\270\246\345\205\245\345\272\223\345\255\230\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,202 @@
+# 鍌ㄧ綈鍙凤紙鍌ㄧ綈鍒嗙被锛夊甫鍏ュ簱瀛� 鍓嶇鑱旇皟鏂囨。
+
+鏇存柊鏃堕棿锛�2026-09-20
+閫傜敤鐗堟湰锛氭湰娆″悗绔彉鏇村悗
+瀵瑰簲闇�姹傦細闇�姹傛竻鍗曠 12 鏉�
+
+瀹炵幇鏂瑰紡璇存槑锛�**涓嶆槸鏂板啓浠g爜锛岃�屾槸鎶婁笂娓� `dev_New_pro` 涓婂凡鏈夌殑瀹炵幇鎷胯繃鏉�**鈥斺�攃herry-pick 浜� `d8c86797a`锛堟补搴�/鍌ㄧ綈妯″潡 + 搴撳瓨鍑哄叆搴撳瓧娈靛叏閾捐矾锛夊拰 `50921bdc2` 鐨勫簱瀛樻帓搴忎慨澶嶏紝鍐嶈ˉ浜� 2 澶勮鎻愪氦婕忔帀鐨勫睍绀洪潰锛堝簱瀛樼鐞嗗垪琛ㄣ�佸鍑� Excel锛夈�傚洜姝�**搴撹〃鍒楀拰鑿滃崟鍚庣姝ゅ墠鍦ㄦ湰鍒嗘敮鏄己澶辩殑**锛氳彍鍗� 2353/2354 鐐瑰紑涓�鐩存姤閿欙紝鏈涓�骞朵慨濂姐��
+
+---
+
+## 1. 鍙樻洿鑼冨洿
+
+| 鍐呭 | 褰卞搷闈� |
+| --- | --- |
+| 娌瑰簱淇℃伅銆佸偍缃愪俊鎭袱涓富鏁版嵁妯″潡鍚庣琛ラ綈 | 鑿滃崟 2353 娌瑰簱淇℃伅銆�2354 鍌ㄧ綈淇℃伅 |
+| 鍌ㄧ綈鍙� `tankNo` 甯﹀叆搴撳瓨 | 搴撳瓨绠$悊鍒楄〃銆佸簱瀛樿鎯�/鎵瑰彿鏄庣粏銆佸鍑� Excel銆佸叆搴撶鐞嗗垪琛� |
+| 鍏ュ簱璁板綍鏂板娌瑰搧/鍌ㄧ綈/鐗╂祦/杩囩绛� 28 涓瓧娈� | 鏂板鍏ュ簱琛ㄥ崟銆佸叆搴撶鐞嗗垪琛� |
+| 鍑哄簱璁板綍鏂板娌瑰搧/鍌ㄧ綈/鐗╂祦/杩囩绛夊瓧娈� | 鏂板鍑哄簱琛ㄥ崟銆佸嚭搴撹褰曞垪琛� |
+| 鏂板銆屼粎鍒涘缓鍑哄簱璁板綍銆嶆帴鍙� | 娌瑰搧鍑哄簱琛ㄥ崟 |
+
+> **鍙e緞**锛氶渶姹傛柟纭銆屽偍缃愬彿灏辨槸鍌ㄧ綈鍒嗙被銆嶏紝涓嶅彟寤哄垎绫诲瓧娈点�傚偍缃愮殑銆屽ぇ绫汇�嶅湪 `tank_info.category`锛屽偍缃愬彿鏄� `tank_info.tank_no`锛屽簱瀛樺悇椤靛睍绀虹殑鏄悗鑰呫��
+
+---
+
+## 2. 鍓嶆彁锛氬厛寤轰富鏁版嵁锛屽惁鍒欏偍缃愬彿姘歌繙鏄┖
+
+鍌ㄧ綈鍙锋槸浠�**鍏ュ簱璁板綍**甯﹀嚭鏉ョ殑锛歚stock_inventory(product_model_id + batch_no)` 鈫� `stock_in_record(product_model_id + batch_no)` 鈫� `tank_no`銆傛墍浠�**蹇呴』鍏堟湁鍌ㄧ綈涓绘暟鎹�佷笖鍏ュ簱鏃堕�変簡鍌ㄧ綈**锛屽簱瀛橀〉鎵嶆湁鍊笺��
+
+### 2.1 娌瑰簱淇℃伅
+
+鍓嶇紑 `/basic/oilDepot`銆�
+
+| 鎺ュ彛 | 鏂规硶 | 璇存槑 |
+| --- | --- | --- |
+| `/basic/oilDepot/list` | GET | 鍒嗛〉鏌ヨ锛岃繑鍥� `R.data.records` |
+| `/basic/oilDepot/all` | GET | 鍏ㄩ噺锛屼緵涓嬫媺閫夋嫨 |
+| `/basic/oilDepot/{id}` | GET | 璇︽儏 |
+| `/basic/oilDepot/add` | POST | 鏂板 |
+| `/basic/oilDepot/update` | POST | 淇敼锛屽繀椤诲甫 `id` |
+| `/basic/oilDepot/del` | DELETE | 鎵归噺鍒犻櫎锛宐ody 涓� id 鏁扮粍 |
+
+瀛楁锛歚id`銆乣depotName`锛堟补搴撳悕绉帮級銆乣chargePerson`锛堣礋璐d汉锛夈�乣phone`銆乣enableDate`锛堝惎鐢ㄦ棩鏈� `yyyy-MM-dd`锛夈�乣company`锛堟墍灞炲叕鍙革級銆�
+
+### 2.2 鍌ㄧ綈淇℃伅
+
+鍓嶇紑 `/basic/tankInfo`锛屾帴鍙e舰鎬佷笌娌瑰簱瀹屽叏涓�鑷达紙`list` / `all` / `{id}` / `add` / `update` / `del`锛夈��
+
+瀛楁锛歚id`銆乣tankNo`锛�**鍌ㄧ綈鍙�**锛屽 `01#`锛夈�乣category`锛堝ぇ绫伙紝濡� `鎴愬搧娌筦锛夈�乣oilProduct`锛堟补鍝侊紝濡� `鏌�#0`锛夈�乣capacity`锛堢綈瀹癸級銆乣remaining`锛堝簱瀛樹綑閲� m鲁锛夈�乣tenantInfo`锛堢瀹俊鎭級銆�
+
+**鍓嶇**锛氬偍缃愪俊鎭〃鍗曠殑銆屽ぇ绫汇�嶃�屾补鍝併�嶅缓璁仛鎴愪笅鎷夛紙褰撳墠搴撳唴鍊间负 澶х被=`鎴愬搧娌筦銆佹补鍝�=`鏌�#0`/`鏌�#-35`/`姹�#92`/`姹�#95`锛屽彲涓庝骇鍝佽鏍煎彛寰勫榻愬悗鍐嶅畾锛夈��
+
+---
+
+## 3. 鏂板鍏ュ簱锛氬瓧娈典笌鎺ュ彛锛�**鏈変釜鍧戯紝蹇呯湅**锛�
+
+### 3.1 鍙湁 `addStockInRecordOnly` 浼氶�忎紶娌瑰搧/鍌ㄧ綈瀛楁
+
+| 鎺ュ彛 | 鏄惁閫忎紶娌瑰搧/鍌ㄧ綈/鐗╂祦/杩囩瀛楁 |
+| --- | --- |
+| `POST /stockInventory/addStockInRecordOnly` | **鏄�**锛�28 涓瓧娈靛叏閫忎紶锛屽苟鎸� id 鍙嶆煡鍥炲~娌瑰簱鍚嶇О銆佸偍缃愬彿锛� |
+| `POST /stockInventory/addstockInventory` | **鍚�**锛堝彧鍐� recordType/batchNo/productModelId/鏁伴噺/澶囨敞/棰勮鏁伴噺锛屼紶浜嗚繖浜涘瓧娈典細琚潤榛樹涪寮冿級 |
+
+涓よ�呴兘浼氬啓涓�鏉� `stock_in_record`锛涘尯鍒槸 `addStockInRecordOnly` **涓嶅姩** `stock_inventory`锛堜粎鍒涘缓璁板綍锛夛紝`addstockInventory` 浼氬悓鏃跺鍑忓簱瀛樸��**鏂板鍏ュ簱琛ㄥ崟鑻ヨ瀛樹笅鍌ㄧ綈鍙凤紝蹇呴』璧� `addStockInRecordOnly`銆�**
+
+> 杩欎竴鐐规槸涓婃父瀹炵幇鐨勫師鏍疯涓猴紝鏈鏈敼鍔ㄣ�傝嫢涓氬姟涓婃柊澧炲叆搴撻渶瑕併�屾棦瀛樻补鍝�/鍌ㄧ綈瀛楁銆佸張鍔犲簱瀛樸�嶏紝闇�鍙﹁纭璋冪敤椤哄簭锛堝厛寤鸿褰曞啀鍔犲簱瀛樹細鍐欎袱鏉¤褰曪級锛�**灞為渶涓庨渶姹傛柟纭鐨勮仈璋冪偣**銆�
+
+### 3.2 璇锋眰浣撳瓧娈碉紙`StockInventoryDto`锛�
+
+蹇呭~娌跨敤鍘熸湁锛歚productModelId`銆乣qualitity`锛沗batchNo` 涓虹┖鏃跺悗绔寜 `yyyyMMdd-浜у搧缂栫爜-搴忓彿` 鑷姩鐢熸垚銆�
+
+鏂板瀛楁锛�
+
+| 鍒嗙粍 | 瀛楁 | 绫诲瀷 | 璇存槑 |
+| --- | --- | --- | --- |
+| 娌瑰簱鍌ㄧ綈 | `oilDepotId` | Long | 娌瑰簱 id锛涗紶浜� id 浣嗘病浼犲悕绉版椂鍚庣鍙嶆煡鍥炲~ |
+| | `oilDepotName` | String | 娌瑰簱鍚嶇О锛堝彲鍙紶 id锛� |
+| | `inboundCategory` | String | 鍏ュ簱绫诲埆锛氳嚜璐叆搴� / 浠e偍鍏ュ簱 |
+| | `tankId` | Long | 鍌ㄧ綈 id锛涗紶浜� id 浣嗘病浼犵綈鍙锋椂鍚庣鍙嶆煡鍥炲~ |
+| | `tankNo` | String | **鍌ㄧ綈鍙�**锛堝彲鍙紶 `tankId`锛� |
+| | `inboundTime` | String | 鍏ュ簱鏃堕棿 `yyyy-MM-dd HH:mm:ss` |
+| | `oilProduct` | String | 娌瑰搧锛氭苯#92 / 姹�#95 / 鏌�#0 / 鏌�#-35 |
+| | `originalQuantity` | BigDecimal | 鍘熷巶鏁伴噺(鍚�) |
+| 瀹㈡埛 | `customerId` | Long | 浼犱簡 id 浣嗘病浼犲悕绉版椂鍚庣鍙嶆煡鍥炲~ |
+| | `customerName` / `contactPerson` / `contactPhone` | String | 瀹㈡埛鍚嶇О / 瀵规帴浜� / 鐢佃瘽 |
+| 鐗╂祦 | `driverName` / `driverPhone` | String | 鍙告満濮撳悕 / 鐢佃瘽 |
+| | `escortName` / `escortPhone` | String | 鎶艰繍鍛樺鍚� / 鐢佃瘽 |
+| | `truckPlateNo` / `trailerPlateNo` | String | 杞︾墝鍙� / 杞︽寕鐗屽彿 |
+| 鍗告补鎿嶄綔 | `tankInspectionStatus` | String | 杞︿綋/閾呭皝妫�楠岋細姝e父 / 寮傚父 |
+| | `unloadingCranePosition` | String | 鍗歌溅楣や綅锛�1#鎾 / 2#鎾 / 涓存椂娉� |
+| | `density` | String | 瀵嗗害 kg/L |
+| | `unloadPhotos` | **鏁扮粍** | 鍗告补鍏抽敭鐜妭涓庤溅杈嗗叧閿綅缃媿鐓� |
+| 杩囩 | `grossWeight` / `tareWeight` / `netWeight` | BigDecimal | 姣涢噸 / 鐨噸 / 鍑�閲�(鍚�) |
+| | `lossQuantity` | BigDecimal | 浜忛噺(鍚�) |
+| | `actualVolume` | BigDecimal | 瀹為檯鍏ュ簱浣撶Н(鍗�) |
+| | `poundPhotos` | **鏁扮粍** | 纾呭崟涓婁紶 |
+
+**鐓х墖瀛楁鐨勫叆鍙�/鍑哄弬褰㈡�佷笉涓�鑷达紝鍔″繀娉ㄦ剰**锛�
+
+- **鍏ュ弬**浼犳暟缁勶紙鍚庣鐢� `JSON.toJSONString` 瀛樺簱锛�
+- **鍑哄弬**鏄� JSON **瀛楃涓�**锛坄"[{\"uidFilename\":...}]"`锛夛紝鍓嶇闇� `JSON.parse` 鍚庡啀娓叉煋
+- 鍑哄弬閲岀殑 URL 浼氳繃鏈燂紙绛惧悕 2 灏忔椂锛夛紝鎵�浠�**鎵瑰彿鏄庣粏鎺ュ彛杩斿洖鍓嶄細閲嶆柊绛惧彂**锛堣 4.2锛夛紱鍏朵粬鎺ュ彛鑻ョ洿鎺ヨ繑鍥炲簱閲岀殑鏃т覆锛屽浘鐗囧彲鑳藉姞杞藉け璐�
+
+---
+
+## 4. 鍥涘灞曠ず钀界偣
+
+### 4.1 搴撳瓨绠$悊鍒楄〃 鈥斺�� `GET /stockInventory/pageListCombinedStockInventory`
+
+杩斿洖 `R.data.records`銆傛湰娆℃柊澧炲垪锛�**鍙涓嶅皯锛屽師鏈夊瓧娈典笉鍙�**锛夛細
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| `tankNo` | **鍌ㄧ綈鍙�**锛堟湰娆¢渶姹備富瀛楁锛� |
+| `oilProduct` | 娌瑰搧 |
+| `tankInspectionStatus` / `unloadingCranePosition` | 杞︿綋閾呭皝妫�楠� / 鍗歌溅楣や綅 |
+| `density` | 瀵嗗害 |
+| `grossWeight` / `tareWeight` / `netWeight` / `lossQuantity` / `actualVolume` | 姣涢噸/鐨噸/鍑�閲�/浜忛噺/瀹為檯浣撶Н |
+| `unloadPhotos` / `poundPhotos` | 鍗告补鎷嶇収 / 纾呭崟锛圝SON 瀛楃涓诧級 |
+
+> 璇ユ帴鍙�**涓嶈繑鍥�** `oilDepotName`銆乣inboundCategory`銆乣inboundTime`銆乣tankId`鈥斺�旇杩欎簺璇风敤 4.2 鐨勮鎯呮帴鍙c��
+>
+> **鍙栨暟鍙e緞**锛氭湰椤垫寜**瑙勬牸鍨嬪彿**鑱氬悎锛堜竴琛屼竴涓鏍硷級锛屾墍浠� `tankNo` 鍙栫殑鏄瑙勬牸**鏈�杩戜竴娆″叆搴撹褰�**鐨勫偍缃愩�傚悓瑙勬牸鍒嗗竷鍦ㄥ涓綈鏃跺彧鏄剧ず涓�涓紝璇﹁绗� 6 鑺傞檺鍒躲��
+
+### 4.2 搴撳瓨璇︽儏 / 鎵瑰彿鏄庣粏 鈥斺�� `GET /stockInventory/getBatchNoQty`
+
+鎸� `productModelId`锛堝彲閫� `productId`锛夋煡锛岃繑鍥� `R.data.records`銆傝繖涓�椤垫槸**鎸夋壒娆$簿纭叧鑱�**鐨勶紝鍌ㄧ綈鍙峰噯纭��
+
+鏂板鍏ュ簱渚у垪锛�
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| `tankNo` | **鍌ㄧ綈鍙�** |
+| `oilDepotName` / `oilProduct` / `inboundCategory` / `inboundTime` | 娌瑰簱鍚嶇О / 娌瑰搧 / 鍏ュ簱绫诲埆 / 鍏ュ簱鏃堕棿 |
+| `tankInspectionStatus` / `unloadingCranePosition` / `density` | 妫�楠� / 楣や綅 / 瀵嗗害 |
+| `grossWeight` / `tareWeight` / `netWeight` / `lossQuantity` / `actualVolume` | 杩囩鏁版嵁 |
+| `unloadPhotos` / `poundPhotos` | JSON 瀛楃涓诧紝**宸查噸鏂扮鍙� URL锛屽彲鐩存帴鐢�** |
+
+鏂板鍑哄簱渚у垪锛堝彇璇ヨ鏍�**鏈�鏂颁竴鏉″嚭搴撹褰�**锛岀粺涓� `out` 鍓嶇紑锛夛細
+
+`outCategory`锛堝嚭搴撶被鍒細瀹㈠瓨/浠e偍/閿�鍞級銆乣outDate`銆乣outTonnage`銆乣outCustomerName`銆乣outContactPerson`銆乣outContactPhone`銆乣outOilDepotName`銆乣outTankNo`銆乣outOilProduct`銆乣outDensity`銆乣outDriverName`銆乣outDriverPhone`銆乣outEscortName`銆乣outEscortPhone`銆乣outTruckPlateNo`銆乣outTrailerPlateNo`銆乣outGrossWeight`銆乣outTareWeight`銆乣outNetWeight`銆乣outLossQuantity`銆乣outActualVolume`銆乣outPoundPhotos`
+
+鍙︽湁涓や釜鏈姞鍓嶇紑銆佷絾鍚屽睘鍑哄簱渚х殑鍒楋細`loadingInspectionStatus`锛堣杞︽楠岋級銆乣loadingCranePosition`锛堣杞﹂工浣嶏級銆乣loadingPhotos`锛堣杞︽媿鐓э紝JSON 瀛楃涓诧紝鍚屾牱宸查噸绛� URL锛夈��
+
+**鎺掑簭琛屼负鍙樻洿锛坄50921bdc2`锛�**锛氭帓搴忕敱 `order by batch_no` 鏀逛负 **`order by MAX(update_time) desc`**锛屽嵆**鎸夋洿鏂版椂闂村�掑簭**銆傚墠绔嫢鍘熸湰渚濊禆鎵瑰彿椤哄簭灞曠ず锛岄渶瑕佹敼鎴愭樉寮忔帓搴忔垨鎺ュ彈鏂伴『搴忋��
+
+### 4.3 瀵煎嚭 Excel 鈥斺�� `POST /stockInventory/exportStockInventory`
+
+瀵煎嚭鏂囦欢**鏈熬鏂板銆屽偍缃愬彿銆嶅垪**銆�
+
+- 璇ュ垪鍦ㄥ悗绔爣娉ㄤ负銆屼粎瀵煎嚭銆嶏紝鎵�浠�**涓嬭浇瀵煎叆妯℃澘锛坄POST /stockInventory/downloadStockInventory`锛変笉浼氬嚭鐜板偍缃愬彿鍒�**
+- **瀵煎叆涓嶆敮鎸佸~鍌ㄧ綈鍙�**锛氬鍏ヤ粛鎸夈�屼骇鍝佸悕绉� | 瑙勬牸銆嶅尮閰嶄骇鍝侊紝鍌ㄧ綈鍙蜂笉鍙備笌瑙f瀽銆傝嫢涓氬姟闇�瑕佹壒閲忓鍏ュ偍缃愬彿锛岄渶鍙︽彁闇�姹�
+
+### 4.4 鍏ュ簱绠$悊鍒楄〃 鈥斺�� `GET /stockInRecord/listPage`
+
+搴曞眰鐢� `select sir.*`锛屾墍浠ユ湰娆″叆搴撹褰曟柊澧炵殑瀛楁**鍏ㄩ儴鑷姩甯﹀嚭**锛屽墠绔洿鎺ョ敤鍗冲彲锛�
+
+`tankNo`銆乣tankId`銆乣oilDepotId`銆乣oilDepotName`銆乣inboundCategory`銆乣inboundTime`銆乣oilProduct`銆乣originalQuantity`銆乣customerId`銆乣customerName`銆乣contactPerson`銆乣contactPhone`銆乣driverName`銆乣driverPhone`銆乣escortName`銆乣escortPhone`銆乣truckPlateNo`銆乣trailerPlateNo`銆乣tankInspectionStatus`銆乣unloadingCranePosition`銆乣density`銆乣unloadPhotos`銆乣grossWeight`銆乣tareWeight`銆乣netWeight`銆乣lossQuantity`銆乣actualVolume`銆乣poundPhotos`
+
+### 4.5 闄勫甫锛氬嚭搴撹褰曞垪琛� 鈥斺�� `GET /stockOutRecord/listPage`
+
+鍚屾牱 `sor.*`锛屽嚭搴撲晶鏂板瀛楁鑷姩甯﹀嚭锛歚tankNo`銆乣tankId`銆乣oilDepotId`銆乣oilDepotName`銆乣outCategory`銆乣outDate`銆乣outTonnage`銆乣oilProduct`銆乣customerId`銆乣customerName`銆乣contactPerson`銆乣contactPhone`銆乣loadingInspectionStatus`銆乣loadingCranePosition`銆乣loadingPhotos`銆乣density`銆乣grossWeight`銆乣tareWeight`銆乣netWeight`銆乣lossQuantity`銆乣actualVolume`銆乣poundPhotos`銆乣driverName`銆乣driverPhone`銆乣escortName`銆乣escortPhone`銆乣truckPlateNo`銆乣trailerPlateNo`
+
+---
+
+## 5. 鏂板鎺ュ彛锛氫粎鍒涘缓娌瑰搧鍑哄簱璁板綍
+
+```
+POST /stockInventory/addOilOutRecordOnly
+```
+
+璇锋眰浣撲负 `StockOutRecordDto`锛屽瓧娈典笌 4.5 鍒楀嚭鐨勫嚭搴撲晶瀛楁涓�鑷达紙鏂板瀛楁鍚岃〃锛夈�傝涓猴細
+
+- `stockOutNum` 鐢� `outTonnage` 鍥炲~锛宍approvalStatus` 缃� `0`锛堝緟瀹℃壒锛�
+- `recordType` 鐢卞悗绔浐瀹氫负鑷畾涔夊嚭搴撶被鍨嬶紝`recordId` 鍥哄畾 `0`锛�**鍓嶇涓嶈浼�**
+- **涓嶈皟鏁村簱瀛�**锛屽彧钀戒竴鏉″嚭搴撹褰�
+
+---
+
+## 6. 宸茬煡闄愬埗锛堣仈璋冨墠闇�鐭ユ儏锛�
+
+1. **搴撳瓨绠$悊鍒楄〃鐨勫偍缃愬彿鏄�岃鏍肩骇銆嶈�岄潪銆屾壒娆$骇銆�**銆傝椤典竴琛屼竴涓鏍硷紝`batch_no` 鏄� `GROUP_CONCAT`锛屾墍浠ュ偍缃愬彿鍙兘鍙栬瑙勬牸鏈�杩戜竴鏉″叆搴撹褰曠殑鍊笺��**鍚屼竴瑙勬牸鍒嗗竷鍦ㄥ涓偍缃愭椂锛屽垪琛ㄥ彧浼氭樉绀烘渶杩戝叆搴撶殑閭d釜缃�**銆傝鎸夋壒娆$湅鍑嗙‘鐨勭綈锛岀敤 4.2 鐨勬壒鍙锋槑缁嗐��
+2. **鐜版湁鏁版嵁澶у鏄┖鐨�**銆傚綋鍓嶅簱鍐� 12 鏉″叆搴撹褰曢噷鍙湁 3 鏉℃湁 `tank_no`锛堝巻鍙插叆搴撴湭閲囬泦锛夈�傛墍浠ヨ仈璋冩椂搴撳瓨鍚勯〉澶ч儴鍒嗚鐨勫偍缃愬彿浼氭槸绌衡�斺��**杩欐槸鏁版嵁鐜扮姸锛屼笉鏄帴鍙i棶棰�**銆傞渶瑕佷笟鍔¤ˉ褰曪紝鎴栭噸鏂拌蛋涓�閬嶅甫鍌ㄧ綈鐨勫叆搴撱��
+3. 瀵煎嚭鐨勫偍缃愬彿鍒椾綅缃湪**鏈�鍚�**锛堝娉ㄤ箣鍚庯級锛岃嫢鍓嶇/涓氬姟鏈夊垪椤哄簭绾﹀畾闇�娉ㄦ剰銆�
+4. `addstockInventory` 涓嶉�忎紶娌瑰搧/鍌ㄧ綈瀛楁锛堣 3.1锛夛紝杩欐槸鏈鏈�瀹规槗韪╃殑鐐广��
+5. 鍓嶇鐣岄潰灞傛棤娉曞湪鏈悗绔粨搴撻獙璇侊紙鏃� `web/`銆乣ui/`锛夛紝浠ヤ笅娓呭崟闇�鍓嶇浠撳簱閰嶅悎鎵ц銆�
+
+---
+
+## 7. 鑱旇皟鑷煡娓呭崟
+
+- [ ] 鑿滃崟 2353 娌瑰簱淇℃伅銆�2354 鍌ㄧ綈淇℃伅鑳芥墦寮�骞跺鍒犳敼鏌ワ紙姝ゅ墠鍚庣缂哄け锛岀偣寮�鎶ラ敊锛�
+- [ ] 鍌ㄧ綈淇℃伅鑳戒繚瀛� `tankNo`銆乣category`銆乣oilProduct`銆乣capacity`銆乣remaining`
+- [ ] 鏂板鍏ュ簱琛ㄥ崟锛氭补搴�/鍌ㄧ綈鍋氭垚涓嬫媺锛坄/basic/oilDepot/all`銆乣/basic/tankInfo/all`锛夛紝閫夊偍缃愬悗 `tankNo` 鑷姩甯﹀嚭
+- [ ] 鏂板鍏ュ簱鎻愪氦鍚庯紝鍏ュ簱绠$悊鍒楄〃璇ヨ `tankNo` 鏈夊��
+- [ ] 璧扮殑鏄� `addStockInRecordOnly`锛堢敤 `addstockInventory` 鎻愪氦鍒� `tankNo` 涓虹┖ 鈥斺�� 鐢ㄦ潵鍙嶅悜纭 3.1锛�
+- [ ] 搴撳瓨绠$悊鍒楄〃鍑虹幇銆屽偍缃愬彿銆嶅垪锛屼笖涓庡悓瑙勬牸鏈�杩戜竴娆″叆搴撶殑缃愪竴鑷�
+- [ ] 搴撳瓨璇︽儏/鎵瑰彿鏄庣粏鍑虹幇銆屽偍缃愬彿銆嶏紝涓�**鍚屼竴瑙勬牸涓嶅悓鎵规鑳芥樉绀轰笉鍚岀綈**
+- [ ] 搴撳瓨璇︽儏鎺掑簭涓烘洿鏂版椂闂村�掑簭
+- [ ] 瀵煎嚭搴撳瓨 Excel 鏈熬鏈夈�屽偍缃愬彿銆嶅垪涓旀湁鍊�
+- [ ] 涓嬭浇瀵煎叆妯℃澘**涓嶅惈**鍌ㄧ綈鍙峰垪锛涚敤璇ユā鏉垮鍏ヤ粛鑳芥寜銆屼骇鍝佸悕绉皘瑙勬牸銆嶅尮閰嶆垚鍔�
+- [ ] 鐓х墖瀛楁锛氫笂浼犳椂浼犳暟缁勶紝鍥炴樉鏃惰兘姝g‘瑙f瀽 JSON 瀛楃涓诧紝鍥剧墖鍙墦寮�
+- [ ] 鍑哄簱璁板綍鍒楄〃 `/stockOutRecord/listPage` 鑳藉彇鍒� `tankNo` 绛夊嚭搴撲晶鏂板瓧娈�
diff --git "a/doc/20260920_\345\207\272\345\272\223\345\215\263\345\217\221\350\264\247\344\270\216\345\276\200\346\235\245\345\244\207\344\273\266\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260920_\345\207\272\345\272\223\345\215\263\345\217\221\350\264\247\344\270\216\345\276\200\346\235\245\345\244\207\344\273\266\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..81b68c0
--- /dev/null
+++ "b/doc/20260920_\345\207\272\345\272\223\345\215\263\345\217\221\350\264\247\344\270\216\345\276\200\346\235\245\345\244\207\344\273\266\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,235 @@
+# 鍑哄簱鍗冲彂璐� / 寰�鏉ヤ粯娆惧洖娆� / 澶囦欢娴佹按 鍓嶇鑱旇皟鏂囨。
+
+鏇存柊鏃堕棿锛�2026-09-20
+閫傜敤鐗堟湰锛氭湰娆″悗绔彉鏇村悗
+瀵瑰簲闇�姹傦細闇�姹傛竻鍗曠 11銆�13銆�14銆�15 鏉�
+
+---
+
+## 1. 鍙樻洿鑼冨洿
+
+| 闇�姹� | 鍐呭 | 褰卞搷闈� |
+| --- | --- | --- |
+| 13 | 娌瑰搧**閿�鍞嚭搴撳嵆鍙戣揣**锛氬嚭搴撴椂鑷姩鐢熸垚涓�寮犮�屽凡鍙戣揣銆嶅彂璐у彴璐︼紝涓嶅彂璧峰彂璐у鎵� | 鍑哄簱琛ㄥ崟銆佸嚭搴撳彴璐﹀垪琛� |
+| 11 | 瀹㈡埛/渚涘簲鍟嗗線鏉ュ悇**鏂板涓�涓笟鍔″瓧娈�**锛堝鎴枫�屼粯娆俱�嶃�佷緵搴斿晢銆屽洖娆俱�嶏級骞跺彲鎵嬪伐缁存姢娴佹按 | 瀹㈡埛寰�鏉ラ〉銆佷緵搴斿晢寰�鏉ラ〉 |
+| 15 | 澶囦欢**鍏宠仈浜у搧瑙勬牸**锛岄噰璐叆搴撹嚜鍔ㄧ疮鍔犲浠跺簱瀛橈紝骞舵柊澧炲浠跺嚭鍏ュ簱娴佹按 | 澶囦欢缁存姢椤点�佸浠舵祦姘撮〉 |
+| 14 | 鏂板鍏ュ簱閫夐噰璐鍗曞悗**绮剧‘鍙嶆樉鏄庣粏**锛堜慨澶嶅悗绔� SQL锛� | 鏂板鍏ュ簱椤� |
+
+DDL 宸叉墽琛岋紙`doc/20260920_stock_out_shipping_link.sql`銆乣doc/20260920_create_payment_record.sql`銆乣doc/20260920_spare_parts_product_and_record.sql`锛夈��
+
+---
+
+## 2. 闇�姹� 13锛氬嚭搴撳嵆鍙戣揣
+
+### 2.1 瑙勫垯璇存槑
+
+- 浠� **`type = 2`锛堥攢鍞嚭搴擄級** 瑙﹀彂鑷姩鍙戣揣锛沗type = 1` 閲囪喘鍑哄簱銆乣type = 3` 鑷畾涔夊嚭搴撲笉鍙楀奖鍝嶃��
+- 鑷姩鐢熸垚鐨勫彂璐у彴璐� `status` 鐩存帴涓� **`宸插彂璐**锛屼笉杩涘叆瀹℃壒锛屼篃涓嶅嚭鐜板湪銆屽緟瀹℃牳銆嶅垪琛ㄣ��
+- 鍙戣揣鍗曞彿鐢卞悗绔敓鎴愶紝鏍煎紡 `SH` + `yyyyMMdd` + 褰撴棩 3 浣嶆祦姘达紝濡� `SH20260920001`銆�
+- **鍚屼竴閿�鍞骇鍝佸厑璁稿垎鎵瑰娆″彂璐�**锛氭瘡寮犲嚭搴撳崟瀵瑰簲涓�寮犳柊鐨勫彂璐у崟锛屽悇鑷墸鍑忎竴娆″簱瀛樸��
+- 鍑哄簱鍗曚笌鍙戣揣鍗曢�氳繃 `procurement_record_out.shipping_info_id` 鍏宠仈锛�**鍒犻櫎鍑哄簱鍗曚細杩炲甫鍐查攢鍙戣揣鍗曞苟鍥炴粴宸叉墸搴撳瓨**銆�
+
+### 2.2 鍑哄簱鎺ュ彛锛氭柊澧炲繀濉� `batchNo`
+
+`POST /stockmanagement/stockout`
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+| --- | --- | --- | --- |
+| `type` | Integer | 鏄� | `2` 閿�鍞嚭搴撴椂鎵嶈Е鍙戣嚜鍔ㄥ彂璐� |
+| `batchNo` | String | **閿�鍞嚭搴撳繀濉�** | 鍑哄簱鎵规銆備负绌烘椂鍚庣鎶涖�岃閫夋嫨鍑哄簱鎵规銆� |
+| `salesLedgerProductId` | Long | 閿�鍞嚭搴撳繀濉� | 闇�鑳藉弽鏌ュ埌 `sales_ledger_product` 涓� `type = 1`锛堥攢鍞級 |
+| `productModelId` | Long | 鏄� | 瑙勬牸鍨嬪彿 id |
+| `quantity` | String | 鏄� | 鍑哄簱鏁伴噺 |
+| `id` | Long | 鏄� | 瀵瑰簲鐨� `procurement_record_storage` 鏄庣粏 id |
+
+**鎵规涓嶅厑璁哥暀绌�**锛氱郴缁熸棤鍏堣繘鍏堝嚭锛屽簱瀛樻寜鎵规绠$悊锛屽悗绔寜 `productModelId + batchNo` 鎵e噺锛屾壒娆′笉瀵逛細鎶ャ�屽簱瀛樿褰曚笉瀛樺湪銆嶃��
+
+### 2.3 鏂板锛氭壒娆″彲閫夊垪琛�
+
+```
+GET /stockmanagement/batchNoList?productModelId={瑙勬牸鍨嬪彿id}
+```
+
+鍝嶅簲锛�
+
+```json
+{
+ "code": 200,
+ "data": [
+ { "productModelId": 405, "batchNo": "20260915-001-001", "availableQuantity": 33.00 },
+ { "productModelId": 405, "batchNo": "CG20260825001-192", "availableQuantity": 10000.00 }
+ ]
+}
+```
+
+- `availableQuantity` = 搴撳瓨閲� 鈭� 閿佸畾鏁伴噺锛屽凡杩囨护鎺� 鈮� 0 鐨勬壒娆°��
+- 鍓嶇鍦ㄥ嚭搴撹〃鍗曢�夋嫨瑙勬牸鍨嬪彿鍚庤皟鐢紝鎶� `batchNo` 鍋氭垚涓嬫媺锛屽苟鎶婂彲鐢ㄩ噺灞曠ず缁欑敤鎴枫��
+
+### 2.4 鍑哄簱鍙拌处鍒楄〃鏂板鍙戣揣鍗曞彿
+
+`GET /stockmanagement/listPageByProduct` 杩斿洖浣撳湪鍘熸湁瀛楁鍩虹涓�**鏂板**锛�
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| `shippingNo` | 鑷姩鐢熸垚鐨勫彂璐у崟鍙凤紝濡� `SH20260920001`锛涢潪閿�鍞嚭搴撲负 `null` |
+
+> `GET /stockmanagement/listPage`锛堥噰璐嚭搴� type=1锛�**涓嶈繑鍥�**璇ュ瓧娈碉紝閲囪喘鍑哄簱涓嶅彂璐с��
+
+### 2.5 鍒犻櫎鍑哄簱鍗�
+
+`POST /stockmanagement/del` 琛屼负宸插彉鏇达細鑻ヨ鍑哄簱鍗曞凡鑷姩鐢熸垚鍙戣揣鍗曪紝鍚庣浼氬厛鍒犻櫎鍙戣揣鍗曚笌鏄庣粏銆佸洖婊氬簱瀛橈紝鍐嶅垹闄ゅ嚭搴撳崟銆傚墠绔�**鏃犻渶鏀瑰姩璋冪敤鏂瑰紡**锛屼絾鎻愮ず鏂囨鍙鏄庛�屼細鍚屾椂鍐查攢鍙戣揣璁板綍銆嶃��
+
+---
+
+## 3. 闇�姹� 11锛氬線鏉ヤ粯娆� / 鍥炴
+
+### 3.1 璇箟
+
+| 寰�鏉� | 鏂板瀛楁 | 鍙栧�兼潵婧� |
+| --- | --- | --- |
+| 瀹㈡埛寰�鏉� | `paymentAmount`锛�**浠樻**锛� | `payment_record` 涓� `party_type=1 and direction=1` 鐨勫悎璁� |
+| 渚涘簲鍟嗗線鏉� | `receiptAmount`锛�**鍥炴**锛� | `payment_record` 涓� `party_type=2 and direction=2` 鐨勫悎璁� |
+
+**鍙湁涓�涓柟鍚戞槸鎵嬪伐娴佹按**锛氬鎴蜂晶鐨勩�屾敹娆鹃噾棰濄�峘receiptPaymentAmount`銆佷緵搴斿晢渚х殑銆屼粯娆鹃噾棰濄�峘paymentAmount` 浠嶆潵鑷師鏈夌殑閲嶅崟鎹〃锛�**璁$畻鍙e緞鏈彉**锛沗receiptableAmount`锛堝簲鏀讹級涓� `payableAmount`锛堝簲浠橈級鍏紡**淇濇寔涓嶅彉**锛屾柊瀛楁鍙仛灞曠ず锛屼笉鍙備笌浣欓璁$畻銆�
+
+### 3.2 杩斿洖瀛楁鏂板
+
+| 鎺ュ彛 | 鏂板杩斿洖瀛楁 |
+| --- | --- |
+| `GET /metricStatistics/customewTransactions` | `paymentAmount` |
+| `GET /metricStatistics/customewTransactionsDetails` | `paymentAmount` |
+| `GET /purchase/report/supplierTransactions` | `receiptAmount` |
+| `GET /purchase/report/supplierTransactionsDetails` | `receiptAmount` |
+
+> 鏄庣粏鎺ュ彛鐨� `paymentAmount` / `receiptAmount` 鏄�**寰�鏉ユ柟缁村害**鐨勫悎璁★紙璇ュ鎴�/渚涘簲鍟嗗叏閮ㄦ祦姘村悎璁★級锛屾祦姘存湰韬笉鎸傚埌鍗曞紶鍚堝悓涓婏紝鍥犳鏄庣粏鍚勮璇ュ垪鍊肩浉鍚屻�傝嫢鍓嶇甯屾湜鍙湪鍒楄〃椤靛睍绀猴紝鏄庣粏椤靛彲闅愯棌璇ュ垪銆�
+
+### 3.3 鏂板锛氫粯娆惧洖娆炬祦姘村洓鎺ュ彛
+
+鍓嶇紑 `/paymentRecord`銆�
+
+| 鎺ュ彛 | 鏂规硶 | 璇存槑 |
+| --- | --- | --- |
+| `/paymentRecord/listPage` | GET | 鍒嗛〉鏌ヨ锛屾敮鎸� `partyType`銆乣partyId`銆乣direction`銆乣paymentMethod` |
+| `/paymentRecord/add` | POST | 鏂板 |
+| `/paymentRecord/update` | POST | 淇敼锛屽繀椤诲甫 `id` |
+| `/paymentRecord/delete` | DELETE | 鎵归噺鍒犻櫎锛宐ody 涓� id 鏁扮粍 |
+
+璇锋眰浣撳瓧娈碉細
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+| --- | --- | --- | --- |
+| `id` | Long | 淇敼蹇呭~ | 涓婚敭 |
+| `partyType` | Integer | 鏄� | `1` 瀹㈡埛锛宍2` 渚涘簲鍟� |
+| `partyId` | Long | 鏄� | 瀹㈡埛 id 鎴栦緵搴斿晢 id |
+| `direction` | Integer | 鏄� | `1` 浠樻锛宍2` 鍥炴 |
+| `amount` | BigDecimal | 鏄� | 閲戦 |
+| `recordDate` | String | 鍚� | 鍙戠敓鏃ユ湡锛屾牸寮� `yyyy-MM-dd` |
+| `paymentMethod` | String | 鍚� | 浠樻/鍥炴鏂瑰紡 |
+| `remark` | String | 鍚� | 澶囨敞 |
+
+`tenantId` / `deptId` / `createUser` / `createTime` / `updateUser` / `updateTime` 鐢卞悗绔嚜鍔ㄥ~鍏咃紝**鍓嶇涓嶈浼�**銆�
+
+**鏌ヨ鍙傛暟绀轰緥**锛堝鎴峰線鏉ラ〉銆屾柊澧炰粯娆俱�嶅悗鏌ョ湅璇ュ鎴锋祦姘达級锛�
+
+```
+GET /paymentRecord/listPage?pageNum=1&pageSize=10&partyType=1&partyId=449&direction=1
+```
+
+`listPage` 杩斿洖缁撴瀯涓� MyBatis-Plus 鍒嗛〉瀵硅薄锛屽墠绔彇 `data.records`銆�
+
+---
+
+## 4. 闇�姹� 15锛氬浠跺叧鑱斾骇鍝佷笌鍑哄叆搴撴祦姘�
+
+### 4.1 澶囦欢鏂板涓や釜瀛楁
+
+`GET /spareParts/listPage`銆乣POST /spareParts/add`銆乣POST /spareParts/update` 鐨勫浠跺璞�**鏂板**锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+| --- | --- | --- |
+| `productId` | Long | 鍏宠仈浜у搧澶х被 id |
+| `productModelId` | Long | 鍏宠仈浜у搧**瑙勬牸鍨嬪彿** id |
+
+**杩欐槸鎵撻�氶噰璐殑涓氬姟閽╁瓙**锛氶噰璐彴璐︽柊澧炴槑缁嗘椂锛屽悗绔敤鏄庣粏鐨� `productModelId` 鍘诲尮閰� `spare_parts.product_model_id`銆傚尮閰嶄笂鍒欙細
+
+1. `spare_parts.quantity` 绱姞璇ユ槑缁嗘暟閲忥紱
+2. 鍐欎竴鏉″浠跺叆搴撴祦姘达紙`direction=1`銆乣sourceType=1`銆乣sourceId=閲囪喘鍙拌处id`銆乣sourceNo=閲囪喘鍚堝悓鍙穈锛夈��
+
+鏈淮鎶� `productModelId` 鐨勫浠�**涓嶄細**琚尮閰嶏紝闈欓粯璺宠繃锛屼笉褰卞搷閲囪喘鍙拌处淇濆瓨銆�
+
+> 浠呭湪閲囪喘鍙拌处**鏂板鏄庣粏**鏃跺洖鍐欙紱缂栬緫閲囪喘鍙拌处璧版洿鏂板垎鏀紝涓嶄細閲嶅璁℃暟銆�
+
+### 4.2 鏂板锛氬浠跺嚭鍏ュ簱娴佹按
+
+鍓嶇紑 `/sparePartsRecord`銆�
+
+| 鎺ュ彛 | 鏂规硶 | 璇存槑 |
+| --- | --- | --- |
+| `/sparePartsRecord/listPage` | GET | 鍒嗛〉鏌ヨ锛屾敮鎸� `sparePartsId`銆乣direction`銆乣sourceType` |
+| `/sparePartsRecord/delete` | DELETE | 鎵归噺鍒犻櫎锛宐ody 涓� id 鏁扮粍 |
+
+杩斿洖瀛楁锛�
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| `sparePartsId` | 澶囦欢 id |
+| `sparePartsName` | 澶囦欢鍚嶇О锛堝悗绔� join 甯﹀嚭锛� |
+| `direction` | `1` 鍏ュ簱锛宍2` 鍑哄簱 |
+| `quantity` | 鏁伴噺 |
+| `sourceType` | 鏉ユ簮绫诲瀷锛岃涓嬭〃 |
+| `sourceId` | 鏉ユ簮鍗曟嵁 id |
+| `sourceNo` | 鏉ユ簮鍗曞彿 |
+| `remark` | 澶囨敞 |
+| `createTime` | 鍙戠敓鏃堕棿 |
+
+`sourceType` 鏋氫妇锛�
+
+| 鍊� | 鍚箟 | 瑙﹀彂鐐� |
+| --- | --- | --- |
+| `1` | 閲囪喘鍏ュ簱 | 閲囪喘鍙拌处鏂板鍚瑙勬牸鐨勬槑缁� |
+| `2` | 缁翠慨棰嗙敤 | 璁惧鎶ヤ慨鍗曟洿鏂版椂棰嗙敤澶囦欢 |
+| `3` | 淇濆吇棰嗙敤 | 璁惧淇濆吇鍗曟洿鏂版椂棰嗙敤澶囦欢 |
+
+> 缁翠慨/淇濆吇**棰嗙敤**浠嶆部鐢ㄥ師鏈� `spare_parts_requisition_record`锛堣澶囩淮搴︼紝鍙︽湁椤甸潰锛夛紱鏈鏂板鐨� `spare_parts_record` 鏄�**澶囦欢缁村害**鐨勮繘鍑烘祦姘达紝涓よ�呭苟瀛橈紝鑱岃矗涓嶅悓銆�
+
+### 4.3 鍓嶇椤甸潰寤鸿
+
+- 澶囦欢缁存姢琛ㄥ崟锛氫骇鍝佸ぇ绫汇�佽鏍煎瀷鍙蜂袱涓笅鎷夛紙澶嶇敤浜у搧鎺ュ彛锛夈��
+- 鏂板椤甸潰锛氬浠跺嚭鍏ュ簱娴佹按锛堟斁鍦ㄨ澶囩鐞嗘垨澶囦欢绠$悊涓嬶級锛屽垪琛ㄥ垪寤鸿锛氬浠跺悕绉般�佹柟鍚戙�佹暟閲忋�佹潵婧愮被鍨嬨�佹潵婧愬崟鍙枫�佹椂闂淬��
+
+---
+
+## 5. 闇�姹� 14锛氭柊澧炲叆搴撳叧鑱旇鍗曞弽鏄�
+
+### 5.1 鍚庣淇璇存槑锛堝墠绔帴鍙e舰鎬佷笉鍙橈級
+
+鍘� `listProcurementBySalesLedgerId` 鐨� join 鏉′欢鍐欎簡 `slp.type = 2`锛屼絾 `slp` 鏈畾涔夛紝瀵艰嚧**绫诲瀷杩囨护瀹為檯涓嶇敓鏁�**锛屽彲鑳借繑鍥為潪閲囪喘鏄庣粏銆傚凡淇涓� `t2.type = 2`銆�
+
+### 5.2 鏂板鍙�夊弬鏁�
+
+`GET /stockin/productlist` 鏂板鍙�夊弬鏁帮細
+
+| 鍙傛暟 | 绫诲瀷 | 璇存槑 |
+| --- | --- | --- |
+| `purchaseLedgerId` | Integer | **鎺ㄨ崘**銆傛寜閲囪喘鍙拌处 id 绮剧‘鍙嶆樉鏄庣粏 |
+| `purchaseContractNumber` | String | 鍏煎淇濈暀锛屾寜鍚堝悓鍙峰尮閰� |
+
+涓や釜鍙傛暟鍙彧浼犲叾涓�锛沗purchaseLedgerId` 浼樺厛銆傚墠绔粠閲囪喘璁㈠崟涓嬫媺閫変腑璁㈠崟鍚庯紝鐩存帴鎶婅璁㈠崟鐨� id 浣滀负 `purchaseLedgerId` 浼犲叆锛屾瘮浼犲悎鍚屽彿鏇村彲闈狅紙鍚堝悓鍙峰彲鑳介噸澶�/涓虹┖锛夈��
+
+鍑哄簱渚у弽鏄炬部鐢ㄥ凡鏈夋帴鍙o紝**鏃犲彉鏇�**锛歚GET /sales/ledger/getSalesLedgerWithProducts`銆�
+
+---
+
+## 6. 鑱旇皟鑷煡娓呭崟
+
+- [ ] 鍑哄簱琛ㄥ崟锛氶�夎鏍煎悗鑳芥媺鍒版壒娆″垪琛紝鎵规涓哄繀濉」
+- [ ] 鍑哄簱锛坱ype=2锛夊悗锛屽嚭搴撳彴璐︺�屽彂璐у崟鍙枫�嶅垪鏈夊�硷紝鏍煎紡 `SH+鏃ユ湡+娴佹按`
+- [ ] 鍙戣揣鍙拌处涓兘鐪嬪埌璇ヨ嚜鍔ㄧ敓鎴愮殑鍗曪紝鐘舵�佷负 `宸插彂璐
+- [ ] 鍚屼竴浜у搧鍐嶆鍑哄簱锛岀敓鎴�**鍙︿竴寮�**鍙戣揣鍗曪紙涓嶆槸琚嫤鎴級
+- [ ] 鍒犻櫎鍑哄簱鍗曞悗锛屽彂璐у彴璐︺�佸彂璐ф槑缁嗐�佸簱瀛樻墸鍑忚褰曚竴骞舵秷澶�
+- [ ] 瀹㈡埛寰�鏉ュ垪琛�/鏄庣粏鍑虹幇銆屼粯娆俱�嶅垪锛屾彃鍏ヤ竴鏉� `direction=1` 娴佹按鍚庢暟鍊煎彉鍖栵紝涓斻�屽簲鏀躲�嶄笉鍙�
+- [ ] 渚涘簲鍟嗗線鏉ュ垪琛�/鏄庣粏鍑虹幇銆屽洖娆俱�嶅垪锛岄�昏緫鍚屼笂
+- [ ] 澶囦欢缁存姢鑳戒繚瀛樹骇鍝佸ぇ绫� + 瑙勬牸鍨嬪彿
+- [ ] 鏂板涓�寮犲惈璇ヨ鏍肩殑閲囪喘鍙拌处锛屽浠舵暟閲忓鍔狅紝澶囦欢娴佹按鍑虹幇 `direction=1` 璁板綍
+- [ ] 璁惧鎶ヤ慨/淇濆吇棰嗙敤澶囦欢鍚庯紝澶囦欢娴佹按鍑虹幇 `direction=2` 璁板綍
+- [ ] 鏂板鍏ュ簱閫夎鍗曞悗锛屾槑缁嗗彧鏄剧ず閲囪喘绫伙紙type=2锛変骇鍝�
diff --git "a/doc/20260920_\351\207\207\350\264\255\347\224\263\350\257\267\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/20260920_\351\207\207\350\264\255\347\224\263\350\257\267\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..21d38a3
--- /dev/null
+++ "b/doc/20260920_\351\207\207\350\264\255\347\224\263\350\257\267\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,322 @@
+# 閲囪喘鐢宠妯″潡鍓嶇鑱旇皟鏂囨。
+
+鏇存柊鏃堕棿锛�2026-09-20
+閫傜敤鐗堟湰锛氭湰娆″悗绔彉鏇村悗
+
+## 1. 鍙樻洿鑼冨洿
+
+1. 鑿滃崟銆岄噰璐鐞� 鈫� **閲囪喘瀹℃牳**銆嶆敼鍚嶄负銆�**閲囪喘绠$悊 鈫� 閲囪喘鐢宠**銆嶏紝鍘绘帀瀹℃牳鐜妭銆�
+2. 鏂板閲囪喘鐢宠鍗曟ā鍧楋紙鐙珛浜庨噰璐彴璐︼級锛屾敮鎸佷繚瀛樿崏绋裤�佺紪杈戙�佸垹闄ゃ��**杞鍗�**銆�
+3. 杞鍗曞鐢ㄩ噰璐彴璐︽柊澧為�昏緫锛氳嚜鍔ㄧ敓鎴愰噰璐悎鍚屽彿銆佸啓鍏ラ噰璐彴璐︿笌浜у搧鏄庣粏锛屽苟**鑷姩鎻愪氦 OA 閲囪喘瀹℃壒**锛堝鎵规祦绋嬩笉鍦ㄦ湰妯″潡鍐咃紝浠嶅湪銆屽鎵圭鐞嗐�嶄腑澶勭悊锛夈��
+4. 鍚庣鎺ュ彛鍓嶇紑锛歚/purchase/application`銆�
+
+### 1.1 鑿滃崟鏀瑰姩锛堟暟鎹簱宸叉墽琛岋級
+
+| 椤� | 鍘熷�� | 鏂板�� |
+| --- | --- | --- |
+| `sys_menu.menu_name`锛坢enu_id=2355锛� | 閲囪喘瀹℃牳 | 閲囪喘鐢宠 |
+| `path` | purchaseReview | purchaseApplication |
+| `component` | procurementManagement/purchaseReview/index | procurementManagement/purchaseApplication/index |
+
+**鍓嶇闇�鏂板椤甸潰**锛歚src/views/procurementManagement/purchaseApplication/index.vue`锛堝師 `purchaseReview` 椤甸潰宸蹭笉鍐嶈鑿滃崟寮曠敤锛屽彲鍒犻櫎鎴栦繚鐣欙級銆�
+
+---
+
+## 2. 鐘舵�佹灇涓�
+
+| 鍊� | 鍚箟 | 鍓嶇鍙搷浣滈」 |
+| --- | --- | --- |
+| `0` | 鑽夌 | 缂栬緫銆佸垹闄ゃ�佽浆璁㈠崟 |
+| `1` | 宸茬敓鎴愯鍗� | 浠呮煡鐪� |
+
+璇存槑锛�
+
+- 鏂板鐨勭敵璇峰崟**寮哄埗涓鸿崏绋�**锛屽墠绔笉闇�瑕佷篃涓嶅厑璁镐紶 `status`銆�
+- 缂栬緫銆佸垹闄ゆ帴鍙i兘浼氭牎楠岀姸鎬侊紝闈炶崏绋夸細杩斿洖閿欒锛堣 4.6 甯歌閿欒锛夈��
+- 杞鍗曟垚鍔熷悗鐘舵�佽嚜鍔ㄥ彉涓� `1`锛屽苟鍐欏叆 `purchaseLedgerId`銆乣convertTime`銆�
+
+---
+
+## 3. 鍏叡瀛楁璇存槑锛堝悗绔嚜鍔ㄧ淮鎶わ紝鍓嶇鍙锛�
+
+| 瀛楁 | 璇存槑 |
+| --- | --- |
+| `applicationNo` | 鐢宠鍗曞彿锛屾柊澧炴椂鍚庣鐢熸垚锛屾牸寮� `CGSQ` + 鏃ユ湡 + 褰撴棩娴佹按锛屽 `CGSQ20260920001`銆�**鍓嶇涓嶈浼�**锛屼紶浜嗕篃浼氳蹇界暐銆� |
+| `supplierName` | 渚濇嵁 `supplierId` 鍙嶆煡鍥炲~锛屽墠绔笉蹇呬紶锛堜紶浜嗕互鍚庣缁撴灉涓哄噯锛夈�� |
+| `applicantId` / `applicantName` | 涓嶄紶鏃堕粯璁ゅ彇褰撳墠鐧诲綍鐢ㄦ埛锛涗篃鍙樉寮忎紶 `applicantId` 鎸囧畾鐢宠浜恒�俙applicantName` 鐢卞悗绔牴鎹� `applicantId` 鏌ユ樀绉板洖濉�� |
+| `applyDate` | 涓嶄紶鏃堕粯璁ゅ綋澶┿�� |
+| `productCategory` / `specificationModel` | **鏄庣粏琛�**涓殑杩欎袱涓瓧娈电敱鍚庣鏍规嵁 `productId` / `productModelId` 鍙嶆煡鍐欏叆锛屽墠绔彧闇�浼� id锛屼紶浜嗕篃浼氳瑕嗙洊銆� |
+| `tenantId` / `deptId` / `createUser` / `createTime` / `updateTime` | 鍚庣鑷姩濉厖锛屽墠绔笉瑕佷紶銆� |
+| `purchaseLedgerId` / `convertTime` | 杞鍗曟垚鍔熷悗鎵嶆湁鍊笺�� |
+
+---
+
+## 4. 鎺ュ彛娓呭崟
+
+### 4.1 鏌ヨ閲囪喘鐢宠鍒楄〃
+
+`GET /purchase/application/list`
+
+**璇锋眰鍙傛暟**锛堝潎涓哄彲閫夛紝query 褰㈠紡锛�
+
+| 鍙傛暟 | 绫诲瀷 | 璇存槑 |
+| --- | --- | --- |
+| `applicationNo` | string | 鐢宠鍗曞彿锛屾ā绯婂尮閰� |
+| `projectName` | string | 椤圭洰鍚嶇О锛屾ā绯婂尮閰� |
+| `supplierId` | number | 渚涘簲鍟唅d锛岀簿纭尮閰� |
+| `status` | number | 鐘舵�侊紝`0` 鑽夌 / `1` 宸茬敓鎴愯鍗� |
+| `startDate` | string | 鐢宠鏃ユ湡璧凤紝`yyyy-MM-dd` |
+| `endDate` | string | 鐢宠鏃ユ湡姝紝`yyyy-MM-dd` |
+| `pageNum` | number | 椤电爜锛屽垎椤靛弬鏁� |
+| `pageSize` | number | 姣忛〉鏉℃暟锛屽垎椤靛弬鏁� |
+
+鎺掑簭锛氭寜 `id` 鍊掑簭锛�**涓嶆敮鎸佷紶鍏ユ帓搴忓瓧娈�**銆�
+
+**杩斿洖**锛歊uoYi 鏍囧噯鍒嗛〉缁撴瀯 `{ total, rows, code, msg }`锛堟敞鎰忔槸 `rows`锛屼笉鏄� `records`锛夈��
+
+```json
+{
+ "total": 1,
+ "rows": [
+ {
+ "id": 3,
+ "applicationNo": "CGSQ20260920001",
+ "supplierId": 12,
+ "supplierName": "XX渚涘簲鍟�",
+ "projectName": "鍗楅�歑X椤圭洰",
+ "salesContractNo": "XS20260901001",
+ "salesLedgerId": 88,
+ "applicantId": 1,
+ "applicantName": "寮犱笁",
+ "applyDate": "2026-09-20",
+ "expectedArrivalDate": "2026-09-30",
+ "contractAmount": 11300.00,
+ "paymentMethod": "鐢垫眹",
+ "remarks": "绱ф��",
+ "status": 0,
+ "purchaseLedgerId": null,
+ "convertTime": null,
+ "createTime": "2026-09-20 10:12:33",
+ "updateTime": "2026-09-20 10:12:33"
+ }
+ ],
+ "code": 200,
+ "msg": "鏌ヨ鎴愬姛"
+}
+```
+
+> 鍒楄〃鎺ュ彛**涓嶈繑鍥�** `productData` 鏄庣粏锛屾槑缁嗚鍗曠嫭璋� 4.2 鑾峰彇銆�
+
+---
+
+### 4.2 鏌ヨ閲囪喘鐢宠璇︽儏
+
+`GET /purchase/application/getById?id={id}`
+
+**杩斿洖**锛欰jaxResult锛宍data` 涓虹敵璇峰崟瀵硅薄锛堝瓧娈靛悓 4.1锛屽彟鍚� `productData` 鏄庣粏鏁扮粍锛夈��
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "id": 3,
+ "applicationNo": "CGSQ20260920001",
+ "supplierId": 12,
+ "supplierName": "XX渚涘簲鍟�",
+ "projectName": "鍗楅�歑X椤圭洰",
+ "applyDate": "2026-09-20",
+ "contractAmount": 11300.00,
+ "status": 0,
+ "productData": [
+ {
+ "id": 21,
+ "purchaseApplicationId": 3,
+ "productId": 5,
+ "productModelId": 17,
+ "productCategory": "閾滄潗",
+ "specificationModel": "T2-30x3",
+ "unit": "绫�",
+ "quantity": 100,
+ "taxRate": 13,
+ "taxInclusiveUnitPrice": 113.00,
+ "taxInclusiveTotalPrice": 11300.00,
+ "taxExclusiveTotalPrice": 10000.00,
+ "invoiceType": "澧炲�肩◣涓撶敤鍙戠エ",
+ "isChecked": true,
+ "remark": ""
+ }
+ ]
+ }
+}
+```
+
+鐢宠鍗曚笉瀛樺湪鏃惰繑鍥� `code=500`銆乣msg="閲囪喘鐢宠涓嶅瓨鍦�"`銆�
+
+---
+
+### 4.3 鏂板閲囪喘鐢宠锛堣崏绋匡級
+
+`POST /purchase/application/add`
+
+`Content-Type: application/json`
+
+**璇锋眰浣�**锛氬悓 4.2 鐨� `data` 缁撴瀯锛宍id`銆乣applicationNo`銆乣status`銆乣purchaseLedgerId`銆乣convertTime` 涓嶉渶瑕佷紶銆�
+
+**杩斿洖**锛歚data` 涓烘柊鐢宠鍗� id銆�
+
+```json
+{ "code": 200, "msg": "鎿嶄綔鎴愬姛", "data": 3 }
+```
+
+---
+
+### 4.4 淇敼閲囪喘鐢宠
+
+`POST /purchase/application/edit`
+
+`Content-Type: application/json`
+
+**璇锋眰浣�**锛氬悓 4.3锛�**蹇呴』甯� `id`**锛屾槑缁嗛渶鍏ㄩ噺鎻愪氦锛堝悗绔负銆屽厛鍒犲悗鎻掋�嶏級銆�
+
+**杩斿洖**锛歚{ "code": 200, "msg": "鎿嶄綔鎴愬姛" }`
+
+> 浠呰崏绋跨姸鎬佸彲淇敼銆傚凡鐢熸垚璁㈠崟鐨勭敵璇峰崟淇敼浼氳繑鍥為敊璇��
+
+---
+
+### 4.5 鍒犻櫎閲囪喘鐢宠
+
+`DELETE /purchase/application/delete`
+
+`Content-Type: application/json`
+
+**璇锋眰浣�**锛歩d 鏁扮粍锛屾敮鎸佹壒閲忋��
+
+```json
+[3, 4]
+```
+
+**杩斿洖**锛歚{ "code": 200, "msg": "鎿嶄綔鎴愬姛" }`
+
+> 浠呰崏绋跨姸鎬佸彲鍒犻櫎銆傝姹備綋涓虹┖鏃惰繑鍥� `code=500`銆乣msg="璇烽�夋嫨瑕佸垹闄ょ殑閲囪喘鐢宠"`銆�
+
+---
+
+### 4.6 閲囪喘鐢宠杞鍗�
+
+`POST /purchase/application/convertToOrder?id={id}`
+
+> 娉ㄦ剰锛歚id` 鏄� **query 鍙傛暟**锛屼笉鏄姹備綋銆�
+
+**杩斿洖**锛歚data` 涓虹敓鎴愮殑閲囪喘鍙拌处 id锛堝嵆銆岄噰璐鍗曘�峣d锛屽悗缁叆搴撱�佽川妫�銆佷粯娆剧瓑娴佺▼閮界敤瀹冿級銆�
+
+```json
+{ "code": 200, "msg": "鎿嶄綔鎴愬姛", "data": 1201 }
+```
+
+**杞鍗曞仛浜嗕粈涔�**锛堝墠绔簡瑙e嵆鍙紝鏃犻渶棰濆璋冪敤锛夛細
+
+1. 鏍¢獙锛氫緵搴斿晢宸查�夋嫨涓斿瓨鍦ㄣ�佸瓨鍦ㄤ骇鍝佹槑缁嗭紱
+2. 鎶㈠崰鐘舵�侊細浠ユ潯浠舵洿鏂版妸鐘舵�佷粠 `0` 鏀逛负 `1`锛�**骞跺彂閲嶅鐐瑰嚮鍙湁绗竴娆′細鎴愬姛**锛�
+3. 鐢熸垚閲囪喘鍚堝悓鍙凤紙鐢遍噰璐彴璐︿晶瑙勫垯鐢熸垚锛夛紱
+4. 鍐欏叆 `purchase_ledger` 涓昏〃 + `sales_ledger_product` 浜у搧鏄庣粏锛坄type=2`锛夛紱
+5. 鑷姩鎻愪氦 OA 閲囪喘瀹℃壒锛堟祦绋嬭銆屽鎵圭鐞嗐�嶏紝鏈ā鍧椾笉鍐嶆湁瀹℃牳鎸夐挳锛夛紱
+6. 鍥炲~ `purchaseLedgerId`銆乣convertTime`銆�
+
+浠讳竴姝ュけ璐ユ暣涓簨鍔″洖婊氾紝鐢宠鍗曚粛淇濇寔鑽夌鐘舵�併��
+
+---
+
+## 5. 甯歌閿欒杩斿洖
+
+缁熶竴涓� `{ "code": 500, "msg": "<閿欒淇℃伅>" }`锛屽墠绔洿鎺� toast `msg` 鍗冲彲銆�
+
+| msg | 瑙﹀彂鍦烘櫙 |
+| --- | --- |
+| `閲囪喘鐢宠涓嶅瓨鍦╜ | 璇︽儏/缂栬緫/杞鍗曚紶鍏ョ殑 id 涓嶅瓨鍦� |
+| `閲囪喘鐢宠id涓嶈兘涓虹┖` | 缂栬緫銆佽浆璁㈠崟鏈紶 id |
+| `宸茬敓鎴愯鍗曠殑閲囪喘鐢宠涓嶅厑璁镐慨鏀筦 | 瀵� `status=1` 鐨勫崟鎹皟鐢ㄧ紪杈� |
+| `宸茬敓鎴愯鍗曠殑閲囪喘鐢宠涓嶅厑璁稿垹闄わ細<鍗曞彿>` | 瀵� `status=1` 鐨勫崟鎹皟鐢ㄥ垹闄わ紝浼氬洖鏄惧叿浣撳崟鍙� |
+| `璇烽�夋嫨瑕佸垹闄ょ殑閲囪喘鐢宠` | 鍒犻櫎鎺ュ彛璇锋眰浣撲负绌烘暟缁� |
+| `璇峰厛閫夋嫨渚涘簲鍟嗗啀杞鍗昤 | 杞鍗曟椂 `supplierId` 涓虹┖ |
+| `渚涘簲鍟嗕笉瀛樺湪锛岃閲嶆柊閫夋嫨` | `supplierId` 鍦ㄤ緵搴斿晢琛ㄤ腑鏌ヤ笉鍒� |
+| `閲囪喘鐢宠娌℃湁浜у搧鏄庣粏锛屾棤娉曡浆璁㈠崟` | 鐢宠鍗曚笅娌℃湁鏄庣粏琛� |
+| `璇ラ噰璐敵璇峰凡鐢熸垚璁㈠崟锛岃鍕块噸澶嶈浆鎹 | 骞跺彂/閲嶅鐐瑰嚮杞鍗� |
+| `杞鍗曞け璐ワ細<鍘熷洜>` | 閲囪喘鍙拌处渚уけ璐ワ紝濡� `杞鍗曞け璐ワ細璇峰厛閰嶇疆閲囪喘瀹℃壒妯℃澘` |
+
+---
+
+## 6. 璇锋眰绀轰緥锛坈url锛�
+
+```bash
+# 鍒楄〃
+curl -X GET "http://localhost:8080/purchase/application/list?status=0&pageNum=1&pageSize=10" \
+ -H "Authorization: Bearer <token>"
+
+# 璇︽儏
+curl -X GET "http://localhost:8080/purchase/application/getById?id=3" \
+ -H "Authorization: Bearer <token>"
+
+# 鏂板
+curl -X POST "http://localhost:8080/purchase/application/add" \
+ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
+ -d '{
+ "supplierId": 12,
+ "projectName": "鍗楅�歑X椤圭洰",
+ "salesContractNo": "XS20260901001",
+ "salesLedgerId": 88,
+ "expectedArrivalDate": "2026-09-30",
+ "contractAmount": 11300.00,
+ "paymentMethod": "鐢垫眹",
+ "remarks": "绱ф��",
+ "productData": [
+ {
+ "productId": 5,
+ "productModelId": 17,
+ "unit": "绫�",
+ "quantity": 100,
+ "taxRate": 13,
+ "taxInclusiveUnitPrice": 113.00,
+ "taxInclusiveTotalPrice": 11300.00,
+ "taxExclusiveTotalPrice": 10000.00,
+ "invoiceType": "澧炲�肩◣涓撶敤鍙戠エ",
+ "isChecked": true
+ }
+ ]
+ }'
+
+# 杞鍗�
+curl -X POST "http://localhost:8080/purchase/application/convertToOrder?id=3" \
+ -H "Authorization: Bearer <token>"
+```
+
+---
+
+## 7. 鍓嶇鏀归�犲缓璁�
+
+1. 鏂板缓 `procurementManagement/purchaseApplication/index.vue`锛岄〉闈㈢粨鏋勫彲鐩存帴娌跨敤鍘熼噰璐鏍搁〉闈紝**鍘绘帀瀹℃牳鐩稿叧鎸夐挳鍜屽鎵圭姸鎬佸垪**銆�
+2. 鍒楄〃椤垫柊澧炪�岀姸鎬併�嶅垪锛宍0` 鏄剧ず銆岃崏绋裤�嶏紝`1` 鏄剧ず銆屽凡鐢熸垚璁㈠崟銆嶏紝寤鸿鐢� tag 鍖哄垎棰滆壊銆�
+3. 鍒楄〃椤垫柊澧炪�岀姸鎬併�嶇瓫閫夐」锛屽�� `0` / `1`锛涙柊澧炪�岀敵璇锋棩鏈熴�嶅尯闂寸瓫閫夐」锛屽搴� `startDate` / `endDate`銆�
+4. 銆岀紪杈戙�嶃�屽垹闄ゃ�嶃�岃浆璁㈠崟銆嶄笁涓寜閽粎鍦� `status === 0` 鏃舵樉绀猴紝杞鍗曞缓璁姞浜屾纭銆�
+5. 杞鍗曟垚鍔熷悗鐢ㄨ繑鍥炵殑 `data`锛堥噰璐彴璐d锛夎烦杞噰璐彴璐﹁鎯咃紝骞跺埛鏂板垪琛ㄣ��
+6. 鏂板/缂栬緫琛ㄥ崟涓殑渚涘簲鍟嗐�侀攢鍞悎鍚屽彿寤鸿澶嶇敤閲囪喘鍙拌处鐨勭幇鏈変笅鎷�/閫夋嫨閫昏緫锛坄/purchase/ledger/getSalesNo`銆乣/purchase/ledger/getPurchaseNo` 绛夛級銆�
+7. 鏄庣粏琛岀殑 `productCategory`锛堜骇鍝佸ぇ绫伙級銆乣specificationModel`锛堣鏍煎瀷鍙凤級涓哄悗绔洖濉瓧娈碉紝琛ㄥ崟閲屽彧鍋氬睍绀猴紝鎻愪氦鏃朵紶 `productId` / `productModelId` 鍗冲彲銆�
+
+---
+
+## 8. 鑱旇皟妫�鏌ユ竻鍗�
+
+1. 鑿滃崟灞曠ず涓恒�岄噰璐敵璇枫�嶏紝鐐瑰嚮鍙繘鍏ユ柊椤甸潰锛屾棤 404銆�
+2. `/list` 涓嶄紶浠讳綍鏉′欢锛氳繑鍥炲叏閮ㄦ暟鎹紝`rows` 鍚� `status`銆乣applicationNo`銆乣supplierName` 绛夊瓧娈点��
+3. `/list?status=0`锛氬彧杩斿洖鑽夌锛沗/list?status=1`锛氬彧杩斿洖宸茬敓鎴愯鍗曘��
+4. `/list?startDate=2026-09-01&endDate=2026-09-30`锛氭寜鐢宠鏃ユ湡杩囨护銆�
+5. `/add` 鍙紶 `supplierId` + `productData`锛氳兘鏂板鎴愬姛锛岃繑鍥� id锛屽垪琛ㄨ兘鐪嬪埌鑽夌锛宍applicationNo` 鑷姩鐢熸垚銆�
+6. `/getById`锛氳繑鍥炲惈 `productData` 鐨勫畬鏁存槑缁嗭紝`productCategory` / `specificationModel` 宸茬敱鍚庣鍥炲~銆�
+7. `/edit` 淇敼鑽夌锛氭槑缁嗗叏閲忔浛鎹㈢敓鏁堬紙鏀规暟閲忋�佸鍒犺閮芥纭級銆�
+8. `/edit` 淇敼 `status=1` 鐨勫崟鎹細杩斿洖銆屽凡鐢熸垚璁㈠崟鐨勯噰璐敵璇蜂笉鍏佽淇敼銆嶃��
+9. `/delete` 鍒犻櫎鑽夌锛氭垚鍔燂紱`/delete` 鍒犻櫎 `status=1`锛氳繑鍥炲甫鍗曞彿鐨勯敊璇彁绀恒��
+10. `/convertToOrder`锛氭垚鍔熷悗鐘舵�佸彉 `1`锛岃繑鍥為噰璐彴璐d锛�**鎵撳紑閲囪喘鍙拌处鍒楄〃鑳芥煡鍒拌閲囪喘鍚堝悓鍙�**銆�
+11. `/convertToOrder` 瀵瑰悓涓� id 鍐嶇偣涓�娆★細杩斿洖銆岃閲囪喘鐢宠宸茬敓鎴愯鍗曪紝璇峰嬁閲嶅杞崲銆嶏紝涓�**涓嶄細**鐢熸垚绗簩鏉¢噰璐彴璐︺��
+12. 杞鍗曞悗鍘汇�屽鎵圭鐞嗐�嶏細鑳界湅鍒颁竴鏉¢噰璐鎵癸紙鏍囬涓恒�岄噰璐悎鍚屽彿 + 瀹℃壒銆嶏級寰呭鐞嗐��
+13. 鏃� `supplierId` 鏃剁偣杞鍗曪細杩斿洖銆岃鍏堥�夋嫨渚涘簲鍟嗗啀杞鍗曘�嶏紱鏃犳槑缁嗘椂杩斿洖銆岄噰璐敵璇锋病鏈変骇鍝佹槑缁嗭紝鏃犳硶杞鍗曘�嶃��
diff --git a/sql/DbTool.java b/sql/DbTool.java
new file mode 100644
index 0000000..668e75c
--- /dev/null
+++ b/sql/DbTool.java
@@ -0,0 +1,193 @@
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * 鏁版嵁搴撶洿杩炲皬宸ュ叿銆傜敤娉曡 printUsage()銆�
+ * 杩愯锛歜ash sql/db.sh "SELECT 1"
+ */
+public class DbTool {
+
+ private static final String DEFAULT_URL =
+ "jdbc:mysql://localhost:3306/product-inventory-management-rbhb"
+ + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"
+ + "&serverTimezone=GMT%2B8&sslMode=DISABLED&allowPublicKeyRetrieval=true";
+ private static final String DEFAULT_USER = "root";
+ private static final String DEFAULT_PASS = "123456";
+
+ public static void main(String[] args) throws Exception {
+ String url = DEFAULT_URL;
+ String user = DEFAULT_USER;
+ String pass = DEFAULT_PASS;
+ String sqlText = null;
+ Path scriptFile = null;
+
+ List<String> positional = new ArrayList<>();
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ switch (arg) {
+ case "-url" -> url = args[++i];
+ case "-user" -> user = args[++i];
+ case "-pass" -> pass = args[++i];
+ case "-f" -> scriptFile = Path.of(args[++i]);
+ case "-h", "--help" -> {
+ printUsage();
+ return;
+ }
+ default -> positional.add(arg);
+ }
+ }
+
+ if (scriptFile != null) {
+ sqlText = Files.readString(scriptFile, StandardCharsets.UTF_8);
+ } else if (!positional.isEmpty()) {
+ sqlText = String.join(" ", positional);
+ } else if (System.console() != null) {
+ printUsage();
+ return;
+ } else {
+ Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8).useDelimiter("\\A");
+ sqlText = scanner.hasNext() ? scanner.next() : null;
+ }
+
+ if (sqlText == null || sqlText.isBlank()) {
+ printUsage();
+ return;
+ }
+
+ List<String> statements = splitStatements(sqlText);
+ if (statements.isEmpty()) {
+ printUsage();
+ return;
+ }
+
+ try (Connection conn = DriverManager.getConnection(url, user, pass)) {
+ for (String sql : statements) {
+ runOne(conn, sql);
+ }
+ }
+ }
+
+ private static void runOne(Connection conn, String sql) throws Exception {
+ long start = System.currentTimeMillis();
+ try (Statement stmt = conn.createStatement()) {
+ boolean hasResultSet = stmt.execute(sql);
+ long cost = System.currentTimeMillis() - start;
+ if (hasResultSet) {
+ try (ResultSet rs = stmt.getResultSet()) {
+ printResultSet(rs);
+ }
+ System.out.println("鑰楁椂 " + cost + " ms");
+ } else {
+ System.out.println("鍙楀奖鍝嶈鏁�: " + stmt.getUpdateCount() + " 鑰楁椂 " + cost + " ms");
+ }
+ } catch (Exception e) {
+ System.out.println("鎵ц澶辫触: " + e.getMessage());
+ System.out.println("SQL: " + sql);
+ throw e;
+ }
+ }
+
+ private static void printResultSet(ResultSet rs) throws Exception {
+ ResultSetMetaData meta = rs.getMetaData();
+ int columnCount = meta.getColumnCount();
+ String[] headers = new String[columnCount];
+ int[] widths = new int[columnCount];
+ for (int i = 0; i < columnCount; i++) {
+ headers[i] = meta.getColumnLabel(i + 1);
+ widths[i] = displayWidth(headers[i]);
+ }
+
+ List<String[]> rows = new ArrayList<>();
+ while (rs.next()) {
+ String[] row = new String[columnCount];
+ for (int i = 0; i < columnCount; i++) {
+ Object value = rs.getObject(i + 1);
+ row[i] = value == null ? "NULL" : String.valueOf(value);
+ widths[i] = Math.max(widths[i], displayWidth(row[i]));
+ }
+ rows.add(row);
+ }
+
+ StringBuilder separator = new StringBuilder();
+ for (int width : widths) {
+ separator.append('-').append("-".repeat(width)).append('-');
+ }
+ System.out.println(separator);
+ System.out.println(joinRow(headers, widths));
+ System.out.println(separator);
+ for (String[] row : rows) {
+ System.out.println(joinRow(row, widths));
+ }
+ System.out.println(separator);
+ System.out.println("鍏� " + rows.size() + " 琛�");
+ }
+
+ private static String joinRow(String[] cells, int[] widths) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < cells.length; i++) {
+ sb.append("| ").append(cells[i]);
+ sb.append(" ".repeat(Math.max(0, widths[i] - displayWidth(cells[i]) + 1)));
+ }
+ return sb.append('|').toString();
+ }
+
+ /**
+ * 鍏ㄨ瀛楃鍗犱袱涓粓绔搴︼紝鎸� 2 璁$畻浠ヤ繚璇佽〃鏍煎榻愩��
+ */
+ private static int displayWidth(String text) {
+ int width = 0;
+ for (int i = 0; i < text.length(); i++) {
+ width += text.charAt(i) > 0x2E80 ? 2 : 1;
+ }
+ return width;
+ }
+
+ /**
+ * 鎸夊垎鍙峰垏鍒嗚鍙ワ紝蹇界暐绌鸿鍙ヤ笌绾敞閲婅銆�
+ * 绠�鍗曞疄鐜帮細涓嶆敮鎸佸瓧绗︿覆瀛楅潰閲忎腑鍖呭惈鍒嗗彿鐨勬瀬绔満鏅��
+ */
+ private static List<String> splitStatements(String sqlText) {
+ List<String> statements = new ArrayList<>();
+ for (String part : sqlText.split(";")) {
+ String trimmed = part.trim();
+ if (trimmed.isEmpty() || isCommentOnly(trimmed)) {
+ continue;
+ }
+ statements.add(trimmed);
+ }
+ return statements;
+ }
+
+ private static boolean isCommentOnly(String sql) {
+ for (String line : sql.split("\\R")) {
+ String stripped = line.trim();
+ if (!stripped.isEmpty() && !stripped.startsWith("--") && !stripped.startsWith("#")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void printUsage() {
+ System.out.println("""
+ 鏁版嵁搴撶洿杩炲伐鍏�
+
+ 鐢ㄦ硶:
+ bash sql/db.sh "SQL璇彞"
+ bash sql/db.sh -f 鑴氭湰.sql
+ echo "SQL璇彞" | bash sql/db.sh
+
+ 鍙鐩栬繛鎺ュ弬鏁�:
+ -url jdbc:mysql://host:3306/db?... -user root -pass 123456
+ """);
+ }
+}
diff --git a/sql/db.sh b/sql/db.sh
new file mode 100644
index 0000000..0dcbf82
--- /dev/null
+++ b/sql/db.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# 鏁版嵁搴撶洿杩炲伐鍏峰寘瑁呰剼鏈紝鑷姩瀹氫綅 MySQL JDBC 椹卞姩骞惰皟鐢� sql/DbTool.java
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+JAR="$(ls "$HOME"/.m2/repository/com/mysql/mysql-connector-j/*/mysql-connector-j-*.jar 2>/dev/null | grep -v sources | tail -1 || true)"
+if [ -z "$JAR" ]; then
+ JAR="$(ls "$HOME"/.m2/repository/mysql/mysql-connector-java/*/mysql-connector-java-*.jar 2>/dev/null | grep -v sources | tail -1 || true)"
+fi
+if [ -z "$JAR" ]; then
+ echo "鏈壘鍒� MySQL JDBC 椹卞姩锛岃纭 ~/.m2 涓嬪瓨鍦� mysql-connector-j" >&2
+ exit 1
+fi
+
+if command -v cygpath >/dev/null 2>&1; then
+ JAR="$(cygpath -m "$JAR")"
+ SOURCE="$(cygpath -m "$SCRIPT_DIR/DbTool.java")"
+else
+ SOURCE="$SCRIPT_DIR/DbTool.java"
+fi
+
+exec java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 --class-path "$JAR" "$SOURCE" "$@"
diff --git a/src/main/java/com/ruoyi/account/controller/PaymentRecordController.java b/src/main/java/com/ruoyi/account/controller/PaymentRecordController.java
new file mode 100644
index 0000000..6e30ae6
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/PaymentRecordController.java
@@ -0,0 +1,82 @@
+package com.ruoyi.account.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.pojo.PaymentRecord;
+import com.ruoyi.account.service.PaymentRecordService;
+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 io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 瀹㈡埛/渚涘簲鍟嗗線鏉ヤ粯娆惧洖娆炬祦姘�
+ *
+ * @author :yys
+ */
+@RestController
+@Tag(name = "寰�鏉ヤ粯娆惧洖娆�")
+@RequestMapping("/paymentRecord")
+@AllArgsConstructor
+public class PaymentRecordController extends BaseController {
+
+ private PaymentRecordService paymentRecordService;
+
+ @GetMapping("/listPage")
+ @Log(title = "寰�鏉ヤ粯娆惧洖娆�-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+ public AjaxResult listPage(Page page, PaymentRecord paymentRecord) {
+ IPage<PaymentRecord> listPage = paymentRecordService.listPage(page, paymentRecord);
+ return AjaxResult.success(listPage);
+ }
+
+ @PostMapping("/add")
+ @Log(title = "寰�鏉ヤ粯娆惧洖娆�-鏂板", businessType = BusinessType.INSERT)
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult add(@RequestBody PaymentRecord paymentRecord) {
+ validate(paymentRecord);
+ boolean save = paymentRecordService.save(paymentRecord);
+ return save ? AjaxResult.success() : AjaxResult.error();
+ }
+
+ @PostMapping("/update")
+ @Log(title = "寰�鏉ヤ粯娆惧洖娆�-淇敼", businessType = BusinessType.UPDATE)
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult update(@RequestBody PaymentRecord paymentRecord) {
+ if (paymentRecord.getId() == null) {
+ return AjaxResult.error("缂哄皯涓婚敭id");
+ }
+ validate(paymentRecord);
+ boolean update = paymentRecordService.updateById(paymentRecord);
+ return update ? AjaxResult.success() : AjaxResult.error();
+ }
+
+ @DeleteMapping("/delete")
+ @Log(title = "寰�鏉ヤ粯娆惧洖娆�-鍒犻櫎", businessType = BusinessType.DELETE)
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult delete(@RequestBody List<Long> ids) {
+ if (CollectionUtils.isEmpty(ids)) {
+ return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+ }
+ boolean delete = paymentRecordService.removeBatchByIds(ids);
+ return delete ? AjaxResult.success() : AjaxResult.error();
+ }
+
+ private void validate(PaymentRecord paymentRecord) {
+ if (paymentRecord.getPartyType() == null || paymentRecord.getPartyId() == null) {
+ throw new RuntimeException("璇烽�夋嫨寰�鏉ユ柟");
+ }
+ if (paymentRecord.getDirection() == null) {
+ throw new RuntimeException("璇烽�夋嫨鏂瑰悜锛堜粯娆�/鍥炴锛�");
+ }
+ if (paymentRecord.getAmount() == null) {
+ throw new RuntimeException("璇峰~鍐欓噾棰�");
+ }
+ }
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/PaymentRecordMapper.java b/src/main/java/com/ruoyi/account/mapper/PaymentRecordMapper.java
new file mode 100644
index 0000000..bc4affb
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/PaymentRecordMapper.java
@@ -0,0 +1,10 @@
+package com.ruoyi.account.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.PaymentRecord;
+
+/**
+ * @author :yys
+ */
+public interface PaymentRecordMapper extends BaseMapper<PaymentRecord> {
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/PaymentRecord.java b/src/main/java/com/ruoyi/account/pojo/PaymentRecord.java
new file mode 100644
index 0000000..a5118ed
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/PaymentRecord.java
@@ -0,0 +1,74 @@
+package com.ruoyi.account.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 com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 瀹㈡埛/渚涘簲鍟嗗線鏉ヤ粯娆惧洖娆炬祦姘�
+ *
+ * @author :yys
+ */
+@Data
+@TableName("payment_record")
+@Schema(name = "PaymentRecord", description = "寰�鏉ヤ粯娆惧洖娆炬祦姘�")
+public class PaymentRecord implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ @Schema(description = "涓婚敭id")
+ private Long id;
+
+ @Schema(description = "寰�鏉ユ柟绫诲瀷 1瀹㈡埛 2渚涘簲鍟�")
+ private Integer partyType;
+
+ @Schema(description = "瀹㈡埛id鎴栦緵搴斿晢id")
+ private Long partyId;
+
+ @Schema(description = "鏂瑰悜 1浠樻 2鍥炴")
+ private Integer direction;
+
+ @Schema(description = "閲戦")
+ private BigDecimal amount;
+
+ @Schema(description = "鍙戠敓鏃ユ湡")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate recordDate;
+
+ @Schema(description = "浠樻/鍥炴鏂瑰紡")
+ private String paymentMethod;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Integer updateUser;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+}
diff --git a/src/main/java/com/ruoyi/account/service/PaymentRecordService.java b/src/main/java/com/ruoyi/account/service/PaymentRecordService.java
new file mode 100644
index 0000000..571d5b4
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/PaymentRecordService.java
@@ -0,0 +1,14 @@
+package com.ruoyi.account.service;
+
+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.pojo.PaymentRecord;
+
+/**
+ * @author :yys
+ */
+public interface PaymentRecordService extends IService<PaymentRecord> {
+
+ IPage<PaymentRecord> listPage(Page page, PaymentRecord req);
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/PaymentRecordServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/PaymentRecordServiceImpl.java
new file mode 100644
index 0000000..601c3ab
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/PaymentRecordServiceImpl.java
@@ -0,0 +1,36 @@
+package com.ruoyi.account.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.mapper.PaymentRecordMapper;
+import com.ruoyi.account.pojo.PaymentRecord;
+import com.ruoyi.account.service.PaymentRecordService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author :yys
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class PaymentRecordServiceImpl extends ServiceImpl<PaymentRecordMapper, PaymentRecord> implements PaymentRecordService {
+
+ private final PaymentRecordMapper paymentRecordMapper;
+
+ @Override
+ public IPage<PaymentRecord> listPage(Page page, PaymentRecord req) {
+ LambdaQueryWrapper<PaymentRecord> wrapper = new LambdaQueryWrapper<PaymentRecord>()
+ .eq(req.getPartyType() != null, PaymentRecord::getPartyType, req.getPartyType())
+ .eq(req.getPartyId() != null, PaymentRecord::getPartyId, req.getPartyId())
+ .eq(req.getDirection() != null, PaymentRecord::getDirection, req.getDirection())
+ .like(StringUtils.isNotBlank(req.getPaymentMethod()), PaymentRecord::getPaymentMethod, req.getPaymentMethod())
+ .orderByDesc(PaymentRecord::getRecordDate)
+ .orderByDesc(PaymentRecord::getId);
+ return paymentRecordMapper.selectPage(page, wrapper);
+ }
+}
diff --git a/src/main/java/com/ruoyi/basic/pojo/OilDepot.java b/src/main/java/com/ruoyi/basic/pojo/OilDepot.java
index 2afbe20..5538807 100644
--- a/src/main/java/com/ruoyi/basic/pojo/OilDepot.java
+++ b/src/main/java/com/ruoyi/basic/pojo/OilDepot.java
@@ -70,12 +70,13 @@
/**
* 鏇存柊鏃堕棿
*/
- @TableField(fill = FieldFill.INSERT)
+ @TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 鏇存柊浜�
*/
+ @TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
/**
diff --git a/src/main/java/com/ruoyi/basic/pojo/TankInfo.java b/src/main/java/com/ruoyi/basic/pojo/TankInfo.java
index 86dd410..9933bc4 100644
--- a/src/main/java/com/ruoyi/basic/pojo/TankInfo.java
+++ b/src/main/java/com/ruoyi/basic/pojo/TankInfo.java
@@ -73,12 +73,13 @@
/**
* 鏇存柊鏃堕棿
*/
- @TableField(fill = FieldFill.INSERT)
+ @TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 鏇存柊浜�
*/
+ @TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
/**
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java
index 5e59117..effb42a 100644
--- a/src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java
@@ -7,6 +7,7 @@
import com.ruoyi.collaborativeApproval.pojo.RulesRegulationsManagement;
import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement;
import com.ruoyi.collaborativeApproval.service.RulesRegulationsManagementService;
+import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -17,6 +18,7 @@
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
+import java.time.LocalDateTime;
import java.util.List;
@RestController
@@ -43,6 +45,9 @@
@PostMapping("/update")
@Operation(summary = "淇敼")
public AjaxResult update(@RequestBody RulesRegulationsManagement rulesRegulationsManagement){
+ // 鍓嶇鍥炰紶鏁磋浼氬甫涓婃棫鐨� updateTime/updateUser锛屽鑷磋嚜鍔ㄥ~鍏呰璺宠繃锛岄渶鏄惧紡鍚屾涓哄綋鍓嶆搷浣滀汉涓庢椂闂�
+ rulesRegulationsManagement.setUpdateTime(LocalDateTime.now());
+ rulesRegulationsManagement.setUpdateUser(SecurityUtils.getUserId().intValue());
return AjaxResult.success(rulesRegulationsManagementService.updateById(rulesRegulationsManagement));
}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java
index baa8d02..5390854 100644
--- a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java
@@ -173,28 +173,7 @@
//鍙備細浜哄憳id(浜哄憳鍙拌处鍜岀敤鎴疯〃閫氳繃浜哄憳缂栧彿鍜岀敤鎴峰悕绉�(鐧诲綍璐﹀彿)鍋氬尮閰�)
if (Integer.valueOf(1).equals(meetApplication.getPublishStatus())) {
MeetApplication application = meetApplicationMapper.selectById(meetApplication.getId());
-
- String cleanStr = application.getParticipants()
- .replaceAll("^\\[|\\]$", "")
- .trim();
-
- List<Long> userIds = Arrays.stream(cleanStr.split(","))
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .map(Long::valueOf)
- .map(staffId -> {
- StaffOnJob record = staffOnJobMapper.selectById(staffId);
- if (record == null) {
- return null;
- }
- return userMapper.selectOne(
- Wrappers.<SysUser>lambdaQuery()
- .eq(SysUser::getUserName, record.getStaffNo())
- );
- })
- .filter(Objects::nonNull)
- .map(SysUser::getUserId)
- .collect(Collectors.toList());
+ List<Long> userIds = resolveParticipantUserIds(application.getParticipants());
if (!userIds.isEmpty()) {
sysNoticeService.simpleNoticeByUser(
@@ -211,6 +190,44 @@
return R.ok();
}
+ // 鍙備細浜哄憳鍙拌处id -> 鐧诲綍鐢ㄦ埛id锛涘悓涓�鐧诲綍璐﹀彿鍦� sys_user 瀛樺湪澶氭潯鍘嗗彶璁板綍鏃讹紝鎸夋湭鍒犻櫎浼樺厛鍙栦竴鏉★紝閬垮厤 selectOne 鎶� TooManyResultsException
+ private List<Long> resolveParticipantUserIds(String participants) {
+ if (StrUtil.isBlank(participants)) {
+ return Collections.emptyList();
+ }
+ String cleanStr = participants.replaceAll("^\\[|\\]$", "").trim();
+ if (cleanStr.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<Long> staffIds = Arrays.stream(cleanStr.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .map(Long::valueOf)
+ .collect(Collectors.toList());
+ if (staffIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<String> loginNames = staffOnJobMapper.selectBatchIds(staffIds).stream()
+ .map(StaffOnJob::getStaffNo)
+ .filter(StrUtil::isNotBlank)
+ .distinct()
+ .collect(Collectors.toList());
+ if (loginNames.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return userMapper.selectList(Wrappers.<SysUser>lambdaQuery()
+ .in(SysUser::getUserName, loginNames)
+ .orderByAsc(SysUser::getDelFlag)
+ .orderByAsc(SysUser::getUserId))
+ .stream()
+ .collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId,
+ (first, ignored) -> first, LinkedHashMap::new))
+ .values()
+ .stream()
+ .distinct()
+ .collect(Collectors.toList());
+ }
+
@Override
public IPage<MeetApplication> getMeetingApplicationList(SearchMeetingApplicationVo vo) {
LambdaQueryWrapper<MeetApplication> queryWrapper = new LambdaQueryWrapper<>();
diff --git a/src/main/java/com/ruoyi/common/enums/PurchaseApplicationStatusEnum.java b/src/main/java/com/ruoyi/common/enums/PurchaseApplicationStatusEnum.java
new file mode 100644
index 0000000..3cf5467
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/PurchaseApplicationStatusEnum.java
@@ -0,0 +1,40 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 閲囪喘鐢宠鐘舵�佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum PurchaseApplicationStatusEnum implements BaseEnum<Integer> {
+
+ DRAFT(0, "鑽夌"),
+ ORDER_GENERATED(1, "宸茬敓鎴愯鍗�");
+
+ private final Integer value;
+ private final String label;
+
+ @Override
+ public Integer getCode() {
+ return value;
+ }
+
+ @Override
+ public String getValue() {
+ return label;
+ }
+
+ public static String getLabelByValue(Integer value) {
+ if (value == null) {
+ return "鏈煡鐘舵��";
+ }
+ for (PurchaseApplicationStatusEnum status : values()) {
+ if (status.getCode().equals(value)) {
+ return status.getValue();
+ }
+ }
+ return "鏈煡鐘舵��";
+ }
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
index fbf03ad..a2f613f 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -18,7 +18,9 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
+import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord;
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
+import com.ruoyi.measuringinstrumentledger.service.SparePartsRecordService;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@@ -38,6 +40,7 @@
private final DeviceMaintenanceMapper deviceMaintenanceMapper;
private final SparePartsMapper sparePartsMapper;
private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
+ private final SparePartsRecordService sparePartsRecordService;
private final FileUtil fileUtil;
@Override
@@ -84,6 +87,15 @@
record.setSparePartsId(sparePartUse.getId());
record.setQuantity(sparePartUse.getQuantity());
sparePartsRequisitionRecordService.save(record);
+
+ // 杩藉姞澶囦欢鍑哄簱娴佹按锛�3 淇濆吇棰嗙敤锛�
+ SparePartsRecord outRecord = new SparePartsRecord();
+ outRecord.setSparePartsId(sparePartUse.getId());
+ outRecord.setDirection(2);
+ outRecord.setQuantity(new BigDecimal(sparePartUse.getQuantity()));
+ outRecord.setSourceType(3);
+ outRecord.setSourceId(deviceMaintenance.getId());
+ sparePartsRecordService.save(outRecord);
} else {
return AjaxResult.error("澶囦欢 " + spareParts.getName() + " 鏁伴噺涓嶈冻");
}
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
index e5a73ac..a74e327 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
@@ -21,7 +21,9 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
+import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord;
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
+import com.ruoyi.measuringinstrumentledger.service.SparePartsRecordService;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@@ -45,6 +47,7 @@
private final IDeviceLedgerService deviceLedgerService;
private final SparePartsMapper sparePartsMapper;
private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
+ private final SparePartsRecordService sparePartsRecordService;
private final FileUtil fileUtil;
private static final int STATUS_PENDING_REPAIR = 0;
@@ -114,6 +117,15 @@
record.setSparePartsId(sparePartUse.getId());
record.setQuantity(sparePartUse.getQuantity());
sparePartsRequisitionRecordService.save(record);
+
+ // 杩藉姞澶囦欢鍑哄簱娴佹按锛�2 缁翠慨棰嗙敤锛�
+ SparePartsRecord outRecord = new SparePartsRecord();
+ outRecord.setSparePartsId(sparePartUse.getId());
+ outRecord.setDirection(2);
+ outRecord.setQuantity(new BigDecimal(sparePartUse.getQuantity()));
+ outRecord.setSourceType(2);
+ outRecord.setSourceId(deviceRepairDto.getId());
+ sparePartsRecordService.save(outRecord);
} else {
return AjaxResult.error("澶囦欢 " + spareParts.getName() + " 鏁伴噺涓嶈冻");
}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRecordController.java b/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRecordController.java
new file mode 100644
index 0000000..4dd78be
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRecordController.java
@@ -0,0 +1,49 @@
+package com.ruoyi.measuringinstrumentledger.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.measuringinstrumentledger.pojo.SparePartsRecord;
+import com.ruoyi.measuringinstrumentledger.service.SparePartsRecordService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 澶囦欢鍑哄叆搴撴祦姘�
+ *
+ * @author :yys
+ */
+@RestController
+@Tag(name = "澶囦欢鍑哄叆搴撴祦姘�")
+@RequestMapping("/sparePartsRecord")
+@AllArgsConstructor
+public class SparePartsRecordController extends BaseController {
+
+ private SparePartsRecordService sparePartsRecordService;
+
+ @GetMapping("/listPage")
+ @Log(title = "澶囦欢鍑哄叆搴撴祦姘�-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+ public AjaxResult listPage(Page page, SparePartsRecord sparePartsRecord) {
+ IPage<SparePartsRecord> listPage = sparePartsRecordService.listPage(page, sparePartsRecord);
+ return AjaxResult.success(listPage);
+ }
+
+ @DeleteMapping("/delete")
+ @Log(title = "澶囦欢鍑哄叆搴撴祦姘�-鍒犻櫎", businessType = BusinessType.DELETE)
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult delete(@RequestBody List<Long> ids) {
+ if (CollectionUtils.isEmpty(ids)) {
+ return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+ }
+ boolean delete = sparePartsRecordService.removeBatchByIds(ids);
+ return delete ? AjaxResult.success() : AjaxResult.error();
+ }
+}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/mapper/SparePartsRecordMapper.java b/src/main/java/com/ruoyi/measuringinstrumentledger/mapper/SparePartsRecordMapper.java
new file mode 100644
index 0000000..5547489
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/mapper/SparePartsRecordMapper.java
@@ -0,0 +1,15 @@
+package com.ruoyi.measuringinstrumentledger.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.measuringinstrumentledger.pojo.SparePartsRecord;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author :yys
+ */
+public interface SparePartsRecordMapper extends BaseMapper<SparePartsRecord> {
+
+ IPage<SparePartsRecord> listPage(Page page, @Param("req") SparePartsRecord req);
+}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SpareParts.java b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SpareParts.java
index 13a9229..0c7b7b6 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SpareParts.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SpareParts.java
@@ -46,6 +46,14 @@
*/
private Long parentId;
/**
+ * 鍏宠仈浜у搧澶х被id
+ */
+ private Long productId;
+ /**
+ * 鍏宠仈浜у搧瑙勬牸id锛堥噰璐彴璐︽槑缁嗘寜姝ゅ尮閰嶅浠讹級
+ */
+ private Long productModelId;
+ /**
* 澶囦欢鐘舵��
*/
private String status;
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SparePartsRecord.java b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SparePartsRecord.java
new file mode 100644
index 0000000..4bbf55f
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/SparePartsRecord.java
@@ -0,0 +1,80 @@
+package com.ruoyi.measuringinstrumentledger.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 澶囦欢鍑哄叆搴撴祦姘�
+ *
+ * @author :yys
+ */
+@Getter
+@Setter
+@TableName("spare_parts_record")
+@Schema(name = "SparePartsRecord瀵硅薄", description = "澶囦欢鍑哄叆搴撴祦姘�")
+public class SparePartsRecord implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description = "澶囦欢id")
+ private Long sparePartsId;
+
+ @Schema(description = "鏂瑰悜 1鍏ュ簱 2鍑哄簱")
+ private Integer direction;
+
+ @Schema(description = "鏁伴噺")
+ private BigDecimal quantity;
+
+ @Schema(description = "鏉ユ簮绫诲瀷 1閲囪喘鍏ュ簱 2缁翠慨棰嗙敤 3淇濆吇棰嗙敤")
+ private Integer sourceType;
+
+ @Schema(description = "鏉ユ簮鍗曟嵁id")
+ private Long sourceId;
+
+ @Schema(description = "鏉ユ簮鍗曞彿")
+ private String sourceNo;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "澶囦欢鍚嶇О")
+ @TableField(exist = false)
+ private String sparePartsName;
+
+ @Schema(description = "绉熸埛id")
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @Schema(description = "鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @Schema(description = "褰曞叆鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createTime;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updateTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Integer updateUser;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRecordService.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRecordService.java
new file mode 100644
index 0000000..3b2747a
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRecordService.java
@@ -0,0 +1,14 @@
+package com.ruoyi.measuringinstrumentledger.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord;
+
+/**
+ * @author :yys
+ */
+public interface SparePartsRecordService extends IService<SparePartsRecord> {
+
+ IPage<SparePartsRecord> listPage(Page page, SparePartsRecord req);
+}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRecordServiceImpl.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRecordServiceImpl.java
new file mode 100644
index 0000000..55a73ba
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRecordServiceImpl.java
@@ -0,0 +1,27 @@
+package com.ruoyi.measuringinstrumentledger.service.impl;
+
+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.measuringinstrumentledger.mapper.SparePartsRecordMapper;
+import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord;
+import com.ruoyi.measuringinstrumentledger.service.SparePartsRecordService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author :yys
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SparePartsRecordServiceImpl extends ServiceImpl<SparePartsRecordMapper, SparePartsRecord> implements SparePartsRecordService {
+
+ private final SparePartsRecordMapper sparePartsRecordMapper;
+
+ @Override
+ public IPage<SparePartsRecord> listPage(Page page, SparePartsRecord req) {
+ return sparePartsRecordMapper.listPage(page, req);
+ }
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
index aa88d45..f868bc8 100644
--- a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementDto.java
@@ -16,6 +16,11 @@
private String purchaseContractNumber;
+ /**
+ * 閲囪喘鍙拌处id锛屾柊澧炲叆搴撻�夎鍗曞悗绮剧‘鍙嶆樉鏄庣粏鐢紙浼樹簬鎸夊悎鍚屽彿妯$硦鍖归厤锛�
+ */
+ private Integer purchaseLedgerId;
+
private Integer id;
private Integer recordId;
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
index 09272c4..8345ca0 100644
--- a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutAdd.java
@@ -26,4 +26,9 @@
private Long productModelId;// 鍩虹浜у搧ID
+ /**
+ * 鍑哄簱鎵规锛岄攢鍞嚭搴撳繀濉紙鎵e簱瀛樻寜鎵规鎵o級
+ */
+ private String batchNo;
+
}
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
index a5dd8d6..731af6f 100644
--- a/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/dto/ProcurementRecordOutPageDto.java
@@ -18,6 +18,17 @@
@Excel(name = "鍑哄簱缂栧彿")
private String code;
+ /**
+ * 鍑哄簱鎵规
+ */
+ private String batchNo;
+
+ /**
+ * 鑷姩鐢熸垚鐨勫彂璐у崟鍙凤紙閿�鍞嚭搴撴墠绠楀彂璐э級
+ */
+ @Excel(name = "鍙戣揣鍗曞彿")
+ private String shippingNo;
+
private Integer id;
private BigDecimal warnNum;
diff --git a/src/main/java/com/ruoyi/procurementrecord/bean/vo/BatchNoOptionVo.java b/src/main/java/com/ruoyi/procurementrecord/bean/vo/BatchNoOptionVo.java
new file mode 100644
index 0000000..71b16d2
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/bean/vo/BatchNoOptionVo.java
@@ -0,0 +1,25 @@
+package com.ruoyi.procurementrecord.bean.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 鍑哄簱鍙�夋壒娆★細鎵规鍙� + 褰撳墠鍙敤閲�
+ *
+ * @author :yys
+ */
+@Data
+@Schema(name = "BatchNoOptionVo", description = "鍑哄簱鍙�夋壒娆�")
+public class BatchNoOptionVo {
+
+ @Schema(description = "瑙勬牸鍨嬪彿id")
+ private Long productModelId;
+
+ @Schema(description = "鎵瑰彿")
+ private String batchNo;
+
+ @Schema(description = "鍙敤閲忥紙搴撳瓨閲� - 閿佸畾鏁伴噺锛�")
+ private BigDecimal availableQuantity;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
index c86d3e6..1188385 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
@@ -45,6 +45,15 @@
return AjaxResult.success(result);
}
+ /**
+ * 閿�鍞嚭搴撻�夋壒娆★細鎸夎鏍煎瀷鍙锋煡鍙�夋壒娆′笌鍙敤閲�
+ */
+ @GetMapping("/batchNoList")
+ @Log(title = "閲囪喘鍑哄簱-鍑哄簱绠$悊-鎵规鍒楄〃", businessType = BusinessType.OTHER)
+ public AjaxResult batchNoList(@RequestParam("productModelId") Long productModelId) {
+ return AjaxResult.success(procurementRecordOutService.listBatchNoByProductModelId(productModelId));
+ }
+
@GetMapping("/listPageByProduct")
@Log(title = "鐢熶骇鍑哄簱-鍑哄簱鍙拌处-鍑哄簱鏌ヨ", businessType = BusinessType.OTHER)
public AjaxResult listPageByProduct(Page page, ProcurementRecordOutPageDto procurementDto) {
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
index a1e003e..72e7bb8 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
+import com.ruoyi.procurementrecord.bean.vo.BatchNoOptionVo;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import org.apache.ibatis.annotations.Param;
@@ -33,4 +34,9 @@
ProcurementRecordOut selectCode(@Param("format") String format);
IPage<ProcurementRecordOutPageDto> listPageBySemiProduct(Page page,@Param("req") ProcurementRecordOutPageDto procurementDto);
+
+ /**
+ * 鏌ヨ鏌愯鏍煎瀷鍙蜂笅鍙�夋壒娆″強鍙敤閲忥紙閿�鍞嚭搴撻�夋壒娆$敤锛�
+ */
+ List<BatchNoOptionVo> listBatchNoByProductModelId(@Param("productModelId") Long productModelId);
}
diff --git a/src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java b/src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java
index e6f0798..343d619 100644
--- a/src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java
+++ b/src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java
@@ -92,6 +92,16 @@
*/
private Long productModelId;
+ /**
+ * 鍑哄簱鎵规锛堟墸搴撳瓨鐢級
+ */
+ private String batchNo;
+
+ /**
+ * 閿�鍞嚭搴撴椂鑷姩鐢熸垚鐨勫彂璐у彴璐d
+ */
+ private Long shippingInfoId;
+
@TableField(fill = FieldFill.INSERT)
private Long deptId;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
index 803c03f..f57c1c5 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
@@ -6,9 +6,11 @@
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.bean.vo.BatchNoOptionVo;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
/**
* @author :yys
@@ -29,4 +31,9 @@
IPage<ProcurementRecordOutPageDto> listPageByCustom(Page page, ProcurementRecordOutPageDto procurementDto);
IPage<ProcurementRecordOutPageDto> listPageBySemiProduct(Page page, ProcurementRecordOutPageDto procurementDto);
+
+ /**
+ * 閿�鍞嚭搴撻�夋壒娆★細杩斿洖璇ヨ鏍煎瀷鍙蜂笅鐨勫彲閫夋壒娆′笌鍙敤閲�
+ */
+ List<BatchNoOptionVo> listBatchNoByProductModelId(Long productModelId);
}
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 8b73f4a..d75db6c 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
@@ -8,15 +8,22 @@
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.bean.vo.BatchNoOptionVo;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.sales.dto.ShippingInfoDto;
+import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.ShippingProductDetail;
+import com.ruoyi.sales.service.ShippingInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import jakarta.servlet.http.HttpServletResponse;
@@ -25,6 +32,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -37,11 +45,26 @@
@Slf4j
public class ProcurementRecordOutServiceImpl extends ServiceImpl<ProcurementRecordOutMapper, ProcurementRecordOut> implements ProcurementRecordOutService {
+ /**
+ * 鍑哄簱绫诲瀷锛氶攢鍞嚭搴撱�傛补鍝侀攢鍞嚭搴撳嵆鍙戣揣
+ */
+ private static final Integer TYPE_SALE_OUT = 2;
+
+ /**
+ * 閿�鍞骇鍝佹槑缁嗙被鍨嬶細1閿�鍞紙sales_ledger_product.type锛屼笌鍑哄簱 type 璇箟涓嶅悓锛�
+ */
+ private static final Integer LEDGER_PRODUCT_TYPE_SALE = 1;
+
public final ProcurementRecordOutMapper procurementRecordOutMapper;
public final SysUserMapper sysUserMapper;
+ public final ShippingInfoService shippingInfoService;
+
+ public final SalesLedgerProductMapper salesLedgerProductMapper;
+
@Override
+ @Transactional(rollbackFor = Exception.class)
public int stockout(ProcurementRecordOutAdd procurementRecordOutAdd) {
SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(procurementRecordOutAdd.getUserId().toString()));
if(sysUser == null){
@@ -79,9 +102,40 @@
.createBy(sysUser.getNickName())
.updateUser(Long.valueOf(procurementRecordOutAdd.getUserId()))
.updateTime(LocalDateTime.now())
- .productModelId(procurementRecordOutAdd.getProductModelId());
- this.save(procurementRecordOut.build());
+ .productModelId(procurementRecordOutAdd.getProductModelId())
+ .batchNo(procurementRecordOutAdd.getBatchNo());
+ ProcurementRecordOut record = procurementRecordOut.build();
+ this.save(record);
+
+ // 娌瑰搧閿�鍞嚭搴撳嵆鍙戣揣锛氳嚜鍔ㄧ敓鎴愪竴鏉″凡鍙戣揣鐨勫彂璐у彴璐﹀苟鍥炲啓鍏宠仈
+ if (TYPE_SALE_OUT.equals(record.getType())) {
+ record.setShippingInfoId(createShippingForOutbound(record));
+ this.updateById(record);
+ }
return 0;
+ }
+
+ /**
+ * 鎸夊嚭搴撹褰曠敓鎴愬彂璐у彴璐︼細鎵规蹇呭~锛屽簱瀛樼敱鍙戣揣渚ф墸鍑�
+ */
+ private Long createShippingForOutbound(ProcurementRecordOut record) {
+ if (StringUtils.isBlank(record.getBatchNo())) {
+ throw new RuntimeException("璇烽�夋嫨鍑哄簱鎵规");
+ }
+ SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(record.getSalesLedgerProductId());
+ if (salesLedgerProduct == null || !LEDGER_PRODUCT_TYPE_SALE.equals(salesLedgerProduct.getType())) {
+ throw new RuntimeException("閿�鍞嚭搴撴湭鍏宠仈鍒版湁鏁堢殑閿�鍞骇鍝佹槑缁�");
+ }
+ ShippingProductDetail detail = new ShippingProductDetail();
+ detail.setProductModelId(record.getProductModelId());
+ detail.setQuantity(record.getInboundNum());
+ detail.setBatchNo(record.getBatchNo());
+
+ ShippingInfoDto shippingInfoDto = new ShippingInfoDto();
+ shippingInfoDto.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
+ shippingInfoDto.setSalesLedgerProductId(record.getSalesLedgerProductId());
+ shippingInfoDto.setBatchNoDetailList(Collections.singletonList(detail));
+ return shippingInfoService.addShippedFromOutbound(shippingInfoDto);
}
@Override
@@ -94,10 +148,17 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public int deletePro(ProcurementUpdateDto procurementDto) {
List<ProcurementRecordOut> procurementRecordOutByIds = getProcurementRecordOutByIds(procurementDto.getIds());
if(CollectionUtils.isEmpty(procurementRecordOutByIds)){
throw new RuntimeException("鏈煡璇㈠埌閫変腑鐨勪汉");
+ }
+ // 閿�鍞嚭搴撳凡鐢熸垚鍙戣揣鍗曪紝鍒犻櫎鍑哄簱鍗曞墠鍏堝啿閿�鍙戣揣鍗曞苟褰掕繕宸叉墸搴撳瓨
+ for (ProcurementRecordOut record : procurementRecordOutByIds) {
+ if (record.getShippingInfoId() != null) {
+ shippingInfoService.cancelByOutbound(record.getShippingInfoId());
+ }
}
procurementRecordOutMapper.deleteBatchIds(procurementRecordOutByIds.stream().map(ProcurementRecordOut::getId).collect(Collectors.toList()));
return 0;
@@ -124,4 +185,12 @@
public IPage<ProcurementRecordOutPageDto> listPageBySemiProduct(Page page, ProcurementRecordOutPageDto procurementDto) {
return procurementRecordOutMapper.listPageBySemiProduct(page, procurementDto);
}
+
+ @Override
+ public List<BatchNoOptionVo> listBatchNoByProductModelId(Long productModelId) {
+ if (productModelId == null) {
+ return Collections.emptyList();
+ }
+ return procurementRecordOutMapper.listBatchNoByProductModelId(productModelId);
+ }
}
diff --git a/src/main/java/com/ruoyi/purchase/controller/PurchaseApplicationController.java b/src/main/java/com/ruoyi/purchase/controller/PurchaseApplicationController.java
new file mode 100644
index 0000000..7be98a4
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/controller/PurchaseApplicationController.java
@@ -0,0 +1,90 @@
+package com.ruoyi.purchase.controller;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.framework.web.page.TableDataInfo;
+import com.ruoyi.purchase.dto.PurchaseApplicationDto;
+import com.ruoyi.purchase.pojo.PurchaseApplication;
+import com.ruoyi.purchase.service.PurchaseApplicationService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 閲囪喘鐢宠鍗� Controller
+ */
+@RestController
+@RequestMapping("/purchase/application")
+@AllArgsConstructor
+@Tag(name = "閲囪喘鐢宠")
+public class PurchaseApplicationController extends BaseController {
+
+ private final PurchaseApplicationService purchaseApplicationService;
+
+ /**
+ * 鏌ヨ閲囪喘鐢宠鍒楄〃
+ */
+ @GetMapping("/list")
+ public TableDataInfo list(PurchaseApplicationDto purchaseApplicationDto) {
+ startPage();
+ List<PurchaseApplication> list = purchaseApplicationService.selectApplicationList(purchaseApplicationDto);
+ return getDataTable(list);
+ }
+
+ /**
+ * 鏌ヨ閲囪喘鐢宠璇︽儏锛堝惈浜у搧鏄庣粏锛�
+ */
+ @GetMapping("/getById")
+ public AjaxResult getById(@RequestParam Long id) {
+ return AjaxResult.success(purchaseApplicationService.getDetail(id));
+ }
+
+ /**
+ * 鏂板閲囪喘鐢宠锛堣崏绋匡級
+ */
+ @Log(title = "閲囪喘鐢宠", businessType = BusinessType.INSERT)
+ @PostMapping("/add")
+ public AjaxResult add(@RequestBody PurchaseApplicationDto purchaseApplicationDto) {
+ return AjaxResult.success(purchaseApplicationService.saveApplication(purchaseApplicationDto));
+ }
+
+ /**
+ * 淇敼閲囪喘鐢宠锛屼粎鑽夌鐘舵�佸厑璁镐慨鏀�
+ */
+ @Log(title = "閲囪喘鐢宠", businessType = BusinessType.UPDATE)
+ @PostMapping("/edit")
+ public AjaxResult edit(@RequestBody PurchaseApplicationDto purchaseApplicationDto) {
+ return toAjax(purchaseApplicationService.updateApplication(purchaseApplicationDto));
+ }
+
+ /**
+ * 鍒犻櫎閲囪喘鐢宠锛屼粎鑽夌鐘舵�佸厑璁稿垹闄�
+ */
+ @Log(title = "閲囪喘鐢宠", businessType = BusinessType.DELETE)
+ @DeleteMapping("/delete")
+ public AjaxResult delete(@RequestBody Long[] ids) {
+ return toAjax(purchaseApplicationService.deleteApplications(
+ ids == null ? List.of() : List.of(ids)));
+ }
+
+ /**
+ * 閲囪喘鐢宠杞鍗曪細鐢熸垚閲囪喘鍙拌处骞舵彁浜� OA 閲囪喘瀹℃壒
+ */
+ @Operation(summary = "閲囪喘鐢宠杞鍗�")
+ @Log(title = "閲囪喘鐢宠杞鍗�", businessType = BusinessType.OTHER)
+ @PostMapping("/convertToOrder")
+ public AjaxResult convertToOrder(@RequestParam Long id) {
+ return AjaxResult.success(purchaseApplicationService.convertToOrder(id));
+ }
+}
diff --git a/src/main/java/com/ruoyi/purchase/dto/PurchaseApplicationDto.java b/src/main/java/com/ruoyi/purchase/dto/PurchaseApplicationDto.java
new file mode 100644
index 0000000..e8aec0e
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/dto/PurchaseApplicationDto.java
@@ -0,0 +1,79 @@
+package com.ruoyi.purchase.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.purchase.pojo.PurchaseApplicationProduct;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 閲囪喘鐢宠鍗� DTO
+ */
+@Data
+public class PurchaseApplicationDto {
+
+ @Schema(description = "鐢宠鍗曚富閿�")
+ private Long id;
+
+ @Schema(description = "鐢宠鍗曞彿")
+ private String applicationNo;
+
+ @Schema(description = "渚涘簲鍟唅d")
+ private Long supplierId;
+
+ @Schema(description = "渚涘簲鍟嗗悕绉�")
+ private String supplierName;
+
+ @Schema(description = "椤圭洰鍚嶇О")
+ private String projectName;
+
+ @Schema(description = "閿�鍞悎鍚屽彿")
+ private String salesContractNo;
+
+ @Schema(description = "鍏宠仈閿�鍞彴璐︿富琛ㄤ富閿�")
+ private Long salesLedgerId;
+
+ @Schema(description = "鐢宠浜篿d")
+ private Long applicantId;
+
+ @Schema(description = "鐢宠浜哄鍚�")
+ private String applicantName;
+
+ @Schema(description = "鐢宠鏃ユ湡")
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate applyDate;
+
+ @Schema(description = "鏈熸湜鍒拌揣鏃ユ湡")
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate expectedArrivalDate;
+
+ @Schema(description = "鐢宠閲戦锛堝惈绋庢�讳环锛�")
+ private BigDecimal contractAmount;
+
+ @Schema(description = "浠樻鏂瑰紡")
+ private String paymentMethod;
+
+ @Schema(description = "澶囨敞")
+ private String remarks;
+
+ @Schema(description = "鐘舵�� 0鑽夌 1宸茬敓鎴愯鍗�")
+ private Integer status;
+
+ @Schema(description = "杞鍗曞悗鐢熸垚鐨勯噰璐彴璐d")
+ private Long purchaseLedgerId;
+
+ @Schema(description = "鐢宠鏃ユ湡鏌ヨ寮�濮嬶紙yyyy-MM-dd锛�")
+ private String startDate;
+
+ @Schema(description = "鐢宠鏃ユ湡鏌ヨ缁撴潫锛坹yyy-MM-dd锛�")
+ private String endDate;
+
+ @Schema(description = "閲囪喘鐢宠浜у搧鏄庣粏")
+ private List<PurchaseApplicationProduct> productData;
+}
diff --git a/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationMapper.java b/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationMapper.java
new file mode 100644
index 0000000..82de43b
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.purchase.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.purchase.pojo.PurchaseApplication;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 閲囪喘鐢宠鍗� Mapper 鎺ュ彛
+ */
+@Mapper
+public interface PurchaseApplicationMapper extends BaseMapper<PurchaseApplication> {
+}
diff --git a/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationProductMapper.java b/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationProductMapper.java
new file mode 100644
index 0000000..6fffc1a
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/mapper/PurchaseApplicationProductMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.purchase.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.purchase.pojo.PurchaseApplicationProduct;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 閲囪喘鐢宠浜у搧鏄庣粏 Mapper 鎺ュ彛
+ */
+@Mapper
+public interface PurchaseApplicationProductMapper extends BaseMapper<PurchaseApplicationProduct> {
+}
diff --git a/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplication.java b/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplication.java
new file mode 100644
index 0000000..064cc0b
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplication.java
@@ -0,0 +1,107 @@
+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 com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 閲囪喘鐢宠鍗曞璞� purchase_application
+ */
+@Getter
+@Setter
+@TableName("purchase_application")
+@Schema(name = "PurchaseApplication瀵硅薄", description = "閲囪喘鐢宠鍗�")
+public class PurchaseApplication implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description = "鐢宠鍗曞彿")
+ private String applicationNo;
+
+ @Schema(description = "渚涘簲鍟唅d")
+ private Long supplierId;
+
+ @Schema(description = "渚涘簲鍟嗗悕绉�")
+ private String supplierName;
+
+ @Schema(description = "椤圭洰鍚嶇О")
+ private String projectName;
+
+ @Schema(description = "閿�鍞悎鍚屽彿")
+ private String salesContractNo;
+
+ @Schema(description = "鍏宠仈閿�鍞彴璐︿富琛ㄤ富閿�")
+ private Long salesLedgerId;
+
+ @Schema(description = "鐢宠浜篿d")
+ private Long applicantId;
+
+ @Schema(description = "鐢宠浜哄鍚�")
+ private String applicantName;
+
+ @Schema(description = "鐢宠鏃ユ湡")
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate applyDate;
+
+ @Schema(description = "鏈熸湜鍒拌揣鏃ユ湡")
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate expectedArrivalDate;
+
+ @Schema(description = "鐢宠閲戦锛堝惈绋庢�讳环锛�")
+ private BigDecimal contractAmount;
+
+ @Schema(description = "浠樻鏂瑰紡")
+ private String paymentMethod;
+
+ @Schema(description = "澶囨敞")
+ private String remarks;
+
+ @Schema(description = "鐘舵�� 0鑽夌 1宸茬敓鎴愯鍗�")
+ private Integer status;
+
+ @Schema(description = "杞鍗曞悗鐢熸垚鐨勯噰璐彴璐d")
+ private Long purchaseLedgerId;
+
+ @Schema(description = "杞鍗曟椂闂�")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime convertTime;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @TableField(fill = FieldFill.INSERT)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updateTime;
+
+ @TableField(exist = false)
+ @Schema(description = "閲囪喘鐢宠浜у搧鏄庣粏")
+ private List<PurchaseApplicationProduct> productData;
+}
diff --git a/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplicationProduct.java b/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplicationProduct.java
new file mode 100644
index 0000000..e79cf05
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/pojo/PurchaseApplicationProduct.java
@@ -0,0 +1,89 @@
+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 com.fasterxml.jackson.annotation.JsonFormat;
+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;
+
+/**
+ * 閲囪喘鐢宠浜у搧鏄庣粏瀵硅薄 purchase_application_product
+ */
+@Getter
+@Setter
+@TableName("purchase_application_product")
+@Schema(name = "PurchaseApplicationProduct瀵硅薄", description = "閲囪喘鐢宠浜у搧鏄庣粏")
+public class PurchaseApplicationProduct implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description = "閲囪喘鐢宠鍗昳d")
+ private Long purchaseApplicationId;
+
+ @Schema(description = "浜у搧id")
+ private Long productId;
+
+ @Schema(description = "浜у搧瑙勬牸id")
+ private Long productModelId;
+
+ @Schema(description = "浜у搧澶х被")
+ private String productCategory;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String specificationModel;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "鏁伴噺")
+ private BigDecimal quantity;
+
+ @Schema(description = "绋庣巼")
+ private BigDecimal taxRate;
+
+ @Schema(description = "鍚◣鍗曚环")
+ private BigDecimal taxInclusiveUnitPrice;
+
+ @Schema(description = "鍚◣鎬讳环")
+ private BigDecimal taxInclusiveTotalPrice;
+
+ @Schema(description = "涓嶅惈绋庢�讳环")
+ private BigDecimal taxExclusiveTotalPrice;
+
+ @Schema(description = "鍙戠エ绫诲瀷")
+ private String invoiceType;
+
+ @Schema(description = "鏄惁鎺ㄩ�佽川妫�")
+ private Boolean isChecked;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @TableField(fill = FieldFill.INSERT)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updateTime;
+}
diff --git a/src/main/java/com/ruoyi/purchase/service/PurchaseApplicationService.java b/src/main/java/com/ruoyi/purchase/service/PurchaseApplicationService.java
new file mode 100644
index 0000000..6477f21
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/service/PurchaseApplicationService.java
@@ -0,0 +1,43 @@
+package com.ruoyi.purchase.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.purchase.dto.PurchaseApplicationDto;
+import com.ruoyi.purchase.pojo.PurchaseApplication;
+
+import java.util.List;
+
+/**
+ * 閲囪喘鐢宠鍗� Service 鎺ュ彛
+ */
+public interface PurchaseApplicationService extends IService<PurchaseApplication> {
+
+ /**
+ * 鏌ヨ閲囪喘鐢宠鍒楄〃
+ */
+ List<PurchaseApplication> selectApplicationList(PurchaseApplicationDto purchaseApplicationDto);
+
+ /**
+ * 鏌ヨ閲囪喘鐢宠璇︽儏锛堝惈浜у搧鏄庣粏锛�
+ */
+ PurchaseApplicationDto getDetail(Long id);
+
+ /**
+ * 鏂板閲囪喘鐢宠锛堣崏绋匡級
+ */
+ Long saveApplication(PurchaseApplicationDto purchaseApplicationDto);
+
+ /**
+ * 缂栬緫閲囪喘鐢宠锛屼粎鑽夌鐘舵�佸厑璁哥紪杈�
+ */
+ boolean updateApplication(PurchaseApplicationDto purchaseApplicationDto);
+
+ /**
+ * 鍒犻櫎閲囪喘鐢宠锛屼粎鑽夌鐘舵�佸厑璁稿垹闄�
+ */
+ boolean deleteApplications(List<Long> ids);
+
+ /**
+ * 閲囪喘鐢宠杞鍗曪細鐢熸垚閲囪喘鍙拌处骞舵彁浜� OA 閲囪喘瀹℃壒锛岃繑鍥為噰璐彴璐d
+ */
+ Long convertToOrder(Long id);
+}
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseApplicationServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseApplicationServiceImpl.java
new file mode 100644
index 0000000..51ad290
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseApplicationServiceImpl.java
@@ -0,0 +1,346 @@
+package com.ruoyi.purchase.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.basic.mapper.ProductMapper;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.mapper.SupplierManageMapper;
+import com.ruoyi.basic.pojo.Product;
+import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.basic.pojo.SupplierManage;
+import com.ruoyi.common.enums.PurchaseApplicationStatusEnum;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.purchase.dto.PurchaseApplicationDto;
+import com.ruoyi.purchase.dto.PurchaseLedgerDto;
+import com.ruoyi.purchase.mapper.PurchaseApplicationMapper;
+import com.ruoyi.purchase.mapper.PurchaseApplicationProductMapper;
+import com.ruoyi.purchase.pojo.PurchaseApplication;
+import com.ruoyi.purchase.pojo.PurchaseApplicationProduct;
+import com.ruoyi.purchase.service.IPurchaseLedgerService;
+import com.ruoyi.purchase.service.PurchaseApplicationService;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 閲囪喘鐢宠鍗� Service 涓氬姟灞傚鐞�
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class PurchaseApplicationServiceImpl extends ServiceImpl<PurchaseApplicationMapper, PurchaseApplication>
+ implements PurchaseApplicationService {
+
+ private static final String APPLICATION_NO_PREFIX = "CGSQ";
+
+ private final PurchaseApplicationMapper purchaseApplicationMapper;
+ private final PurchaseApplicationProductMapper purchaseApplicationProductMapper;
+ private final IPurchaseLedgerService purchaseLedgerService;
+ private final SupplierManageMapper supplierManageMapper;
+ private final SysUserMapper sysUserMapper;
+ private final ProductMapper productMapper;
+ private final ProductModelMapper productModelMapper;
+
+ @Override
+ public List<PurchaseApplication> selectApplicationList(PurchaseApplicationDto purchaseApplicationDto) {
+ LambdaQueryWrapper<PurchaseApplication> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.like(StringUtils.isNotBlank(purchaseApplicationDto.getApplicationNo()),
+ PurchaseApplication::getApplicationNo, purchaseApplicationDto.getApplicationNo())
+ .like(StringUtils.isNotBlank(purchaseApplicationDto.getProjectName()),
+ PurchaseApplication::getProjectName, purchaseApplicationDto.getProjectName())
+ .eq(purchaseApplicationDto.getSupplierId() != null,
+ PurchaseApplication::getSupplierId, purchaseApplicationDto.getSupplierId())
+ .eq(purchaseApplicationDto.getStatus() != null,
+ PurchaseApplication::getStatus, purchaseApplicationDto.getStatus())
+ .ge(StringUtils.isNotBlank(purchaseApplicationDto.getStartDate()),
+ PurchaseApplication::getApplyDate, purchaseApplicationDto.getStartDate())
+ .le(StringUtils.isNotBlank(purchaseApplicationDto.getEndDate()),
+ PurchaseApplication::getApplyDate, purchaseApplicationDto.getEndDate())
+ .orderByDesc(PurchaseApplication::getId);
+ return this.list(queryWrapper);
+ }
+
+ @Override
+ public PurchaseApplicationDto getDetail(Long id) {
+ PurchaseApplication application = this.getById(id);
+ if (application == null) {
+ throw new ServiceException("閲囪喘鐢宠涓嶅瓨鍦�");
+ }
+ PurchaseApplicationDto resultDto = new PurchaseApplicationDto();
+ BeanUtils.copyProperties(application, resultDto);
+ resultDto.setProductData(listProducts(id));
+ return resultDto;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long saveApplication(PurchaseApplicationDto purchaseApplicationDto) {
+ PurchaseApplication application = new PurchaseApplication();
+ BeanUtils.copyProperties(purchaseApplicationDto, application);
+ application.setId(null);
+ application.setStatus(PurchaseApplicationStatusEnum.DRAFT.getCode());
+ application.setPurchaseLedgerId(null);
+ application.setConvertTime(null);
+ application.setApplicationNo(OrderUtils.countTodayByCreateTime(
+ purchaseApplicationMapper, APPLICATION_NO_PREFIX, "application_no", LocalDateTime.now()));
+ fillSupplierName(application);
+ fillApplicant(application, purchaseApplicationDto);
+ this.save(application);
+
+ saveProducts(application.getId(), purchaseApplicationDto.getProductData());
+ return application.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateApplication(PurchaseApplicationDto purchaseApplicationDto) {
+ if (purchaseApplicationDto.getId() == null) {
+ throw new ServiceException("閲囪喘鐢宠id涓嶈兘涓虹┖");
+ }
+ PurchaseApplication existing = this.getById(purchaseApplicationDto.getId());
+ if (existing == null) {
+ throw new ServiceException("閲囪喘鐢宠涓嶅瓨鍦�");
+ }
+ if (!PurchaseApplicationStatusEnum.DRAFT.getCode().equals(existing.getStatus())) {
+ throw new ServiceException("宸茬敓鎴愯鍗曠殑閲囪喘鐢宠涓嶅厑璁镐慨鏀�");
+ }
+
+ PurchaseApplication application = new PurchaseApplication();
+ BeanUtils.copyProperties(purchaseApplicationDto, application);
+ // 鍗曞彿銆佺姸鎬併�佽鍗曞叧鑱旂敱绯荤粺缁存姢锛屼笉鍏佽鍓嶇鏀瑰姩锛涚疆绌哄悗 MyBatis-Plus 涓嶄細鏇存柊杩欎簺鍒�
+ application.setApplicationNo(null);
+ application.setStatus(null);
+ application.setPurchaseLedgerId(null);
+ application.setConvertTime(null);
+ application.setTenantId(null);
+ application.setCreateUser(null);
+ application.setCreateTime(null);
+ fillSupplierName(application);
+ boolean updated = this.updateById(application);
+
+ if (updated) {
+ purchaseApplicationProductMapper.delete(Wrappers.<PurchaseApplicationProduct>lambdaQuery()
+ .eq(PurchaseApplicationProduct::getPurchaseApplicationId, purchaseApplicationDto.getId()));
+ saveProducts(purchaseApplicationDto.getId(), purchaseApplicationDto.getProductData());
+ }
+ return updated;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean deleteApplications(List<Long> ids) {
+ if (ids == null || ids.isEmpty()) {
+ throw new ServiceException("璇烽�夋嫨瑕佸垹闄ょ殑閲囪喘鐢宠");
+ }
+ List<String> orderedNos = this.listByIds(ids).stream()
+ .filter(application -> !PurchaseApplicationStatusEnum.DRAFT.getCode().equals(application.getStatus()))
+ .map(PurchaseApplication::getApplicationNo)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (!orderedNos.isEmpty()) {
+ throw new ServiceException("宸茬敓鎴愯鍗曠殑閲囪喘鐢宠涓嶅厑璁稿垹闄わ細" + String.join("銆�", orderedNos));
+ }
+
+ purchaseApplicationProductMapper.delete(Wrappers.<PurchaseApplicationProduct>lambdaQuery()
+ .in(PurchaseApplicationProduct::getPurchaseApplicationId, ids));
+ return this.removeByIds(ids);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long convertToOrder(Long id) {
+ if (id == null) {
+ throw new ServiceException("閲囪喘鐢宠id涓嶈兘涓虹┖");
+ }
+ PurchaseApplication application = this.getById(id);
+ if (application == null) {
+ throw new ServiceException("閲囪喘鐢宠涓嶅瓨鍦�");
+ }
+ if (application.getSupplierId() == null) {
+ throw new ServiceException("璇峰厛閫夋嫨渚涘簲鍟嗗啀杞鍗�");
+ }
+ SupplierManage supplierManage = supplierManageMapper.selectById(application.getSupplierId());
+ if (supplierManage == null) {
+ throw new ServiceException("渚涘簲鍟嗕笉瀛樺湪锛岃閲嶆柊閫夋嫨");
+ }
+ List<PurchaseApplicationProduct> products = listProducts(id);
+ if (products.isEmpty()) {
+ throw new ServiceException("閲囪喘鐢宠娌℃湁浜у搧鏄庣粏锛屾棤娉曡浆璁㈠崟");
+ }
+
+ // 鏉′欢鏇存柊鎶㈠崰鑽夌鐘舵�侊紝閬垮厤骞跺彂涓嬮噸澶嶇敓鎴愯鍗�
+ boolean converted = this.update(Wrappers.<PurchaseApplication>lambdaUpdate()
+ .eq(PurchaseApplication::getId, id)
+ .eq(PurchaseApplication::getStatus, PurchaseApplicationStatusEnum.DRAFT.getCode())
+ .set(PurchaseApplication::getStatus, PurchaseApplicationStatusEnum.ORDER_GENERATED.getCode()));
+ if (!converted) {
+ throw new ServiceException("璇ラ噰璐敵璇峰凡鐢熸垚璁㈠崟锛岃鍕块噸澶嶈浆鎹�");
+ }
+
+ PurchaseLedgerDto purchaseLedgerDto = buildPurchaseLedgerDto(application, supplierManage, products);
+ try {
+ // 澶嶇敤閲囪喘鍙拌处鏂板閫昏緫锛氱敓鎴愰噰璐悎鍚屽彿銆佷繚瀛樺彴璐︿笌浜у搧鏄庣粏銆佹彁浜� OA 閲囪喘瀹℃壒
+ purchaseLedgerService.addOrEditPurchase(purchaseLedgerDto);
+ } catch (Exception e) {
+ log.error("閲囪喘鐢宠杞鍗曞け璐�, applicationId={}, applicationNo={}",
+ id, application.getApplicationNo(), e);
+ throw new ServiceException("杞鍗曞け璐ワ細" + e.getMessage());
+ }
+
+ Long purchaseLedgerId = purchaseLedgerDto.getId();
+ if (purchaseLedgerId == null) {
+ throw new ServiceException("杞鍗曞け璐ワ細鏈敓鎴愰噰璐鍗�");
+ }
+ this.update(Wrappers.<PurchaseApplication>lambdaUpdate()
+ .eq(PurchaseApplication::getId, id)
+ .set(PurchaseApplication::getPurchaseLedgerId, purchaseLedgerId)
+ .set(PurchaseApplication::getConvertTime, LocalDateTime.now()));
+ return purchaseLedgerId;
+ }
+
+ private List<PurchaseApplicationProduct> listProducts(Long applicationId) {
+ return purchaseApplicationProductMapper.selectList(Wrappers.<PurchaseApplicationProduct>lambdaQuery()
+ .eq(PurchaseApplicationProduct::getPurchaseApplicationId, applicationId)
+ .orderByAsc(PurchaseApplicationProduct::getId));
+ }
+
+ private void saveProducts(Long applicationId, List<PurchaseApplicationProduct> products) {
+ if (products == null || products.isEmpty()) {
+ return;
+ }
+ fillProductNames(products);
+ for (PurchaseApplicationProduct product : products) {
+ product.setId(null);
+ product.setPurchaseApplicationId(applicationId);
+ product.setTenantId(null);
+ product.setCreateUser(null);
+ product.setCreateTime(null);
+ purchaseApplicationProductMapper.insert(product);
+ }
+ }
+
+ /**
+ * 琛ュ叏浜у搧澶х被涓庤鏍煎瀷鍙凤紝渚夸簬鐢宠鍗曠洿鎺ュ睍绀轰骇鍝佸悕绉�
+ */
+ private void fillProductNames(List<PurchaseApplicationProduct> products) {
+ Set<Long> productIds = products.stream()
+ .map(PurchaseApplicationProduct::getProductId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ Set<Long> modelIds = products.stream()
+ .map(PurchaseApplicationProduct::getProductModelId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+
+ Map<Long, String> productMap = new HashMap<>();
+ if (!productIds.isEmpty()) {
+ for (Product product : productMapper.selectBatchIds(productIds)) {
+ productMap.put(product.getId(), product.getProductName());
+ }
+ }
+ Map<Long, String> modelMap = new HashMap<>();
+ if (!modelIds.isEmpty()) {
+ for (ProductModel model : productModelMapper.selectBatchIds(modelIds)) {
+ modelMap.put(model.getId(), model.getModel());
+ }
+ }
+
+ for (PurchaseApplicationProduct product : products) {
+ String productName = productMap.get(product.getProductId());
+ if (productName != null) {
+ product.setProductCategory(productName);
+ }
+ String model = modelMap.get(product.getProductModelId());
+ if (model != null) {
+ product.setSpecificationModel(model);
+ }
+ }
+ }
+
+ private void fillSupplierName(PurchaseApplication application) {
+ if (application.getSupplierId() == null) {
+ return;
+ }
+ SupplierManage supplierManage = supplierManageMapper.selectById(application.getSupplierId());
+ if (supplierManage != null) {
+ application.setSupplierName(supplierManage.getSupplierName());
+ }
+ }
+
+ private void fillApplicant(PurchaseApplication application, PurchaseApplicationDto purchaseApplicationDto) {
+ Long applicantId = purchaseApplicationDto.getApplicantId() != null
+ ? purchaseApplicationDto.getApplicantId()
+ : SecurityUtils.getUserId();
+ application.setApplicantId(applicantId);
+
+ if (StringUtils.isBlank(purchaseApplicationDto.getApplicantName())) {
+ SysUser applicant = sysUserMapper.selectUserById(applicantId);
+ application.setApplicantName(applicant != null ? applicant.getNickName() : SecurityUtils.getUsername());
+ }
+ if (application.getApplyDate() == null) {
+ application.setApplyDate(LocalDate.now());
+ }
+ }
+
+ /**
+ * 缁勮閲囪喘鍙拌处鍏ュ弬锛氫緵搴斿晢銆侀攢鍞悎鍚屻�佷骇鍝佹槑缁嗙瓑锛涢噰璐悎鍚屽彿鐢遍噰璐彴璐︿晶鐢熸垚
+ */
+ private PurchaseLedgerDto buildPurchaseLedgerDto(PurchaseApplication application,
+ SupplierManage supplierManage,
+ List<PurchaseApplicationProduct> products) {
+ PurchaseLedgerDto purchaseLedgerDto = new PurchaseLedgerDto();
+ purchaseLedgerDto.setSupplierId(application.getSupplierId());
+ purchaseLedgerDto.setSupplierName(supplierManage.getSupplierName());
+ purchaseLedgerDto.setRecorderId(application.getApplicantId());
+ purchaseLedgerDto.setRecorderName(application.getApplicantName());
+ purchaseLedgerDto.setSalesContractNo(application.getSalesContractNo());
+ purchaseLedgerDto.setSalesLedgerId(application.getSalesLedgerId());
+ purchaseLedgerDto.setProjectName(application.getProjectName());
+ purchaseLedgerDto.setRemarks(application.getRemarks());
+ purchaseLedgerDto.setPaymentMethod(application.getPaymentMethod());
+ purchaseLedgerDto.setContractAmount(application.getContractAmount());
+ LocalDate entryDate = application.getApplyDate() != null ? application.getApplyDate() : LocalDate.now();
+ purchaseLedgerDto.setEntryDate(DateUtils.toDate(entryDate));
+ purchaseLedgerDto.setProductData(products.stream()
+ .map(this::toSalesLedgerProduct)
+ .collect(Collectors.toList()));
+ return purchaseLedgerDto;
+ }
+
+ private SalesLedgerProduct toSalesLedgerProduct(PurchaseApplicationProduct source) {
+ SalesLedgerProduct target = new SalesLedgerProduct();
+ target.setProductId(source.getProductId());
+ target.setProductModelId(source.getProductModelId());
+ target.setProductCategory(source.getProductCategory());
+ target.setSpecificationModel(source.getSpecificationModel());
+ target.setUnit(source.getUnit());
+ target.setQuantity(source.getQuantity());
+ target.setTaxRate(source.getTaxRate());
+ target.setTaxInclusiveUnitPrice(source.getTaxInclusiveUnitPrice());
+ target.setTaxInclusiveTotalPrice(source.getTaxInclusiveTotalPrice());
+ target.setTaxExclusiveTotalPrice(source.getTaxExclusiveTotalPrice());
+ target.setInvoiceType(source.getInvoiceType());
+ target.setIsChecked(source.getIsChecked());
+ return target;
+ }
+}
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
index 5a80184..a81fca7 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -37,6 +37,10 @@
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
+import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
+import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord;
+import com.ruoyi.measuringinstrumentledger.service.SparePartsRecordService;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
@@ -96,6 +100,11 @@
@RequiredArgsConstructor
public class PurchaseLedgerServiceImpl extends ServiceImpl<PurchaseLedgerMapper, PurchaseLedger> implements IPurchaseLedgerService {
+ /**
+ * 鏄庣粏绫诲瀷锛氶噰璐紙sales_ledger_product.type锛夈�備粎閲囪喘鏄庣粏鎵嶅洖鍐欏浠跺簱瀛�
+ */
+ private static final Integer LEDGER_TYPE_PURCHASE = 2;
+
private final PurchaseLedgerMapper purchaseLedgerMapper;
private final SalesLedgerMapper salesLedgerMapper;
private final SalesLedgerProductMapper salesLedgerProductMapper;
@@ -121,6 +130,8 @@
private final StockUtils stockUtils;
private final ApprovalTemplateMapper approvalTemplateMapper;
private final OilDepotMapper oilDepotMapper;
+ private final SparePartsMapper sparePartsMapper;
+ private final SparePartsRecordService sparePartsRecordService;
@Override
public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
@@ -190,6 +201,8 @@
purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
}
this.saveOrUpdate(purchaseLedger);
+ // 鍥炲啓涓婚敭锛屼緵璋冪敤鏂癸紙濡傞噰璐敵璇疯浆璁㈠崟锛夎幏鍙栨柊鐢熸垚鐨勯噰璐彴璐d
+ purchaseLedgerDto.setId(purchaseLedger.getId());
// 4. 澶勭悊瀛愯〃鏁版嵁
List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData();
@@ -706,6 +719,10 @@
salesLedgerProduct.setRegisterDate(localDateTime);
salesLedgerProductMapper.insert(salesLedgerProduct);
}
+ // 浠呮柊澧炴槑缁嗗洖鍐欏浠讹紝缂栬緫璧� update 鍒嗘敮澶╃劧涓嶉噸澶嶈鏁�
+ if (LEDGER_TYPE_PURCHASE.equals(ledgerType)) {
+ recordSparePartsInbound(insertList, purchaseLedger);
+ }
}
// 璁$畻鎬诲惈绋庨噾棰�
@@ -721,6 +738,47 @@
}
}
+ /**
+ * 閲囪喘鍙拌处鏂板鏄庣粏鎸夎鏍煎尮閰嶅浠讹細绱姞澶囦欢搴撳瓨骞惰涓�鏉″叆搴撴祦姘淬��
+ * 鏈淮鎶よ鏍肩殑澶囦欢涓嶅弬涓庯紝闈欓粯璺宠繃銆�
+ */
+ private void recordSparePartsInbound(List<SalesLedgerProduct> insertList, PurchaseLedger purchaseLedger) {
+ List<Long> modelIds = insertList.stream()
+ .map(SalesLedgerProduct::getProductModelId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (modelIds.isEmpty()) {
+ return;
+ }
+ List<SpareParts> sparePartsList = sparePartsMapper.selectList(new LambdaQueryWrapper<SpareParts>()
+ .in(SpareParts::getProductModelId, modelIds));
+ if (CollectionUtils.isEmpty(sparePartsList)) {
+ return;
+ }
+ Map<Long, SpareParts> sparePartsByModelId = sparePartsList.stream()
+ .collect(Collectors.toMap(SpareParts::getProductModelId, s -> s, (a, b) -> a));
+ for (SalesLedgerProduct product : insertList) {
+ SpareParts spareParts = sparePartsByModelId.get(product.getProductModelId());
+ if (spareParts == null || product.getQuantity() == null
+ || product.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ continue;
+ }
+ BigDecimal quantity = product.getQuantity();
+ spareParts.setQuantity((spareParts.getQuantity() == null ? BigDecimal.ZERO : spareParts.getQuantity()).add(quantity));
+ sparePartsMapper.updateById(spareParts);
+
+ SparePartsRecord record = new SparePartsRecord();
+ record.setSparePartsId(spareParts.getId());
+ record.setDirection(1);
+ record.setQuantity(quantity);
+ record.setSourceType(1);
+ record.setSourceId(purchaseLedger.getId());
+ record.setSourceNo(purchaseLedger.getPurchaseContractNumber());
+ sparePartsRecordService.save(record);
+ }
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public int deletePurchaseLedgerByIds(Long[] ids) {
diff --git a/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java b/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java
index 5cf592d..f5563ed 100644
--- a/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java
+++ b/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java
@@ -27,6 +27,9 @@
@Schema(description = "浠樻閲戦")
private BigDecimal paymentAmount;
+ @Schema(description = "鍥炴閲戦")
+ private BigDecimal receiptAmount;
+
@Schema(description = "搴斾粯閲戦")
private BigDecimal payableAmount;
diff --git a/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java b/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java
index 2434017..73af2b7 100644
--- a/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java
+++ b/src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java
@@ -23,6 +23,10 @@
//璇ヤ緵搴斿晢閲囪喘浠樻绱閲戦
private BigDecimal paymentAmount;
+ @Schema(description = "鍥炴閲戦")
+ //璇ヤ緵搴斿晢寰�鏉ュ洖娆剧疮璁¢噾棰濓紙payment_record 鎵嬪伐娴佹按锛�
+ private BigDecimal receiptAmount;
+
@Schema(description = "搴斾粯閲戦")
//璇ヤ緵搴斿晢閲囪喘搴斾粯绱閲戦=鍒濆閲戦-浠樻-閫�娆�
private BigDecimal payableAmount;
diff --git a/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java b/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
index 12ed935..e04884c 100644
--- a/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
+++ b/src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
@@ -33,4 +33,16 @@
ShippingApproveDto getDateilByShippingNo(String shippingNo);
boolean addReq(ShippingInfoDto req);
+
+ /**
+ * 浠撳偍鐗╂祦鍑哄簱鑷姩鐢熸垚鍙戣揣鍗曪細鐩存帴缃负宸插彂璐э紝涓嶅彂璧峰彂璐у鎵癸紝鍐呴儴鎵e噺搴撳瓨
+ *
+ * @return 鐢熸垚鐨勫彂璐у彴璐d
+ */
+ Long addShippedFromOutbound(ShippingInfoDto req);
+
+ /**
+ * 鍑哄簱鍗曡鍒犻櫎鏃跺啿閿�鍙戣揣鍗曪細鍒犻櫎鍙戣揣鍗曚笌鏄庣粏锛屽苟鍥炴粴宸叉墸鍑忕殑搴撳瓨
+ */
+ void cancelByOutbound(Long shippingInfoId);
}
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 be34bc0..351f1c9 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -36,6 +36,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
+import java.util.Date;
import java.util.List;
/**
@@ -171,7 +172,6 @@
@Override
public boolean addReq(ShippingInfoDto req) {
-
// 鏍¢獙璇ヤ骇鍝佹槸鍚﹀凡鏈夊緟瀹℃牳/瀹℃牳涓殑鍙戣揣璁板綍锛岄槻姝㈤噸澶嶆彁浜�
Long salesLedgerProductId = req.getSalesLedgerProductId();
if (salesLedgerProductId != null) {
@@ -216,4 +216,31 @@
this.updateById(req);
return true;
}
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long addShippedFromOutbound(ShippingInfoDto req) {
+ // 浠撳偍鐗╂祦鍑哄簱鍗冲彂璐э細涓嶅彂璧峰彂璐у鎵癸紝鐩存帴钀戒负宸插彂璐э紝搴撳瓨鐢� add 鍐呴儴鎵e噺
+ req.setShippingNo(OrderUtils.countTodayByCreateTime(
+ shippingInfoMapper, "SH", "shipping_no", LocalDateTime.now()));
+ req.setStatus("宸插彂璐�");
+ if (req.getShippingDate() == null) {
+ req.setShippingDate(new Date());
+ }
+ this.add(req);
+ return req.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void cancelByOutbound(Long shippingInfoId) {
+ if (shippingInfoId == null) {
+ return;
+ }
+ // 鍥炴粴璇ュ彂璐у崟鍗犵敤鐨勫簱瀛�
+ stockUtils.deleteStockOutRecord(shippingInfoId, StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
+ shippingProductDetailMapper.delete(new LambdaQueryWrapper<ShippingProductDetail>()
+ .eq(ShippingProductDetail::getShippingInfoId, shippingInfoId));
+ this.removeById(shippingInfoId);
+ }
}
diff --git a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
index cc205b1..b999a16 100644
--- a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
+++ b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
@@ -27,6 +27,9 @@
@Schema(description = "鏀舵閲戦")
private BigDecimal receiptPaymentAmount;
+ @Schema(description = "浠樻閲戦")
+ private BigDecimal paymentAmount;
+
@Schema(description = "搴旀敹閲戦")
private BigDecimal receiptableAmount;
diff --git a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
index a6c7d7f..5d79da4 100644
--- a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
+++ b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
@@ -23,6 +23,10 @@
//璇ュ鎴烽攢鍞敹娆剧疮璁¢噾棰�
private BigDecimal receiptPaymentAmount;
+ @Schema(description = "浠樻閲戦")
+ //璇ュ鎴峰線鏉ヤ粯娆剧疮璁¢噾棰濓紙payment_record 鎵嬪伐娴佹按锛�
+ private BigDecimal paymentAmount;
+
@Schema(description = "搴旀敹閲戦")
//璇ュ鎴烽攢鍞簲鏀剁疮璁¢噾棰�=鍒濆閲戦-鍥炴-閫�娆�
private BigDecimal receiptableAmount;
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
index 776447e..3911b43 100644
--- a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -19,8 +19,6 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
-import java.util.ArrayList;
-import java.util.List;
/**
* <p>
@@ -116,9 +114,8 @@
@Log(title = "涓嬭浇搴撳瓨瀵煎叆妯℃澘", businessType = BusinessType.EXPORT)
@PostMapping("/downloadStockInventory")
public void downloadStockInventory(HttpServletResponse response) {
- List<StockInventoryExportData> list = new ArrayList<>();
ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
- util.exportExcel(response, list, "搴撳瓨妯℃澘");
+ util.importTemplateExcel(response, "搴撳瓨妯℃澘");
}
@PostMapping("/exportStockInventory")
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
index a608401..bf607c3 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
@@ -1,6 +1,7 @@
package com.ruoyi.stock.execl;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type;
import lombok.Data;
import java.math.BigDecimal;
@@ -40,6 +41,12 @@
@Excel(name = "澶囨敞")
private String remark;
+
+ /**
+ * 鍌ㄧ綈鍙凤紙鍌ㄧ綈鍒嗙被锛夈�傞檺瀹� EXPORT锛岄伩鍏嶆薄鏌撳鍏ユā鏉�
+ */
+ @Excel(name = "鍌ㄧ綈鍙�", type = Type.EXPORT)
+ private String tankNo;
//
// @Excel(name = "鏈�鏂版洿鏂版椂闂�")
// @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
diff --git a/src/main/resources/mapper/basic/CustomerMapper.xml b/src/main/resources/mapper/basic/CustomerMapper.xml
index 037cc23..2f3e800 100644
--- a/src/main/resources/mapper/basic/CustomerMapper.xml
+++ b/src/main/resources/mapper/basic/CustomerMapper.xml
@@ -128,10 +128,12 @@
c.customer_name,
T1.contractAmounts AS contractAmounts,
IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
+ IFNULL(T5.paymentAmount, 0) AS paymentAmount,
T1.contractAmounts - IFNULL(T2.receiptPaymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount,
IFNULL(T4.returnAmount, 0) AS returnAmount
from (select customer_id, sum(contract_amount) as contractAmounts from sales_ledger group by customer_id) T1
left join (select customer_id, sum(collection_amount) as receiptPaymentAmount from account_sales_collection group by customer_id) T2 on T1.customer_id = T2.customer_id
+ left join (select party_id, sum(amount) as paymentAmount from payment_record where party_type = 1 and direction = 1 group by party_id) T5 on T5.party_id = T1.customer_id
left join (
select
sl.customer_id,
@@ -157,6 +159,7 @@
sl.execution_date,
sl.contract_amount AS contract_amount,
IFNULL(T1.receiptPaymentAmount, 0) AS receiptPaymentAmount,
+ IFNULL(T5.paymentAmount, 0) AS paymentAmount,
sl.contract_amount - IFNULL(T1.receiptPaymentAmount, 0) - IFNULL(T3.returnAmount, 0) AS receiptableAmount,
IFNULL(T3.returnAmount, 0) AS returnAmount
from sales_ledger sl
@@ -181,6 +184,12 @@
where rm.status=1
group by sl.id
)T3 on T3.id = sl.id
+ left join (
+ select party_id, sum(amount) as paymentAmount
+ from payment_record
+ where party_type = 1 and direction = 1
+ group by party_id
+ )T5 on T5.party_id = sl.customer_id
where sl.customer_id = #{customerId}
order by sl.id desc
</select>
diff --git a/src/main/resources/mapper/basic/SupplierManageMapper.xml b/src/main/resources/mapper/basic/SupplierManageMapper.xml
index 63bdf7e..0c104c1 100644
--- a/src/main/resources/mapper/basic/SupplierManageMapper.xml
+++ b/src/main/resources/mapper/basic/SupplierManageMapper.xml
@@ -74,10 +74,12 @@
sm.supplier_name,
T1.contractAmounts,
IFNULL(T2.paymentAmount, 0) AS paymentAmount,
+ IFNULL(T5.receiptAmount, 0) AS receiptAmount,
T1.contractAmounts - IFNULL(T2.paymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS payableAmount,
IFNULL(T4.returnAmount, 0) AS returnAmount
from (select supplier_id, sum(contract_amount) as contractAmounts from purchase_ledger group by supplier_id) T1
left join (select supplier_id, sum(payment_amount) as paymentAmount from account_purchase_payment group by supplier_id) T2 on T1.supplier_id = T2.supplier_id
+ left join (select party_id, sum(amount) as receiptAmount from payment_record where party_type = 2 and direction = 2 group by party_id) T5 on T5.party_id = T1.supplier_id
left join (
select
supplier_id,
@@ -100,6 +102,7 @@
pl.execution_date,
pl.contract_amount AS contract_amount,
IFNULL(T1.paymentAmount, 0) AS paymentAmount,
+ IFNULL(T5.receiptAmount, 0) AS receiptAmount,
pl.contract_amount - IFNULL(T1.paymentAmount, 0) - IFNULL(T3.returnAmount, 0) AS payableAmount,
IFNULL(T3.returnAmount, 0) AS returnAmount
from purchase_ledger pl
@@ -126,6 +129,12 @@
left join purchase_ledger pl on pro.purchase_ledger_id = pl.id
group by pl.id
)T3 on T3.id = pl.id
+ left join (
+ select party_id, sum(amount) as receiptAmount
+ from payment_record
+ where party_type = 2 and direction = 2
+ group by party_id
+ )T5 on T5.party_id = pl.supplier_id
where pl.supplier_id = #{supplierId}
order by pl.id desc
</select>
diff --git a/src/main/resources/mapper/measuringinstrumentledger/SparePartsRecordMapper.xml b/src/main/resources/mapper/measuringinstrumentledger/SparePartsRecordMapper.xml
new file mode 100644
index 0000000..d4e79a0
--- /dev/null
+++ b/src/main/resources/mapper/measuringinstrumentledger/SparePartsRecordMapper.xml
@@ -0,0 +1,32 @@
+<?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.measuringinstrumentledger.mapper.SparePartsRecordMapper">
+
+ <select id="listPage" resultType="com.ruoyi.measuringinstrumentledger.pojo.SparePartsRecord">
+ select t1.id,
+ t1.spare_parts_id,
+ t2.name as sparePartsName,
+ t1.direction,
+ t1.quantity,
+ t1.source_type,
+ t1.source_id,
+ t1.source_no,
+ t1.remark,
+ t1.create_user,
+ t1.create_time
+ from spare_parts_record t1
+ left join spare_parts t2 on t2.id = t1.spare_parts_id
+ <where>
+ <if test="req.sparePartsId != null">
+ and t1.spare_parts_id = #{req.sparePartsId}
+ </if>
+ <if test="req.direction != null">
+ and t1.direction = #{req.direction}
+ </if>
+ <if test="req.sourceType != null">
+ and t1.source_type = #{req.sourceType}
+ </if>
+ </where>
+ order by t1.id desc
+ </select>
+</mapper>
diff --git a/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml b/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
index f1a87ca..138e882 100644
--- a/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
@@ -20,12 +20,19 @@
t2.tax_exclusive_total_price,
t3.inbound_num as quantityStock
from purchase_ledger t1
- left join sales_ledger_product t2 on t1.id = t2.sales_ledger_id and slp.type = 2
+ left join sales_ledger_product t2 on t1.id = t2.sales_ledger_id and t2.type = 2
left join procurement_record_storage t3 on t2.id = t3.sales_ledger_product_id
- where t1.purchase_contract_number = #{req.purchaseContractNumber}
- <if test="req.id != null and req.id != ''">
- and t3.id = #{req.id}
- </if>
+ <where>
+ <if test="req.purchaseLedgerId != null">
+ and t1.id = #{req.purchaseLedgerId}
+ </if>
+ <if test="req.purchaseContractNumber != null and req.purchaseContractNumber != ''">
+ and t1.purchase_contract_number = #{req.purchaseContractNumber}
+ </if>
+ <if test="req.id != null and req.id != ''">
+ and t3.id = #{req.id}
+ </if>
+ </where>
group by t2.id
</select>
<select id="listPage" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementPageDto">
diff --git a/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml b/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
index 46925aa..55fefa7 100644
--- a/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
@@ -105,6 +105,8 @@
t2.product_category,
t1.id,
t1.code,
+ t1.batch_no,
+ t5.shipping_no,
t2.specification_model,
t2.unit,
t2.tax_rate,
@@ -120,6 +122,7 @@
left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id and t2.type = 1
left join sales_ledger t3 on t3.id = t2.sales_ledger_id
left join procurement_record_storage t4 on t4.id = t1.procurement_record_storage_id
+ left join shipping_info t5 on t5.id = t1.shipping_info_id
<where>
and t1.type = 2
<if test="req.customerName != null and req.customerName != ''">
@@ -180,6 +183,18 @@
order by id desc
limit 1
</select>
+ <select id="listBatchNoByProductModelId" resultType="com.ruoyi.procurementrecord.bean.vo.BatchNoOptionVo">
+ select si.product_model_id,
+ si.batch_no,
+ (si.qualitity - ifnull(si.locked_quantity, 0)) as availableQuantity
+ from stock_inventory si
+ where si.product_model_id = #{productModelId}
+ and si.batch_no is not null
+ and si.batch_no != ''
+ and (si.qualitity - ifnull(si.locked_quantity, 0)) > 0
+ order by si.batch_no
+ </select>
+
<select id="listPageBySemiProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto">
select
t1.id,
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
index fac5842..0068503 100644
--- a/src/main/resources/mapper/stock/StockInventoryMapper.xml
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -122,6 +122,13 @@
product_id,
'combined' as stockType,
(
+ select sir.tank_no
+ from stock_in_record sir
+ where sir.product_model_id = combined.product_model_id
+ order by sir.id desc
+ limit 1
+ ) as tank_no,
+ (
select sir.oil_product
from stock_in_record sir
where sir.product_model_id = combined.product_model_id
@@ -336,7 +343,15 @@
unit,
product_name,
product_id,
- 'combined' as stockType
+ 'combined' as stockType,
+ (
+ select sir.tank_no
+ from stock_in_record sir
+ where sir.product_model_id = combined.product_model_id
+ and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
+ order by sir.id desc
+ limit 1
+ ) as tank_no
from (
select
si.batch_no,
@@ -1126,7 +1141,7 @@
product_name,
product_id
order by
- batch_no
+ MAX(update_time) desc
</select>
</mapper>
--
Gitblit v1.9.3