From 3acb4bda764d615bc69d18339921cc92e0024cdd Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期四, 30 七月 2026 18:05:23 +0800
Subject: [PATCH] 生产订单物料需求修改
---
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java | 14 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java | 16 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java | 10
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java | 12
docs/work_order_material_requirement.md | 257 +++++++++++++++++++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProductBomMapper.java | 7
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java | 12 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java | 9
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseOrderSourceTypeEnum.java | 37 +++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderItemRespVO.java | 9
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApi.java | 9
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java | 7
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java | 17 +
yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/supplier/SrmSupplierServiceImpl.java | 7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java | 259 +++++++++++++++++----
19 files changed, 664 insertions(+), 53 deletions(-)
diff --git a/docs/work_order_material_requirement.md b/docs/work_order_material_requirement.md
new file mode 100644
index 0000000..39df8c1
--- /dev/null
+++ b/docs/work_order_material_requirement.md
@@ -0,0 +1,257 @@
+# 宸ュ崟鐗╂枡闇�姹傚垪琛紙鍚簱瀛樻牎楠岋級- 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 宸ュ崟璇︽儏椤� / 宸ュ崟鐗╂枡闇�姹� Tab
+- 宸ュ崟鍒楄〃 鈫� 鐗╂枡闇�姹傛煡鐪嬶紙寮圭獥鎴栦晶杈规爮锛�
+- **ERP 閲囪喘璁㈠崟鏂板椤甸潰**锛堝簱瀛樹笉瓒虫椂璺宠浆锛�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 宸ュ崟璇︽儏椤� 鈫� 鐗╂枡闇�姹� Tab锛氫紶鍏ュ綋鍓嶅伐鍗� `workOrderId`
+2. 鎺ュ彛鏍规嵁宸ュ崟鐨勬渶鍚庝竴閬撳伐搴忔姇鏂欙紝娌垮伐搴忎骇鍑洪摼鍚戝墠閫掓帹锛屽睍寮�鍑烘墍鏈夊簳灞傚師鏉愭枡鐨勬�婚渶姹傛暟閲�
+3. 闇�姹傛暟閲� = 姣忓崟浣嶆垚鍝佺敤閲� 脳 宸ュ崟鐢熶骇鏁伴噺
+4. 鑱旀煡搴撳瓨杩斿洖鍙敤閲忓拰鍏呰冻鏍囪瘑锛屽墠绔嵁姝ゅ睍绀哄簱瀛樼姸鎬�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/mes/pro/work-order-bom/item-list-by-work-order-id` | 鑾峰緱宸ュ崟鐗╂枡闇�姹傚垪琛紙鍚簱瀛橈級 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| workOrderId | Long | 鏄� | 宸ュ崟缂栧彿 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| itemId | Long | 鐗╂枡缂栧彿 |
+| itemCode | String | 鐗╂枡缂栫爜 |
+| itemName | String | 鐗╂枡鍚嶇О |
+| itemSpecification | String | 瑙勬牸鍨嬪彿 |
+| unitMeasureName | String | 鍗曚綅鍚嶇О |
+| quantity | BigDecimal | 闇�姹傛暟閲忥紙宸ュ崟鏁伴噺 脳 BOM 鐢ㄩ噺锛� |
+| stockQuantity | BigDecimal | 鍙敤搴撳瓨锛堝湪搴� - 鍐荤粨 - 鍗犵敤锛� |
+| purchaseOrderQuantity | BigDecimal | 鍦ㄩ�旈噰璐暟锛堟潵婧愪负鏈伐鍗曠殑閲囪喘璁㈠崟鏈叆搴撴暟閲忔眹鎬伙級 |
+| stockSufficient | Boolean | 搴撳瓨鏄惁鍏呰冻锛堝彲鐢ㄥ簱瀛� + 鍦ㄩ�旈噰璐暟 >= 闇�姹傛暟閲忥級锛宼rue=鏄� false=鍚� |
+| itemOrProduct | String | 鐗╂枡/浜у搧鏍囪瘑 |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+ "code": 0,
+ "data": [
+ {
+ "itemId": 41,
+ "itemCode": "ITEM_20260716004",
+ "itemName": "閾濇潌",
+ "itemSpecification": "蠁9.5mm",
+ "unitMeasureName": "kg",
+ "quantity": 600.00,
+ "stockQuantity": 500.00,
+ "purchaseOrderQuantity": 200.00,
+ "stockSufficient": true,
+ "itemOrProduct": "ITEM"
+ },
+ {
+ "itemId": 44,
+ "itemCode": "ITEM_20260716008",
+ "itemName": "闀�閿岄挗绾�",
+ "itemSpecification": "蠁1.6mm",
+ "unitMeasureName": "kg",
+ "quantity": 100.00,
+ "stockQuantity": 50.00,
+ "purchaseOrderQuantity": 0.00,
+ "stockSufficient": false,
+ "itemOrProduct": "ITEM"
+ }
+ ]
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| itemCode | 鍒楄〃鍒� | 鐗╂枡缂栫爜 |
+| itemName | 鍒楄〃鍒� | 鐗╂枡鍚嶇О |
+| itemSpecification | 鍒楄〃鍒� | 瑙勬牸鍨嬪彿 |
+| unitMeasureName | 鍒楄〃鍒� | 鍗曚綅 |
+| quantity | 鍒楄〃鍒� | 闇�姹傛暟閲忥紝鍙冲榻愶紝鍗冨垎浣嶆牸寮� |
+| stockQuantity | 鍒楄〃鍒� | 鍙敤搴撳瓨锛屽彸瀵归綈锛屽崈鍒嗕綅鏍煎紡 |
+| purchaseOrderQuantity | 鍒楄〃鍒� | 鍦ㄩ�旈噰璐暟锛屽彸瀵归綈锛屽崈鍒嗕綅鏍煎紡 |
+| stockSufficient | 鍒楄〃鍒� | 搴撳瓨鐘舵�侊紝瑙佷笅鏂圭姸鎬佸睍绀鸿鍒� |
+
+### 搴撳瓨鐘舵�佸睍绀鸿鍒�
+
+| stockSufficient | 灞曠ず鏂囨湰 | 寤鸿棰滆壊 | 璇存槑 |
+|-----------------|---------|---------|------|
+| true | 鍏呰冻 | 缁胯壊 | 鍙敤搴撳瓨 鈮� 闇�姹傛暟閲� |
+| false | 涓嶈冻 | 绾㈣壊 | 鍙敤搴撳瓨 < 闇�姹傛暟閲� |
+
+### 鏁版嵁甯﹀叆鍏崇郴
+
+| 鏉ユ簮 | 甯﹀叆瀛楁 | 璇存槑 |
+|------|----------|------|
+| 宸ュ崟璇︽儏 | workOrderId | 閫氳繃椤甸潰璺敱鍙傛暟鎴栬鏁版嵁鑾峰彇锛屼紶缁欐帴鍙� |
+
+## 鎺ュ彛璋冪敤鏃跺簭
+
+```
+宸ュ崟璇︽儏椤靛姞杞�
+ 鈹溾攢鈹� GET /mes/pro/work-order/get?workOrderId=47 鈫� 鑾峰彇宸ュ崟鍩烘湰淇℃伅
+ 鈹溾攢鈹� GET /mes/pro/work-order-process/list-by-work-order-id?workOrderId=47 鈫� 鑾峰彇宸ュ簭鍒楄〃
+ 鈹斺攢鈹� GET /mes/pro/work-order-bom/item-list-by-work-order-id?workOrderId=47 鈫� 鑾峰彇鐗╂枡闇�姹傦紙鍚簱瀛橈級
+```
+
+鏃犻渶鍓嶇疆璋冪敤锛屼紶鍏� workOrderId 鍗冲彲鐙珛璇锋眰銆�
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 宸ュ崟鏃犲伐搴� | 杩斿洖绌哄垪琛� |
+| 宸ュ崟鏃� BOM 鎶曟枡 | 杩斿洖绌哄垪琛� |
+| 宸ュ崟浜у搧鏈叧鑱斿伐鑹鸿矾绾� | 杩斿洖绌哄垪琛� |
+| 鐗╂枡闇�姹傝绠� | 浠庢渶鍚庝竴閬撳伐搴忥紙sort 鏈�澶э級鐨勬姇鏂欏嚭鍙戯紝娌垮伐搴忎骇鍑洪摼鍚戝墠閫掓帹灞曞紑 |
+| 鏁伴噺璁$畻 | 姣忓崟浣嶆垚鍝佺敤閲� 脳 宸ュ崟鐢熶骇鏁伴噺锛坬uantity锛� |
+| 鍙敤搴撳瓨璁$畻 | 姹囨�绘墍鏈夊簱浣嶇殑锛堝湪搴撴暟閲� - 鍐荤粨鏁伴噺 - 鍗犵敤鏁伴噺锛夛紝鈮�0 鐨勫簱浣嶄笉璁″叆 |
+| 鍦ㄩ�旈噰璐暟璁$畻 | 鏌ヨ鏉ユ簮涓烘湰宸ュ崟锛坰ourceType=1, sourceId=宸ュ崟ID锛夌殑閲囪喘璁㈠崟鏄庣粏锛屾眹鎬伙紙璁㈣喘鏁伴噺 - 宸插叆搴撴暟閲忥級 |
+| 鍏呰冻鍒ゆ柇 | 鍙敤搴撳瓨 + 鍦ㄩ�旈噰璐暟 >= 闇�姹傛暟閲� |
+
+## 閲囪喘鐢宠锛堝簱瀛樹笉瓒虫椂璺宠浆锛�
+
+### 瑙﹀彂鏉′欢
+
+鍒楄〃涓� `stockSufficient = false` 鐨勮锛屽湪鎿嶄綔鍒楀睍绀恒��**鐢宠閲囪喘**銆嶆寜閽��
+
+### 璺宠浆鐩爣
+
+ERP 閲囪喘璁㈠崟鏂板椤甸潰锛歚/erp/purchase-order/create`锛堝墠绔矾鐢憋紝闈炴帴鍙h矾寰勶級
+
+### 鏁版嵁甯﹀叆
+
+浠庣墿鏂欓渶姹傝 + 褰撳墠宸ュ崟鎼哄甫浠ヤ笅鏁版嵁璺宠浆鍒伴噰璐鍗曟柊澧為〉闈細
+
+**宸ュ崟鏉ユ簮瀛楁锛堥噰璐鍗曠嫭鏈夌殑杩芥函瀛楁锛夛細**
+
+| 鏉ユ簮瀛楁 | 鐩爣瀛楁 | 绫诲瀷 | 璇存槑 |
+|----------|----------|------|------|
+| 宸ュ崟 `id` | `sourceId` | Long | 鏉ユ簮鍗曟嵁缂栧彿 |
+| 宸ュ崟 `code` | `sourceNo` | String | 鏉ユ簮鍗曞彿 |
+| 鈥� | `sourceType` | Integer | 鍥哄畾浼� `1`锛堢敓浜у伐鍗曪級 |
+
+**浜у搧琛屽瓧娈碉細**
+
+| 鏉ユ簮瀛楁 | 鐩爣瀛楁 | 璇存槑 |
+|----------|----------|------|
+| `itemId` | `items[].productId` | 浜у搧缂栧彿 |
+| `itemCode` | 鈥� | 浜у搧缂栫爜锛屼緵椤甸潰鏄剧ず |
+| `itemName` | 鈥� | 浜у搧鍚嶇О锛屼緵椤甸潰鏄剧ず |
+| `itemSpecification` | 鈥� | 瑙勬牸鍨嬪彿锛屼緵椤甸潰鏄剧ず |
+| `quantity - stockQuantity` | `items[].count` | 寤鸿閲囪喘鏁伴噺锛堢己鍙� = 闇�姹� - 搴撳瓨锛� |
+| `unitMeasureName` | 鈥� | 鍗曚綅鍚嶇О锛屼緵椤甸潰鏄剧ず |
+
+### 椤甸潰璺宠浆娴佺▼
+
+```
+鐗╂枡闇�姹傚垪琛紙搴撳瓨涓嶈冻琛岋級
+ 鈹�
+ 鈹溾攢 鐐瑰嚮銆岀敵璇烽噰璐�嶆寜閽�
+ 鈹�
+ 鈹溾攢 璺宠浆鍒� ERP 閲囪喘璁㈠崟鏂板椤甸潰
+ 鈹� 鎼哄甫锛氬伐鍗曟潵婧愪俊鎭紙sourceId銆乻ourceNo銆乻ourceType=1锛�
+ 鈹� 浜у搧琛屼俊鎭紙productId銆佹暟閲忋�佺紪鐮併�佸悕绉般�佽鏍笺�佸崟浣嶏級
+ 鈹�
+ 鈹斺攢 閲囪喘璁㈠崟鏂板椤甸潰
+ 鈹溾攢 鑷姩棰勫~鏉ユ簮瀛楁锛堜笉鍙紪杈戯級
+ 鈹溾攢 鑷姩棰勫~浜у搧琛岋紙productId + count + unit锛�
+ 鈹溾攢 鐢ㄦ埛閫夋嫨渚涘簲鍟嗭紙supplierId锛屽繀濉級
+ 鈹溾攢 鐢ㄦ埛琛ュ厖鍗曚环銆佺◣鐜囩瓑淇℃伅
+ 鈹斺攢 鎻愪氦 POST /erp/purchase-order/create
+```
+
+### 浜や簰璇存槑
+
+| 鍦烘櫙 | 澶勭悊鏂瑰紡 |
+|------|----------|
+| 鍗曚釜鐗╂枡涓嶈冻 | 鐐瑰嚮璇ヨ銆岀敵璇烽噰璐�嶁啋 璺宠浆閲囪喘璁㈠崟椤甸潰锛岄濉� 1 琛屼骇鍝� |
+| 澶氫釜鐗╂枡涓嶈冻 | 鍙�愯鐐瑰嚮鍒嗗埆鐢宠锛屾垨鏀寔澶氶�夊悗鎵归噺甯﹀埌鍚屼竴涓噰璐鍗� |
+| 鍏ㄩ儴鍏呰冻 | 涓嶅睍绀恒�岀敵璇烽噰璐�嶆寜閽� |
+| 鍙敤搴撳瓨涓� 0 | `stockSufficient = false`锛屽悓鏍峰睍绀恒�岀敵璇烽噰璐�嶏紝缂哄彛 = 鍏ㄩ儴闇�姹傛暟閲� |
+
+### 閲囪喘璁㈠崟鍒涘缓 API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/erp/purchase-order/create` | 鍒涘缓閲囪喘璁㈠崟 |
+
+**瀹屾暣璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 鏉ユ簮 |
+|------|------|------|------|
+| `sourceType` | Integer | 鍚� | 鍥哄畾 `1`锛堢敓浜у伐鍗曪級锛岀敱椤甸潰鎼哄甫 |
+| `sourceNo` | String | 鍚� | 宸ュ崟缂栫爜 `workOrder.code` |
+| `sourceId` | Long | 鍚� | 宸ュ崟 ID `workOrder.id` |
+| `supplierId` | Long | 鏄� | 鐢ㄦ埛鍦ㄩ噰璐〉闈㈤�夋嫨渚涘簲鍟� |
+| `orderTime` | LocalDateTime | 鏄� | 榛樿褰撳ぉ |
+| `items[].productId` | Long | 鏄� | 鐗╂枡闇�姹傜殑 `itemId` |
+| `items[].productUnitId` | Long | 鏄� | 鐗╂枡瀵瑰簲鐨勮閲忓崟浣� ID |
+| `items[].count` | BigDecimal | 鏄� | 缂哄彛鏁伴噺锛堥渶姹� - 搴撳瓨锛� |
+| `items[].productPrice` | BigDecimal | 鍚� | 鐢ㄦ埛鎵嬪伐濉啓 |
+| `items[].taxPercent` | BigDecimal | 鍚� | 鐢ㄦ埛鎵嬪伐濉啓 |
+
+**璇锋眰绀轰緥锛�**
+
+```json
+{
+ "sourceType": 1,
+ "sourceNo": "GD20260730001",
+ "sourceId": 47,
+ "supplierId": 10,
+ "orderTime": "2026-07-30",
+ "items": [
+ {
+ "productId": 41,
+ "productUnitId": 5,
+ "count": 100.00,
+ "productPrice": 12.50,
+ "taxPercent": 13.00
+ }
+ ]
+}
+```
+
+**鍝嶅簲锛�**
+
+```json
+{
+ "code": 0,
+ "data": {
+ "id": 123,
+ "no": "CGD20260730001",
+ "sourceType": 1,
+ "sourceNo": "GD20260730001",
+ "sourceId": 47,
+ "sourceTypeName": "鐢熶骇宸ュ崟",
+ "supplierId": 10,
+ "items": [...]
+ }
+}
+```
+
+## 娉ㄦ剰浜嬮」
+
+- `stockSufficient` 涓� `false` 鏃讹紝鍓嶇搴�**宸紓鍖栧睍绀�**锛堝绾㈣壊楂樹寒锛夛紝骞跺湪鎿嶄綔鍒楁樉绀恒�岀敵璇烽噰璐�嶆寜閽�
+- 鍙敤搴撳瓨涓� 0 鏃讹紝`stockQuantity` 杩斿洖 `0`锛宍stockSufficient` 涓� `false`
+- 鎺ュ彛杩斿洖鐨勭墿鏂欏垪琛ㄥ彲鑳戒负绌猴紙宸ュ崟鏃犲伐搴�/鏃� BOM/鏈叧鑱旇矾绾匡級锛屽墠绔渶澶勭悊绌虹姸鎬佹彁绀�
+- 鏁伴噺瀛楁涓� `BigDecimal`锛屽墠绔睍绀烘椂娉ㄦ剰灏忔暟浣嶅鐞嗭紝寤鸿淇濈暀 2 浣嶅皬鏁�
+- 璺宠浆閲囪喘璁㈠崟鏃讹紝`productUnitId` 闇�瑕侀�氳繃鐗╂枡璇︽儏鎺ュ彛鑾峰彇锛堝綋鍓嶇墿鏂欓渶姹傚垪琛ㄦ湭杩斿洖姝ゅ瓧娈碉級锛屾垨鑰呴噰璐鍗曢〉闈㈡牴鎹� `productId` 鑷鏌ヨ榛樿鍗曚綅
+- 鍚屼竴涓墿鏂欏彲鑳藉湪澶氫釜宸ュ崟涓兘闇�瑕侀噰璐紝閲囪喘璁㈠崟椤甸潰搴旀敮鎸佺敤鎴蜂慨鏀规暟閲�
+- 鏉ユ簮瀛楁 `sourceType`銆乣sourceNo`銆乣sourceId` 鍦ㄩ噰璐鍗曢〉闈㈠睍绀轰负**鍙**锛屼笉鍏佽鐢ㄦ埛淇敼
+- 閲囪喘璁㈠崟璇︽儏/鍒楄〃涓彲閫氳繃 `sourceTypeName`锛堝"鐢熶骇宸ュ崟"锛夊拰 `sourceNo`锛堝伐鍗曠紪鐮侊級杩芥函鍒版潵婧愬伐鍗�
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java
index 3442060..2dad8f1 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java
@@ -7,6 +7,7 @@
import tools.jackson.databind.ValueDeserializer;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -20,7 +21,8 @@
public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer();
- private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
@@ -34,9 +36,13 @@
long millis = Long.parseLong(text);
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
} catch (NumberFormatException e) {
- // 涓嶆槸绾暟瀛楋紝鎸夐粯璁ゆ棩鏈熸牸寮忚В鏋�
+ // 涓嶆槸绾暟瀛楋紝鎸夋棩鏈熸牸寮忚В鏋�
}
- return LocalDateTime.parse(text, DEFAULT_FORMATTER);
+ // 绾棩鏈熸牸寮忥紙濡� "2026-07-29"锛夛紝琛ュ叏涓哄綋澶� 00:00:00
+ if (text.length() == 10) {
+ return LocalDate.parse(text, DATE_FORMATTER).atStartOfDay();
+ }
+ return LocalDateTime.parse(text, DATE_TIME_FORMATTER);
}
}
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApi.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApi.java
index f62d3d1..98e1b6a 100644
--- a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApi.java
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApi.java
@@ -84,4 +84,13 @@
*/
CommonResult<Long> createPurchaseOrder(ErpPurchaseOrderCreateReqDTO createReqDTO);
+ /**
+ * 鎸夋潵婧愭煡璇㈤噰璐鍗曟槑缁嗗垪琛紙鐢ㄤ簬杩芥函鏉ユ簮宸ュ崟鐨勯噰璐儏鍐碉級
+ *
+ * @param sourceType 鏉ユ簮绫诲瀷
+ * @param sourceId 鏉ユ簮鍗曟嵁缂栧彿
+ * @return 閲囪喘璁㈠崟鏄庣粏鍒楄〃
+ */
+ CommonResult<List<ErpPurchaseOrderItemRespDTO>> getPurchaseOrderItemListBySource(Integer sourceType, Long sourceId);
+
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
index 3b1cedf..7daf6eb 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
@@ -118,4 +118,20 @@
return success(purchaseOrderService.createPurchaseOrder(saveReqVO));
}
+ @Override
+ public CommonResult<List<ErpPurchaseOrderItemRespDTO>> getPurchaseOrderItemListBySource(Integer sourceType, Long sourceId) {
+ if (sourceType == null || sourceId == null) {
+ return success(Collections.emptyList());
+ }
+ // 1. 鎸夋潵婧愭煡璇㈤噰璐鍗�
+ List<ErpPurchaseOrderDO> orders = purchaseOrderService.getPurchaseOrderListBySource(sourceType, sourceId);
+ if (orders.isEmpty()) {
+ return success(Collections.emptyList());
+ }
+ // 2. 鏌ヨ鎵�鏈夎鍗曠殑鏄庣粏
+ List<Long> orderIds = orders.stream().map(ErpPurchaseOrderDO::getId).toList();
+ List<ErpPurchaseOrderItemDO> items = purchaseOrderItemMapper.selectListByOrderIds(orderIds);
+ return success(BeanUtils.toBean(items, ErpPurchaseOrderItemRespDTO.class));
+ }
+
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
index 612761d..70128e8 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;
+import cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderSourceTypeEnum;
import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseOrderService;
@@ -128,7 +129,9 @@
item.setStockCount(BigDecimal.ZERO);
MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName())
.setProductBarCode(mdmItem.getBarCode()));
- })).setAttachmentList(storageAttachmentApi.listAttachments("erp_purchase_order", id))));
+ })).setAttachmentList(storageAttachmentApi.listAttachments("erp_purchase_order", id))
+ .setSourceTypeName(ErpPurchaseOrderSourceTypeEnum.WORK_ORDER.getType()
+ .equals(purchaseOrder.getSourceType()) ? "鐢熶骇宸ュ崟" : null)));
}
@GetMapping("/page")
@@ -185,6 +188,8 @@
purchaseOrder.setProductNames(CollUtil.join(purchaseOrder.getItems(), "锛�", ErpPurchaseOrderRespVO.Item::getProductName));
MapUtils.findAndThen(supplierMap, purchaseOrder.getSupplierId(), supplier -> purchaseOrder.setSupplierName(supplier.getName()));
MapUtils.findAndThen(userMap, Long.parseLong(purchaseOrder.getCreator()), user -> purchaseOrder.setCreatorName(user.getNickname()));
+ purchaseOrder.setSourceTypeName(ErpPurchaseOrderSourceTypeEnum.WORK_ORDER.getType()
+ .equals(purchaseOrder.getSourceType()) ? "鐢熶骇宸ュ崟" : null);
});
}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
index 51cdf18..448bd6e 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
@@ -64,6 +64,20 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal depositPrice;
+ @Schema(description = "鏉ユ簮绫诲瀷", example = "1")
+ @ExcelProperty("鏉ユ簮绫诲瀷")
+ private Integer sourceType;
+
+ @Schema(description = "鏉ユ簮鍗曞彿", example = "GD20260730001")
+ @ExcelProperty("鏉ユ簮鍗曞彿")
+ private String sourceNo;
+
+ @Schema(description = "鏉ユ簮鍗曟嵁缂栧彿", example = "48")
+ private Long sourceId;
+
+ @Schema(description = "鏉ユ簮绫诲瀷鍚嶇О", example = "鐢熶骇宸ュ崟")
+ private String sourceTypeName;
+
@Schema(description = "澶囨敞", example = "浣犵寽")
@ExcelProperty("澶囨敞")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
index 201068d..4ee92f5 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
@@ -32,6 +32,15 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", example = "7127")
private BigDecimal depositPrice;
+ @Schema(description = "鏉ユ簮绫诲瀷", example = "1")
+ private Integer sourceType;
+
+ @Schema(description = "鏉ユ簮鍗曞彿", example = "GD20260730001")
+ private String sourceNo;
+
+ @Schema(description = "鏉ユ簮鍗曟嵁缂栧彿", example = "48")
+ private Long sourceId;
+
@Schema(description = "澶囨敞", example = "浣犵寽")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
index 48266e3..32f6244 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
@@ -92,6 +92,23 @@
private BigDecimal depositPrice;
/**
+ * 鏉ユ簮绫诲瀷
+ *
+ * 鏋氫妇 {@link cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderSourceTypeEnum}
+ */
+ private Integer sourceType;
+ /**
+ * 鏉ユ簮鍗曞彿
+ */
+ private String sourceNo;
+ /**
+ * 鏉ユ簮鍗曟嵁缂栧彿
+ *
+ * 鍏宠仈鏉ユ簮鍗曟嵁琛ㄧ殑涓婚敭 ID
+ */
+ private Long sourceId;
+
+ /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
index 76f979c..dc5c147 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
@@ -81,4 +81,13 @@
.orderByDesc(ErpPurchaseOrderDO::getId));
}
+ /**
+ * 鎸夋潵婧愭煡璇㈤噰璐鍗曪紙鐢ㄤ簬杩芥函鏉ユ簮宸ュ崟瀵瑰簲鐨勯噰璐鍗曪級
+ */
+ default List<ErpPurchaseOrderDO> selectListBySource(Integer sourceType, Long sourceId) {
+ return selectList(new LambdaQueryWrapperX<ErpPurchaseOrderDO>()
+ .eq(ErpPurchaseOrderDO::getSourceType, sourceType)
+ .eq(ErpPurchaseOrderDO::getSourceId, sourceId));
+ }
+
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseOrderSourceTypeEnum.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseOrderSourceTypeEnum.java
new file mode 100644
index 0000000..5c7eb9b
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseOrderSourceTypeEnum.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * ERP 閲囪喘璁㈠崟鏉ユ簮绫诲瀷鏋氫妇
+ *
+ * @author 鑺嬮亾婧愮爜
+ */
+@RequiredArgsConstructor
+@Getter
+public enum ErpPurchaseOrderSourceTypeEnum implements ArrayValuable<Integer> {
+
+ WORK_ORDER(1, "鐢熶骇宸ュ崟");
+
+ public static final Integer[] ARRAYS = Arrays.stream(values())
+ .map(ErpPurchaseOrderSourceTypeEnum::getType).toArray(Integer[]::new);
+
+ /**
+ * 绫诲瀷鍊�
+ */
+ private final Integer type;
+ /**
+ * 绫诲瀷鍚�
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
index a7b6fdd..af4ced2 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
@@ -149,6 +149,15 @@
List<ErpPurchaseOrderDO> getPurchaseOrderList(List<Long> ids);
/**
+ * 鎸夋潵婧愭煡璇㈤噰璐鍗曞垪琛�
+ *
+ * @param sourceType 鏉ユ簮绫诲瀷
+ * @param sourceId 鏉ユ簮鍗曟嵁缂栧彿
+ * @return 閲囪喘璁㈠崟鍒楄〃
+ */
+ List<ErpPurchaseOrderDO> getPurchaseOrderListBySource(Integer sourceType, Long sourceId);
+
+ /**
* 鏇存柊閲囪喘璁㈠崟鐨勫叆搴撶姸鎬�
*
* 鏍规嵁鏄庣粏鍏ュ簱鏁伴噺鑷姩璁$畻璁㈠崟鐘舵�侊細
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
index cd140a7..e04714b 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
@@ -451,6 +451,14 @@
}
@Override
+ public List<ErpPurchaseOrderDO> getPurchaseOrderListBySource(Integer sourceType, Long sourceId) {
+ if (sourceType == null || sourceId == null) {
+ return Collections.emptyList();
+ }
+ return purchaseOrderMapper.selectListBySource(sourceType, sourceId);
+ }
+
+ @Override
public void updatePurchaseOrderInStatus(Long orderId) {
// 1. 鑾峰彇閲囪喘璁㈠崟
ErpPurchaseOrderDO order = validatePurchaseOrderExists(orderId);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
index c2d586c..a44b286 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -12,14 +12,24 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkOrderBomSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
+import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
-import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -31,6 +41,7 @@
import java.math.BigDecimal;
import java.util.*;
+import java.util.Comparator;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -44,13 +55,23 @@
@Resource
private MesProWorkOrderBomService workOrderBomService;
@Resource
+ private MesProWorkOrderService workOrderService;
+ @Resource
+ private MesProWorkOrderProcessService workOrderProcessService;
+ @Resource
private MesMdItemService itemService;
@Resource
private MesMdUnitMeasureService unitMeasureService;
@Resource
private MesMdItemTypeService itemTypeService;
@Resource
- private MesMdProductBomService productBomService;
+ private MesProRouteProductService routeProductService;
+ @Resource
+ private MesProRouteProductBomService routeProductBomService;
+ @Resource
+ private MesWmMaterialStockMapper materialStockMapper;
+ @Resource
+ private ErpPurchaseOrderApi purchaseOrderApi;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ュ崟 BOM")
@@ -102,40 +123,67 @@
@PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
public CommonResult<List<MesProWorkOrderItemRespVO>> getWorkOrderBomItemListByWorkOrderId(
@RequestParam("workOrderId") Long workOrderId) {
- // 1. 鑾峰彇宸ュ崟 BOM 琛�
+ // 1. 鑾峰彇宸ュ崟
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) {
+ return success(Collections.emptyList());
+ }
+
+ // 2. 鑾峰彇宸ュ崟宸ュ簭鍒楄〃锛堝惈鍚勫伐搴忕殑鎶曟枡鏄庣粏涓庝骇鍑哄搧锛�
+ List<MesProWorkOrderProcessDO> processList = workOrderProcessService
+ .getWorkOrderProcessListByWorkOrderId(workOrderId);
+ if (CollUtil.isEmpty(processList)) {
+
+ return success(Collections.emptyList());
+ }
+
+ // 3. 鑾峰彇宸ュ崟 BOM 琛岋紙鍚勫伐搴忕殑鎶曟枡璁板綍锛屾寜 processId 鍒嗙粍浣跨敤锛�
List<MesProWorkOrderBomDO> bomList = workOrderBomService.getWorkOrderBomListByWorkOrderId(workOrderId);
if (CollUtil.isEmpty(bomList)) {
return success(Collections.emptyList());
}
- // 2. 閫愬眰灞曞紑锛屽緱鍒板彾瀛愮墿鏂� itemId 鈫� quantity
- Map<Long, BigDecimal> leafItems = buildWorkOrderItems(bomList);
+ // 4. 閫氳繃浜у搧ID鍙嶆煡宸ヨ壓璺嚎锛堝伐鍗� routeId 鍙兘涓虹┖锛�
+ Long routeId = null;
+ if (workOrder.getProductId() != null) {
+ MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(workOrder.getProductId());
+ if (routeProduct != null) {
+ routeId = routeProduct.getRouteId();
+ }
+ }
+
+ // 5. 鎸夊伐搴忎骇鍑洪摼閫愬眰灞曞紑 BOM锛屽緱鍒板彾瀛愮墿鏂欙紙姣忓崟浣嶆垚鍝佺殑鐢ㄩ噺锛�
+ Map<Long, BigDecimal> leafItems = buildWorkOrderItems(processList, bomList, routeId);
if (CollUtil.isEmpty(leafItems)) {
return success(Collections.emptyList());
}
- // 3. 鎷兼帴 VO 瀛楁
- Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(leafItems.keySet());
- Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
- convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
- Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
- convertSet(itemMap.values(), MesMdItemDO::getItemTypeId));
- List<MesProWorkOrderItemRespVO> result = new ArrayList<>(leafItems.size());
+ // 6. 涔樹互宸ュ崟鐢熶骇鏁伴噺锛屽緱鍒版�婚渶姹�
+ BigDecimal orderQty = workOrder.getQuantity() != null ? workOrder.getQuantity() : BigDecimal.ONE;
+ Map<Long, BigDecimal> totalRequirements = new LinkedHashMap<>();
for (Map.Entry<Long, BigDecimal> entry : leafItems.entrySet()) {
- MesMdItemDO item = itemMap.get(entry.getKey());
- if (item == null) {
- continue;
- }
- MesProWorkOrderItemRespVO vo = new MesProWorkOrderItemRespVO()
- .setItemId(item.getId()).setQuantity(entry.getValue())
- .setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification());
- MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
- unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
- MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
- itemType -> vo.setItemOrProduct(itemType.getItemOrProduct()));
- result.add(vo);
+ totalRequirements.put(entry.getKey(), entry.getValue().multiply(orderQty));
}
- return success(result);
+
+ // 7. 鏌ヨ鏉ユ簮涓烘湰宸ュ崟鐨勯噰璐鍗曟槑缁嗭紝鎸変骇鍝佹眹鎬诲湪閫旈噰璐暟
+ // sourceType=1 琛ㄧず鏉ユ簮绫诲瀷涓�"鐢熶骇宸ュ崟"
+ Map<Long, BigDecimal> purchaseOrderQtyMap = new HashMap<>();
+ List<ErpPurchaseOrderItemRespDTO> poItems = purchaseOrderApi
+ .getPurchaseOrderItemListBySource(1, workOrderId)
+ .getCheckedData();
+ if (CollUtil.isNotEmpty(poItems)) {
+ for (ErpPurchaseOrderItemRespDTO poItem : poItems) {
+ // 鍦ㄩ�� = 璁㈣喘鏁伴噺 - 宸插叆搴撴暟閲�
+ BigDecimal inCount = poItem.getInCount() != null ? poItem.getInCount() : BigDecimal.ZERO;
+ BigDecimal inTransit = poItem.getCount().subtract(inCount);
+ if (inTransit.compareTo(BigDecimal.ZERO) > 0) {
+ purchaseOrderQtyMap.merge(poItem.getProductId(), inTransit, BigDecimal::add);
+ }
+ }
+ }
+
+ // 8. 鏌ヨ搴撳瓨锛屾嫾鎺� VO
+ return success(buildItemRespVOList(totalRequirements, purchaseOrderQtyMap));
}
// ==================== 鎷兼帴 VO ====================
@@ -164,47 +212,160 @@
});
}
- // ==================== 鐗╂枡闇�姹傞�愬眰灞曞紑 ====================
+ // ==================== 鐗╂枡闇�姹傚睍寮�锛堝伐搴忎骇鍑洪摼 + 璺敱浜у搧 BOM锛� ====================
/**
- * 閫愬眰 IN 鏌ヨ灞曞紑宸ュ崟 BOM 鍒板彾瀛愮墿鏂欙紝鍚堝苟鍚岀墿鏂欑殑闇�姹傛暟閲�
+ * 鎸夊伐搴忎骇鍑洪摼閫愬眰灞曞紑鐗╂枡闇�姹傘��
*
- * @param bomList 宸ュ崟 BOM 鍒楄〃
- * @return 鍙跺瓙鐗╂枡 Map锛歩temId 鈫� 鍚堝苟鍚庣殑闇�姹傛暟閲�
+ * <p><b>鏍稿績閫昏緫锛�</b></p>
+ * <ol>
+ * <li>鎵惧嚭鍏抽敭宸ュ簭锛坘eyFlag=true锛夛紝浠ュ畠鐨勬姇鏂欎负灞曞紑璧风偣</li>
+ * <li>鏋勫缓浜у嚭鍝佲啋宸ュ簭鎶曟枡鐨勬槧灏勶紙{@code outputItemId 鈫� per-unit bomItems}锛�</li>
+ * <li>閫愬眰灞曞紑锛氳嫢鐗╂枡鏄煇宸ュ簭鐨勪骇鍑哄搧 鈫� 鐢ㄨ宸ュ簭鐨勬姇鏂欓厤鏂规浛浠o紱
+ * 鍚﹀垯鏌ヨ矾鐢变骇鍝� BOM 鈫� 鐢� BOM 瀛愰」鏇夸唬锛�
+ * 涓よ�呴兘娌℃湁 鈫� 鍙跺瓙鐗╂枡锛堝璐欢锛夛紝绱姞鍒扮粨鏋�</li>
+ * </ol>
+ *
+ * <p>杩欐牱閬垮厤浜嗘妸涓棿宸ュ簭鐨勬姇鏂欑洿鎺ョ撼鍏ヨ捣鐐瑰鑷寸殑閲嶅璁$畻闂銆�</p>
+ *
+ * @param processList 宸ュ崟宸ュ簭鍒楄〃锛堟寜 sort 鍗囧簭锛�
+ * @param bomList 宸ュ崟 BOM 琛岋紙鎵�鏈夊伐搴忕殑鎶曟枡鏄庣粏锛�
+ * @param routeId 宸ヨ壓璺嚎缂栧彿锛堝彲涓� null锛宯ull 鏃惰烦杩囪矾鐢� BOM 鏌ヨ锛�
+ * @return 鍙跺瓙鐗╂枡 Map锛歩temId 鈫� 闇�姹傛暟閲�
*/
- private Map<Long, BigDecimal> buildWorkOrderItems(List<MesProWorkOrderBomDO> bomList) {
- // 1. 鍒濆鍖栵細鏀堕泦绗竴灞傜墿鏂� ID 鍜屽搴旀暟閲�
- Map<Long, BigDecimal> currentLayer = new LinkedHashMap<>();
- for (MesProWorkOrderBomDO bom : bomList) {
- currentLayer.merge(bom.getItemId(), bom.getQuantity(), BigDecimal::add);
+ private Map<Long, BigDecimal> buildWorkOrderItems(List<MesProWorkOrderProcessDO> processList,
+ List<MesProWorkOrderBomDO> bomList,
+ Long routeId) {
+ // 1. 鏋勫缓浜у嚭鍝� 鈫� 宸ュ簭鎶曟枡閰嶆柟锛坧er-unit锛�
+ // outputItemId 鈫� [{itemId, quantity}, ...]
+ Map<Long, List<MesProWorkOrderBomDO>> processBomMap = new HashMap<>();
+ for (MesProWorkOrderProcessDO process : processList) {
+ if (process.getOutputItemId() == null) {
+ continue;
+ }
+ List<MesProWorkOrderBomDO> items = new ArrayList<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ if (bom.getProcessId().equals(process.getProcessId())) {
+ items.add(bom);
+ }
+ }
+ processBomMap.put(process.getOutputItemId(), items);
}
- // 2. 閫愬眰 IN 鏌ヨ灞曞紑 BOM
+ // 2. 鍙栨渶鍚庝竴閬撳伐搴忥紙sort 鏈�澶э級浣滀负灞曞紑璧风偣锛岄�愬眰鍚戝墠閫掓帹
+ MesProWorkOrderProcessDO lastProcess = processList.stream()
+ .max(Comparator.comparing(MesProWorkOrderProcessDO::getSort))
+ .orElse(null);
+ if (lastProcess == null) {
+ return Collections.emptyMap();
+ }
+
+ // 3. 浠ユ渶鍚庝竴閬撳伐搴忕殑鎶曟枡涓鸿捣鐐�
+ Map<Long, BigDecimal> currentLayer = new LinkedHashMap<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ if (bom.getProcessId().equals(lastProcess.getProcessId())) {
+ currentLayer.merge(bom.getItemId(), bom.getQuantity(), BigDecimal::add);
+ }
+ }
+ if (CollUtil.isEmpty(currentLayer)) {
+ return Collections.emptyMap();
+ }
+
+ // 4. 閫愬眰灞曞紑
Map<Long, BigDecimal> leafItems = new LinkedHashMap<>();
for (int i = 0; i < Byte.MAX_VALUE && CollUtil.isNotEmpty(currentLayer); i++) {
- // 2.1 鎵归噺鏌ヨ褰撳墠灞傛墍鏈夌墿鏂欑殑瀛� BOM
- List<MesMdProductBomDO> subBomList = productBomService.getProductBomListByItemIds(currentLayer.keySet());
- // 2.2 鎸� itemId 鍒嗙粍
- Map<Long, List<MesMdProductBomDO>> subBomMap = convertMultiMap(subBomList, MesMdProductBomDO::getItemId);
- // 2.3 鍖哄垎鍙跺瓙鑺傜偣鍜岄潪鍙跺瓙鑺傜偣
Map<Long, BigDecimal> nextLayer = new LinkedHashMap<>();
+
+ // 4.1 鎵归噺鏌ヨ璺敱浜у搧 BOM锛堢敤浜庨潪宸ュ簭浜у嚭鐨勭墿鏂欏睍寮�锛�
+ Map<Long, List<MesProRouteProductBomDO>> routeSubBomMap;
+ if (routeId != null) {
+ List<MesProRouteProductBomDO> routeSubBomList = routeProductBomService
+ .getRouteProductBomListByProductIds(routeId, currentLayer.keySet());
+ routeSubBomMap = convertMultiMap(routeSubBomList, MesProRouteProductBomDO::getProductId);
+ } else {
+ routeSubBomMap = Collections.emptyMap();
+ }
+
for (Map.Entry<Long, BigDecimal> entry : currentLayer.entrySet()) {
Long itemId = entry.getKey();
- BigDecimal quantity = entry.getValue();
- List<MesMdProductBomDO> children = subBomMap.get(itemId);
- // 鎯呭喌涓�锛氬彾瀛愯妭鐐癸紝绱姞鍒扮粨鏋�
- if (CollUtil.isEmpty(children)) {
- leafItems.merge(itemId, quantity, BigDecimal::add);
+ BigDecimal qty = entry.getValue();
+
+ // 鎯呭喌涓�锛氱墿鏂欐槸鏌愬伐搴忕殑浜у嚭鍝� 鈫� 鐢ㄥ伐搴忛厤鏂规浛浠�
+ List<MesProWorkOrderBomDO> processBom = processBomMap.get(itemId);
+ if (CollUtil.isNotEmpty(processBom)) {
+ for (MesProWorkOrderBomDO child : processBom) {
+ nextLayer.merge(child.getItemId(), qty.multiply(child.getQuantity()), BigDecimal::add);
+ }
continue;
}
- // 鎯呭喌浜岋細闈炲彾瀛愶紝瀛愮墿鏂欒繘鍏ヤ笅涓�灞�
- for (MesMdProductBomDO child : children) {
- nextLayer.merge(child.getBomItemId(), quantity.multiply(child.getQuantity()), BigDecimal::add);
+
+ // 鎯呭喌浜岋細璺敱浜у搧 BOM 涓湁涓嬬骇 鈫� 灞曞紑
+ List<MesProRouteProductBomDO> routeChildren = routeSubBomMap.get(itemId);
+ if (CollUtil.isNotEmpty(routeChildren)) {
+ for (MesProRouteProductBomDO child : routeChildren) {
+ nextLayer.merge(child.getItemId(), qty.multiply(child.getQuantity()), BigDecimal::add);
+ }
+ continue;
}
+
+ // 鎯呭喌涓夛細鍙跺瓙鐗╂枡
+ leafItems.merge(itemId, qty, BigDecimal::add);
}
currentLayer = nextLayer;
}
return leafItems;
}
+ // ==================== 鎷兼帴鐗╂枡闇�姹� VO锛堝惈搴撳瓨鏍¢獙锛� ====================
+
+ private List<MesProWorkOrderItemRespVO> buildItemRespVOList(Map<Long, BigDecimal> requirements,
+ Map<Long, BigDecimal> purchaseOrderQtyMap) {
+ // 1. 鎵归噺鑾峰彇鐗╂枡鍩虹淇℃伅
+ Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(requirements.keySet());
+ Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
+ convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
+ Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
+ convertSet(itemMap.values(), MesMdItemDO::getItemTypeId));
+
+ // 2. 鎵归噺鏌ヨ搴撳瓨锛屾寜 itemId 姹囨�诲彲鐢ㄩ噺锛堝湪搴� - 鍐荤粨 - 鍗犵敤锛�
+ Map<Long, BigDecimal> stockMap = new HashMap<>();
+ List<MesWmMaterialStockDO> stockList = materialStockMapper.selectListByItemIds(requirements.keySet());
+ for (MesWmMaterialStockDO stock : stockList) {
+ BigDecimal frozen = stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO;
+ BigDecimal reserved = stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO;
+ BigDecimal available = stock.getQuantity().subtract(frozen).subtract(reserved);
+ if (available.compareTo(BigDecimal.ZERO) > 0) {
+ stockMap.merge(stock.getItemId(), available, BigDecimal::add);
+ }
+ }
+
+ // 3. 鎷兼帴 VO
+ List<MesProWorkOrderItemRespVO> result = new ArrayList<>(requirements.size());
+ for (Map.Entry<Long, BigDecimal> entry : requirements.entrySet()) {
+ MesMdItemDO item = itemMap.get(entry.getKey());
+ if (item == null) {
+ continue;
+ }
+ BigDecimal requiredQty = entry.getValue();
+ BigDecimal stockQty = stockMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+ BigDecimal purchaseQty = purchaseOrderQtyMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+
+ // 鍏呰冻鍒ゆ柇 = 鍙敤搴撳瓨 + 鍦ㄩ�旈噰璐暟 >= 闇�姹傛暟閲�
+ BigDecimal totalSupply = stockQty.add(purchaseQty);
+
+ MesProWorkOrderItemRespVO vo = new MesProWorkOrderItemRespVO()
+ .setItemId(item.getId()).setQuantity(requiredQty)
+ .setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification())
+ .setStockQuantity(stockQty)
+ .setPurchaseOrderQuantity(purchaseQty)
+ .setStockSufficient(totalSupply.compareTo(requiredQty) >= 0);
+ MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
+ unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
+ MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
+ itemType -> vo.setItemOrProduct(itemType.getItemOrProduct()));
+ result.add(vo);
+ }
+ return result;
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderItemRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderItemRespVO.java
index 922bc24..c064071 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderItemRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderItemRespVO.java
@@ -27,6 +27,15 @@
@Schema(description = "闇�姹傛暟閲�", example = "1000.00")
private BigDecimal quantity;
+ @Schema(description = "鍙敤搴撳瓨", example = "500.00")
+ private BigDecimal stockQuantity;
+
+ @Schema(description = "鍦ㄩ�旈噰璐暟锛堟潵婧愪负鏈伐鍗曠殑閲囪喘璁㈠崟鏁伴噺姹囨�伙級", example = "200.00")
+ private BigDecimal purchaseOrderQuantity;
+
+ @Schema(description = "搴撳瓨鏄惁鍏呰冻锛堝彲鐢ㄥ簱瀛� + 鍦ㄩ�旈噰璐暟 >= 闇�姹傛暟閲忥級", example = "true")
+ private Boolean stockSufficient;
+
@Schema(description = "鐗╂枡/浜у搧鏍囪瘑", example = "ITEM")
private String itemOrProduct;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProductBomMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProductBomMapper.java
index 832c549..f14dced 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProductBomMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProductBomMapper.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import org.apache.ibatis.annotations.Mapper;
+import java.util.Collection;
import java.util.List;
/**
@@ -46,4 +47,10 @@
.eq(MesProRouteProductBomDO::getProductId, productId));
}
+ default List<MesProRouteProductBomDO> selectListByProductIds(Long routeId, Collection<Long> productIds) {
+ return selectList(new LambdaQueryWrapperX<MesProRouteProductBomDO>()
+ .eq(MesProRouteProductBomDO::getRouteId, routeId)
+ .in(MesProRouteProductBomDO::getProductId, productIds));
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
index 5769273..08eca45 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
@@ -183,4 +183,16 @@
.orderByAsc(MesWmMaterialStockDO::getReceiptTime));
}
+ /**
+ * 鎸夌墿鏂欑紪鍙锋壒閲忔煡璇㈠簱瀛樿褰曪紙鐢ㄤ簬鐗╂枡闇�姹傚簱瀛樻牎楠岋級
+ */
+ default List<MesWmMaterialStockDO> selectListByItemIds(Collection<Long> itemIds) {
+ if (itemIds == null || itemIds.isEmpty()) {
+ return java.util.Collections.emptyList();
+ }
+ return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
+ .in(MesWmMaterialStockDO::getItemId, itemIds)
+ .ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO));
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
index 09745db..9a50c14 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
@@ -4,6 +4,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import jakarta.validation.Valid;
+import java.util.Collection;
import java.util.List;
/**
@@ -62,4 +63,13 @@
*/
List<MesProRouteProductBomDO> getRouteProductBomListByRouteIdAndProductId(Long routeId, Long productId);
+ /**
+ * 鎸夊伐鑹鸿矾绾垮拰澶氫釜浜у搧鎵归噺鏌ヨ BOM 鍒楄〃
+ *
+ * @param routeId 宸ヨ壓璺嚎缂栧彿
+ * @param productIds 浜у搧鐗╂枡缂栧彿闆嗗悎
+ * @return BOM 鍒楄〃
+ */
+ List<MesProRouteProductBomDO> getRouteProductBomListByProductIds(Long routeId, Collection<Long> productIds);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
index d833683..ab8c209 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
@@ -13,6 +13,7 @@
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -137,4 +138,12 @@
return routeProductBomMapper.selectListByRouteIdAndProductId(routeId, productId);
}
+ @Override
+ public List<MesProRouteProductBomDO> getRouteProductBomListByProductIds(Long routeId, Collection<Long> productIds) {
+ if (CollUtil.isEmpty(productIds)) {
+ return java.util.Collections.emptyList();
+ }
+ return routeProductBomMapper.selectListByProductIds(routeId, productIds);
+ }
+
}
diff --git a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/supplier/SrmSupplierServiceImpl.java b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/supplier/SrmSupplierServiceImpl.java
index de712dc..42b8c4c 100644
--- a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/supplier/SrmSupplierServiceImpl.java
+++ b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/supplier/SrmSupplierServiceImpl.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.srm.service.supplier;
+import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.srm.controller.admin.supplier.vo.SrmSupplierPageReqVO;
@@ -14,7 +15,10 @@
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
@Service
@@ -79,6 +83,9 @@
@Override
public List<SrmSupplierDO> getSupplierList(Collection<Long> ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return Collections.emptyList();
+ }
return supplierMapper.selectBatchIds(ids);
}
--
Gitblit v1.9.3