From ffe7921a138331185186fd100846fe50dcaf841c Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 16 七月 2026 10:08:02 +0800
Subject: [PATCH] 1.采购管理-采购申请恢复
---
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseRequestStatusEnum.java | 38 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java | 127 ++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java | 1
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java | 6
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestItemDO.java | 81 ++
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java | 3
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchaseOrderItemRespDTO.java | 3
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java | 3
docs/erp_purchase_qc_check_flag.md | 103 +++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java | 118 ++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java | 12
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java | 30
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestMapper.java | 44 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java | 132 ++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java | 86 ++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestService.java | 114 +++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java | 430 ++++++++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java | 37 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java | 182 ++++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestItemMapper.java | 29 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java | 24
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineService.java | 8
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java | 3
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestPageReqVO.java | 42 +
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java | 18
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineServiceImpl.java | 6
29 files changed, 1,689 insertions(+), 10 deletions(-)
diff --git a/docs/erp_purchase_qc_check_flag.md b/docs/erp_purchase_qc_check_flag.md
new file mode 100644
index 0000000..3001efe
--- /dev/null
+++ b/docs/erp_purchase_qc_check_flag.md
@@ -0,0 +1,103 @@
+# ERP 閲囪喘璁㈠崟璐ㄦ瀛楁涓� MES 鏉ユ枡妫�楠岃嚜鍔ㄥ垱寤� - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- ERP 閲囪喘璁㈠崟鍒楄〃椤�
+- ERP 閲囪喘璁㈠崟鏂板/缂栬緫椤�
+- ERP 閲囪喘璁㈠崟璇︽儏椤�
+- MES 鍒拌揣閫氱煡鍗曞垪琛ㄩ〉
+- MES 鍒拌揣閫氱煡鍗曡鎯呴〉
+- MES 鏉ユ枡妫�楠岋紙IQC锛夊垪琛ㄩ〉
+
+## 涓氬姟娴佺▼
+
+### 1. 閲囪喘璁㈠崟鏂板璐ㄦ鏍囪瘑
+
+1. 鐢ㄦ埛鍦� ERP 閲囪喘璁㈠崟鏂板/缂栬緫椤甸潰锛屽彲浠ヤ负姣忎釜浜у搧鏄庣粏鍕鹃��"鏄惁闇�瑕佹潵鏂欐楠�"
+2. 淇濆瓨鏃讹紝`qcCheckFlag` 瀛楁闅忚鍗曟槑缁嗕竴璧蜂繚瀛�
+
+### 2. 浠庨噰璐鍗曠敓鎴愬埌璐ч�氱煡鍗�
+
+1. 鐢ㄦ埛鐐瑰嚮"鐢熸垚鍒拌揣閫氱煡鍗�"鎸夐挳
+2. 绯荤粺鑷姩灏嗛噰璐鍗曟槑缁嗙殑 `qcCheckFlag` 甯﹀叆鍒拌揣閫氱煡鍗曡鐨� `iqcCheckFlag`
+3. 鐢ㄦ埛鍙湪鍒拌揣閫氱煡鍗曡鎯呴〉淇敼姣忚鐨勮川妫�鏍囪瘑
+
+### 3. 鍒拌揣閫氱煡鍗曟彁浜よ嚜鍔ㄥ垱寤� IQC
+
+1. 鐢ㄦ埛鎻愪氦鍒拌揣閫氱煡鍗�
+2. 濡傛灉鏈夎鏍囪涓洪渶瑕佽川妫�锛坄iqcCheckFlag = true`锛夛細
+ - 鍒拌揣閫氱煡鍗曠姸鎬佸彉涓�"寰呰川妫�"
+ - 绯荤粺鑷姩涓烘瘡涓渶瑕佽川妫�鐨勮鍒涘缓涓�鏉� IQC 鏉ユ枡妫�楠屽崟
+ - 琛屼笂鐨� `iqcId` 鍏宠仈鍒板垱寤虹殑妫�楠屽崟
+3. 濡傛灉娌℃湁闇�瑕佽川妫�鐨勮锛岀姸鎬佺洿鎺ュ彉涓�"寰呭叆搴�"
+
+## API 鍙樻洿
+
+### ERP 閲囪喘璁㈠崟
+
+#### 璇锋眰鍙傛暟鍙樻洿
+
+**ErpPurchaseOrderSaveReqVO.Item 鏂板瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| qcCheckFlag | Boolean | 鍚� | 鏄惁闇�瑕佹潵鏂欐楠� |
+
+#### 鍝嶅簲鍙傛暟鍙樻洿
+
+**ErpPurchaseOrderRespVO.Item 鏂板瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| qcCheckFlag | Boolean | 鏄惁闇�瑕佹潵鏂欐楠� |
+
+### MES 鍒拌揣閫氱煡鍗�
+
+#### 鍝嶅簲鍙傛暟鍙樻洿
+
+**鍒拌揣閫氱煡鍗曡鏂板瀛楁锛堝凡瀛樺湪锛屽己璋冨甫鍏ラ�昏緫锛夛細**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| iqcCheckFlag | Boolean | 鏄惁闇�瑕佹潵鏂欐楠岋紙浠庨噰璐鍗曞甫鍏ワ級 |
+| iqcId | Long | 鍏宠仈鐨� IQC 妫�楠屽崟 ID锛堟彁浜ゅ悗鑷姩鍒涘缓锛� |
+
+## 瀛楁灞曠ず瑙勫垯
+
+### ERP 閲囪喘璁㈠崟
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鏄惁璐ㄦ | 璁㈠崟鏄庣粏琛ㄦ牸 | 浣跨敤 Checkbox 鎴� Switch 灞曠ず锛屽彲缂栬緫 |
+
+### MES 鍒拌揣閫氱煡鍗�
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鏄惁璐ㄦ | 琛屾槑缁嗚〃鏍� | 浣跨敤 Tag 鎴� Checkbox 灞曠ず锛屾彁浜ゅ悗涓嶅彲淇敼 |
+| IQC 妫�楠屽崟 | 琛屾槑缁嗚〃鏍� | 鍏宠仈鐨勬楠屽崟缂栧彿锛岀偣鍑诲彲璺宠浆锛堟彁浜ゅ悗鑷姩鍏宠仈锛� |
+
+### MES 鏉ユ枡妫�楠�
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鏉ユ簮鍗曟嵁 | 璇︽儏椤� | 鏄剧ず鍒拌揣閫氱煡鍗曠紪鍙凤紝鐐瑰嚮鍙烦杞� |
+| 鏉ユ簮鍗曟嵁琛� | 璇︽儏椤� | 鏄剧ず鍒拌揣閫氱煡鍗曡缂栧彿 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 閲囪喘璁㈠崟淇濆瓨 | `qcCheckFlag` 榛樿涓� false锛堜笉璐ㄦ锛� |
+| 鐢熸垚鍒拌揣閫氱煡鍗� | 浠庨噰璐鍗曟槑缁嗗甫鍏� `qcCheckFlag` 鍒� `iqcCheckFlag` |
+| 鍒拌揣閫氱煡鍗曟彁浜� | 濡傛湁闇�瑕佽川妫�鐨勮锛岃嚜鍔ㄥ垱寤� IQC 妫�楠屽崟骞跺叧鑱� |
+| IQC 鍒涘缓鏉′欢 | 鐗╂枡蹇呴』閰嶇疆浜� IQC 妫�楠屾ā鏉匡紝鍚﹀垯璺宠繃骞惰褰曟棩蹇� |
+| IQC 鍗曟嵁鐘舵�� | 鑷姩鍒涘缓鐨� IQC 涓鸿崏绋跨姸鎬侊紝闇�瑕佹楠屽憳濉啓妫�楠岀粨鏋� |
+
+## 娉ㄦ剰浜嬮」
+
+1. **妫�楠屾ā鏉块厤缃�**锛氱墿鏂欏繀椤诲厛鍦� MES 璐ㄩ噺妯″潡閰嶇疆 IQC 妫�楠屾ā鏉匡紝鍚﹀垯鑷姩鍒涘缓 IQC 浼氬け璐�
+2. **鏁版嵁甯﹀叆**锛氫粠閲囪喘璁㈠崟鐢熸垚鍒拌揣閫氱煡鍗曟椂锛岃川妫�鏍囪瘑浼氳嚜鍔ㄥ甫鍏ワ紝浣嗙敤鎴峰彲鍦ㄥ埌璐ч�氱煡鍗曚腑淇敼
+3. **鐘舵�佹祦杞�**锛氬埌璐ч�氱煡鍗曟彁浜ゅ悗锛岄渶瑕佽川妫�鐨勮浼氳繘鍏�"寰呰川妫�"鐘舵�侊紝鎵�鏈夋楠屽畬鎴愬悗鎵嶅彲鍏ュ簱
+4. **妫�楠屽崟鍏宠仈**锛氭彁浜ゅ悗琛屼笂浼氬叧鑱� IQC 妫�楠屽崟 ID锛岀偣鍑诲彲璺宠浆鏌ョ湅妫�楠岃鎯�
+5. **寮傚父澶勭悊**锛氬鏋滄煇琛屽垱寤� IQC 澶辫触锛堝缂哄皯妯℃澘锛夛紝涓嶄細涓柇鏁翠釜鎻愪氦娴佺▼锛屼細璁板綍鏃ュ織缁х画澶勭悊鍏朵粬琛�
\ No newline at end of file
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
index 8171fb0..f4e3776 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
@@ -62,6 +62,9 @@
@Override
public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return emptyList();
+ }
return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();
}
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchaseOrderItemRespDTO.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchaseOrderItemRespDTO.java
index 7df8499..f100da6 100644
--- a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchaseOrderItemRespDTO.java
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchaseOrderItemRespDTO.java
@@ -36,4 +36,7 @@
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
+ @Schema(description = "鏄惁闇�瑕佹潵鏂欐楠�", example = "true")
+ private Boolean qcCheckFlag;
+
}
\ 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 bc5cb00..f14cbc0 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
@@ -39,6 +39,7 @@
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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘璁㈠崟")
@RestController
@@ -80,6 +81,23 @@
return success(true);
}
+ @PostMapping("/submit")
+ @Operation(summary = "鎻愪氦閲囪喘璁㈠崟瀹℃壒")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-order:update-status')")
+ public CommonResult<Boolean> submitPurchaseOrder(@RequestParam("id") Long id,
+ @RequestParam("processDefinitionKey") String processDefinitionKey) {
+ Long userId = getLoginUserId();
+ purchaseOrderService.submitPurchaseOrder(id, processDefinitionKey, userId);
+ return success(true);
+ }
+
+ @GetMapping("/approve-process-list")
+ @Operation(summary = "鑾峰彇閲囪喘璁㈠崟瀹℃壒娴佺▼鍒楄〃")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-order:query')")
+ public CommonResult<List<Map<String, Object>>> getApproveProcessList() {
+ return success(purchaseOrderService.getPurchaseOrderApproveProcessDefinitionList());
+ }
+
@DeleteMapping("/delete")
@Operation(summary = "鍒犻櫎閲囪喘璁㈠崟")
@Parameter(name = "ids", description = "缂栧彿鏁扮粍", required = true)
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java
new file mode 100644
index 0000000..814f10e
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java
@@ -0,0 +1,182 @@
+package cn.iocoder.yudao.module.erp.controller.admin.purchase;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestRespVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestSaveReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;
+import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseRequestService;
+import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘鐢宠")
+@RestController
+@RequestMapping("/erp/purchase-request")
+@Validated
+public class ErpPurchaseRequestController {
+
+ @Resource
+ private ErpPurchaseRequestService purchaseRequestService;
+ @Resource
+ private ErpSupplierService supplierService;
+
+ @Resource
+ private MdmItemApi mdmItemApi;
+ @Resource
+ private AdminUserApi adminUserApi;
+ @Resource
+ private DeptApi deptApi;
+
+ @PostMapping("/create")
+ @Operation(summary = "鍒涘缓閲囪喘鐢宠")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:create')")
+ public CommonResult<Long> createPurchaseRequest(@Valid @RequestBody ErpPurchaseRequestSaveReqVO createReqVO) {
+ return success(purchaseRequestService.createPurchaseRequest(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "鏇存柊閲囪喘鐢宠")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:update')")
+ public CommonResult<Boolean> updatePurchaseRequest(@Valid @RequestBody ErpPurchaseRequestSaveReqVO updateReqVO) {
+ purchaseRequestService.updatePurchaseRequest(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "鍒犻櫎閲囪喘鐢宠")
+ @Parameter(name = "ids", description = "缂栧彿鏁扮粍", required = true)
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:delete')")
+ public CommonResult<Boolean> deletePurchaseRequest(@RequestParam("ids") List<Long> ids) {
+ purchaseRequestService.deletePurchaseRequest(ids);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "鑾峰緱閲囪喘鐢宠")
+ @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:query')")
+ public CommonResult<ErpPurchaseRequestRespVO> getPurchaseRequest(@RequestParam("id") Long id) {
+ ErpPurchaseRequestDO purchaseRequest = purchaseRequestService.getPurchaseRequest(id);
+ if (purchaseRequest == null) {
+ return success(null);
+ }
+ List<ErpPurchaseRequestItemDO> purchaseRequestItemList = purchaseRequestService.getPurchaseRequestItemListByRequestId(id);
+ Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
+ convertSet(purchaseRequestItemList, ErpPurchaseRequestItemDO::getProductId));
+ return success(BeanUtils.toBean(purchaseRequest, ErpPurchaseRequestRespVO.class, purchaseRequestVO ->
+ purchaseRequestVO.setItems(BeanUtils.toBean(purchaseRequestItemList, ErpPurchaseRequestRespVO.Item.class, item -> {
+ MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName())
+ .setProductBarCode(mdmItem.getBarCode()));
+ }))));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "鑾峰緱閲囪喘鐢宠鍒嗛〉")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:query')")
+ public CommonResult<PageResult<ErpPurchaseRequestRespVO>> getPurchaseRequestPage(@Valid ErpPurchaseRequestPageReqVO pageReqVO) {
+ PageResult<ErpPurchaseRequestDO> pageResult = purchaseRequestService.getPurchaseRequestPage(pageReqVO);
+ return success(buildPurchaseRequestVOPageResult(pageResult));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "瀵煎嚭閲囪喘鐢宠 Excel")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportPurchaseRequestExcel(@Valid ErpPurchaseRequestPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List<ErpPurchaseRequestRespVO> list = buildPurchaseRequestVOPageResult(purchaseRequestService.getPurchaseRequestPage(pageReqVO)).getList();
+ ExcelUtils.write(response, "閲囪喘鐢宠.xls", "鏁版嵁", ErpPurchaseRequestRespVO.class, list);
+ }
+
+ @PutMapping("/submit")
+ @Operation(summary = "鎻愪氦閲囪喘鐢宠瀹℃壒")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:submit')")
+ public CommonResult<Boolean> submitPurchaseRequest(@RequestParam("id") Long id,
+ @RequestParam("processDefinitionKey") String processDefinitionKey) {
+ Long userId = getLoginUserId();
+ purchaseRequestService.submitPurchaseRequest(id, processDefinitionKey, userId);
+ return success(true);
+ }
+
+ @GetMapping("/approve-process-list")
+ @Operation(summary = "鑾峰彇閲囪喘鐢宠瀹℃壒娴佺▼鍒楄〃")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:query')")
+ public CommonResult<List<Map<String, Object>>> getApproveProcessList() {
+ return success(purchaseRequestService.getPurchaseRequestApproveProcessDefinitionList());
+ }
+
+ @PostMapping("/generate-order")
+ @Operation(summary = "鐢熸垚閲囪喘璁㈠崟")
+ @PreAuthorize("@ss.hasPermission('erp:purchase-request:generate-order')")
+ public CommonResult<Long> generatePurchaseOrder(@RequestParam("id") Long id,
+ @RequestParam("supplierId") Long supplierId) {
+ return success(purchaseRequestService.generatePurchaseOrder(id, supplierId));
+ }
+
+ private PageResult<ErpPurchaseRequestRespVO> buildPurchaseRequestVOPageResult(PageResult<ErpPurchaseRequestDO> pageResult) {
+ if (CollUtil.isEmpty(pageResult.getList())) {
+ return PageResult.empty(pageResult.getTotal());
+ }
+ // 1.1 鐢宠鏄庣粏
+ List<ErpPurchaseRequestItemDO> purchaseRequestItemList = purchaseRequestService.getPurchaseRequestItemListByRequestIds(
+ convertSet(pageResult.getList(), ErpPurchaseRequestDO::getId));
+ Map<Long, List<ErpPurchaseRequestItemDO>> purchaseRequestItemMap = convertMultiMap(purchaseRequestItemList, ErpPurchaseRequestItemDO::getRequestId);
+ // 1.2 鐗╂枡淇℃伅
+ Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
+ convertSet(purchaseRequestItemList, ErpPurchaseRequestItemDO::getProductId));
+ // 1.3 渚涘簲鍟嗕俊鎭�
+ Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(
+ convertSet(pageResult.getList(), ErpPurchaseRequestDO::getSupplierId));
+ // 1.4 鐢ㄦ埛淇℃伅
+ Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
+ convertSet(pageResult.getList(), ErpPurchaseRequestDO::getRequestUserId));
+ // 1.5 閮ㄩ棬淇℃伅
+ Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
+ convertSet(pageResult.getList(), ErpPurchaseRequestDO::getRequestDeptId));
+ // 2. 寮�濮嬫嫾鎺�
+ return BeanUtils.toBean(pageResult, ErpPurchaseRequestRespVO.class, purchaseRequest -> {
+ purchaseRequest.setItems(BeanUtils.toBean(purchaseRequestItemMap.get(purchaseRequest.getId()), ErpPurchaseRequestRespVO.Item.class,
+ item -> MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName())
+ .setProductBarCode(mdmItem.getBarCode()))));
+ purchaseRequest.setProductNames(CollUtil.join(purchaseRequest.getItems(), "锛�", ErpPurchaseRequestRespVO.Item::getProductName));
+ MapUtils.findAndThen(supplierMap, purchaseRequest.getSupplierId(), supplier -> purchaseRequest.setSupplierName(supplier.getName()));
+ MapUtils.findAndThen(userMap, purchaseRequest.getRequestUserId(), user -> purchaseRequest.setRequestUserName(user.getNickname()));
+ MapUtils.findAndThen(deptMap, purchaseRequest.getRequestDeptId(), dept -> purchaseRequest.setRequestDeptName(dept.getName()));
+ });
+ }
+
+}
\ No newline at end of file
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 e6a6aa4..20563c6 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
@@ -145,8 +145,13 @@
private String productUnitName;
@Schema(description = "搴撳瓨鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
- private BigDecimal stockCount; // 璇ュ瓧娈典粎浠呭湪鈥滆鎯呪�濆拰鈥滅紪杈戔�濇椂浣跨敤
+ private BigDecimal stockCount; // 璇ュ瓧娈典粎浠呭湪璇︽儏鍜岀紪杈戞椂浣跨敤
+
+ // ========== 璐ㄦ ==========
+
+ @Schema(description = "鏄惁闇�瑕佹潵鏂欐楠�", example = "true")
+ private Boolean qcCheckFlag;
}
-}
+}
\ No newline at end of file
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 061ed9d..d259001 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
@@ -68,6 +68,9 @@
@Schema(description = "澶囨敞", example = "闅忎究")
private String remark;
+ @Schema(description = "鏄惁闇�瑕佹潵鏂欐楠�", example = "true")
+ private Boolean qcCheckFlag;
+
}
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestPageReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestPageReqVO.java
new file mode 100644
index 0000000..2ee0563
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestPageReqVO.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - ERP 閲囪喘鐢宠鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ErpPurchaseRequestPageReqVO extends PageParam {
+
+ @Schema(description = "閲囪喘鐢宠鍗曞彿", example = "CGSQ001")
+ private String no;
+
+ @Schema(description = "鐢宠浜虹紪鍙�", example = "1")
+ private Long requestUserId;
+
+ @Schema(description = "鐢宠閮ㄩ棬缂栧彿", example = "1")
+ private Long requestDeptId;
+
+ @Schema(description = "渚涘簲鍟嗙紪鍙�", example = "1")
+ private Long supplierId;
+
+ @Schema(description = "鐘舵��", example = "0")
+ private Integer status;
+
+ @Schema(description = "鐢宠鏃堕棿")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDateTime[] requestTime;
+
+ @Schema(description = "浜у搧缂栧彿", example = "1")
+ private Long productId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java
new file mode 100644
index 0000000..b62323e
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java
@@ -0,0 +1,118 @@
+package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - ERP 閲囪喘鐢宠 Response VO")
+@Data
+public class ErpPurchaseRequestRespVO {
+
+ @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "閲囪喘鐢宠鍗曞彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "CGSQ001")
+ private String no;
+
+ @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ private Integer status;
+
+ @Schema(description = "鐢宠浜虹紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long requestUserId;
+
+ @Schema(description = "鐢宠浜哄鍚�", example = "寮犱笁")
+ private String requestUserName;
+
+ @Schema(description = "鐢宠閮ㄩ棬缂栧彿", example = "1")
+ private Long requestDeptId;
+
+ @Schema(description = "鐢宠閮ㄩ棬鍚嶇О", example = "鐢熶骇閮�")
+ private String requestDeptName;
+
+ @Schema(description = "鐢宠鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime requestTime;
+
+ @Schema(description = "鐢宠鐞嗙敱", example = "鐢熶骇闇�瑕�")
+ private String requestReason;
+
+ @Schema(description = "渚涘簲鍟嗙紪鍙�", example = "1")
+ private Long supplierId;
+
+ @Schema(description = "渚涘簲鍟嗗悕绉�", example = "渚涘簲鍟咥")
+ private String supplierName;
+
+ @Schema(description = "鍚堣鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED)
+ private BigDecimal totalCount;
+
+ @Schema(description = "鍚堣浠锋牸", requiredMode = Schema.RequiredMode.REQUIRED)
+ private BigDecimal totalPrice;
+
+ @Schema(description = "浜у搧鍚嶇О鍒楄〃", example = "浜у搧A,浜у搧B")
+ private String productNames;
+
+ @Schema(description = "鐢熸垚鐨勯噰璐鍗曠紪鍙�", example = "1")
+ private Long orderId;
+
+ @Schema(description = "鐢熸垚鐨勯噰璐鍗曞彿", example = "CGDD001")
+ private String orderNo;
+
+ @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
+ private String fileUrl;
+
+ @Schema(description = "澶囨敞", example = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private LocalDateTime createTime;
+
+ @Schema(description = "鐢宠鏄庣粏鍒楄〃")
+ private java.util.List<Item> items;
+
+ @Schema(description = "鐢宠鏄庣粏椤�")
+ @Data
+ public static class Item {
+
+ @Schema(description = "缂栧彿", example = "1")
+ private Long id;
+
+ @Schema(description = "浜у搧缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long productId;
+
+ @Schema(description = "浜у搧鍚嶇О", example = "浜у搧A")
+ private String productName;
+
+ @Schema(description = "浜у搧鏉$爜", example = "12345678")
+ private String productBarCode;
+
+ @Schema(description = "浜у搧鍗曚綅缂栧彿", example = "1")
+ private Long productUnitId;
+
+ @Schema(description = "浜у搧鍗曚綅鍚嶇О", example = "涓�")
+ private String productUnitName;
+
+ @Schema(description = "浜у搧鍙傝�冨崟浠�", example = "100.00")
+ private BigDecimal productPrice;
+
+ @Schema(description = "鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private BigDecimal count;
+
+ @Schema(description = "鎬讳环", example = "1000.00")
+ private BigDecimal totalPrice;
+
+ @Schema(description = "绋庣巼锛岀櫨鍒嗘瘮", example = "13")
+ private BigDecimal taxPercent;
+
+ @Schema(description = "绋庨", example = "130.00")
+ private BigDecimal taxPrice;
+
+ @Schema(description = "闇�姹傛棩鏈�")
+ private LocalDateTime demandTime;
+
+ @Schema(description = "澶囨敞", example = "澶囨敞")
+ private String remark;
+
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
new file mode 100644
index 0000000..ae5b693
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
@@ -0,0 +1,86 @@
+package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - ERP 閲囪喘鐢宠鏂板/淇敼 Request VO")
+@Data
+public class ErpPurchaseRequestSaveReqVO {
+
+ @Schema(description = "缂栧彿锛堜慨鏀规椂蹇呭~锛�", example = "1")
+ private Long id;
+
+ @Schema(description = "鐢宠鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "鐢宠鏃堕棿涓嶈兘涓虹┖")
+ private LocalDateTime requestTime;
+
+ @Schema(description = "鐢宠鐞嗙敱", example = "鐢熶骇闇�瑕�")
+ private String requestReason;
+
+ @Schema(description = "渚涘簲鍟嗙紪鍙�", example = "1")
+ private Long supplierId;
+
+ @Schema(description = "浼樻儬鐜囷紝鐧惧垎姣�", example = "10")
+ private BigDecimal discountPercent;
+
+ @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
+ private String fileUrl;
+
+ @Schema(description = "澶囨敞", example = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鐢宠鏄庣粏鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "鐢宠鏄庣粏涓嶈兘涓虹┖")
+ @Valid
+ private List<Item> items;
+
+ @Schema(description = "鐢宠鏄庣粏椤�")
+ @Data
+ public static class Item {
+
+ @Schema(description = "缂栧彿锛堜慨鏀规椂蹇呭~锛�", example = "1")
+ private Long id;
+
+ @Schema(description = "浜у搧缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "浜у搧缂栧彿涓嶈兘涓虹┖")
+ private Long productId;
+
+ @Schema(description = "浜у搧鍗曚綅缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "浜у搧鍗曚綅涓嶈兘涓虹┖")
+ private Long productUnitId;
+
+ @Schema(description = "浜у搧鍙傝�冨崟浠�", example = "100.00")
+ private BigDecimal productPrice;
+
+ @Schema(description = "鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @NotNull(message = "鏁伴噺涓嶈兘涓虹┖")
+ private BigDecimal count;
+
+ @Schema(description = "绋庣巼锛岀櫨鍒嗘瘮", example = "13")
+ private BigDecimal taxPercent;
+
+ @Schema(description = "闇�姹傛棩鏈�")
+ private LocalDateTime demandTime;
+
+ @Schema(description = "澶囨敞", example = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "浜у搧鍚嶇О锛堝墠绔睍绀虹敤锛屽悗绔拷鐣ワ級", example = "浜у搧A")
+ private String productName;
+
+ @Schema(description = "浜у搧鏉$爜锛堝墠绔睍绀虹敤锛屽悗绔拷鐣ワ級", example = "12345678")
+ private String productBarCode;
+
+ @Schema(description = "浜у搧鍗曚綅鍚嶇О锛堝墠绔睍绀虹敤锛屽悗绔拷鐣ワ級", example = "涓�")
+ private String productUnitName;
+
+ }
+
+}
\ No newline at end of file
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 6e6b9d4..88a67a8 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
@@ -119,4 +119,9 @@
*/
private Integer inStatus;
+ /**
+ * BPM 娴佺▼瀹炰緥缂栧彿
+ */
+ private String processInstanceId;
+
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java
index f20b148..717fd49 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java
@@ -89,4 +89,10 @@
*/
private BigDecimal returnCount;
+ // ========== 璐ㄦ ==========
+ /**
+ * 鏄惁闇�瑕佹潵鏂欐楠�
+ */
+ private Boolean qcCheckFlag;
+
}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java
new file mode 100644
index 0000000..ee52968
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java
@@ -0,0 +1,127 @@
+package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * ERP 閲囪喘鐢宠 DO
+ */
+@TableName("erp_purchase_request")
+@KeySequence("erp_purchase_request_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ErpPurchaseRequestDO extends BaseDO {
+
+ /**
+ * 缂栧彿
+ */
+ @TableId
+ private Long id;
+
+ /**
+ * 閲囪喘鐢宠鍗曞彿
+ */
+ private String no;
+
+ /**
+ * 鐘舵��
+ *
+ * 0: 鏈彁浜�
+ * 10: 瀹℃壒涓�
+ * 20: 瀹℃牳閫氳繃
+ * 30: 瀹℃牳涓嶉�氳繃
+ * 40: 宸插彇娑�
+ */
+ private Integer status;
+
+ /**
+ * 鐢宠浜虹紪鍙�
+ */
+ private Long requestUserId;
+
+ /**
+ * 鐢宠閮ㄩ棬缂栧彿
+ */
+ private Long requestDeptId;
+
+ /**
+ * 鐢宠鏃堕棿
+ */
+ private LocalDateTime requestTime;
+
+ /**
+ * 鐢宠鐞嗙敱
+ */
+ private String requestReason;
+
+ /**
+ * 渚涘簲鍟嗙紪鍙凤紙鍙�夛級
+ */
+ private Long supplierId;
+
+ /**
+ * 鍚堣鏁伴噺
+ */
+ private BigDecimal totalCount;
+
+ /**
+ * 鍚堣浠锋牸
+ */
+ private BigDecimal totalPrice;
+
+ /**
+ * 鍚堣浜у搧浠锋牸
+ */
+ private BigDecimal totalProductPrice;
+
+ /**
+ * 鍚堣绋庨
+ */
+ private BigDecimal totalTaxPrice;
+
+ /**
+ * 浼樻儬鐜囷紝鐧惧垎姣�
+ */
+ private BigDecimal discountPercent;
+
+ /**
+ * 浼樻儬閲戦
+ */
+ private BigDecimal discountPrice;
+
+ /**
+ * 闄勪欢鍦板潃
+ */
+ private String fileUrl;
+
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+
+ /**
+ * BPM 娴佺▼瀹炰緥缂栧彿
+ */
+ private String processInstanceId;
+
+ /**
+ * 鐢熸垚鐨勯噰璐鍗曠紪鍙�
+ */
+ private Long orderId;
+
+ /**
+ * 鐢熸垚鐨勯噰璐鍗曞彿
+ */
+ private String orderNo;
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestItemDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestItemDO.java
new file mode 100644
index 0000000..1b88736
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestItemDO.java
@@ -0,0 +1,81 @@
+package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * ERP 閲囪喘鐢宠鏄庣粏 DO
+ */
+@TableName("erp_purchase_request_items")
+@KeySequence("erp_purchase_request_items_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ErpPurchaseRequestItemDO extends BaseDO {
+
+ /**
+ * 缂栧彿
+ */
+ @TableId
+ private Long id;
+
+ /**
+ * 閲囪喘鐢宠缂栧彿
+ */
+ private Long requestId;
+
+ /**
+ * 浜у搧缂栧彿
+ */
+ private Long productId;
+
+ /**
+ * 浜у搧鍗曚綅缂栧彿
+ */
+ private Long productUnitId;
+
+ /**
+ * 浜у搧鍙傝�冨崟浠�
+ */
+ private BigDecimal productPrice;
+
+ /**
+ * 鏁伴噺
+ */
+ private BigDecimal count;
+
+ /**
+ * 鎬讳环
+ */
+ private BigDecimal totalPrice;
+
+ /**
+ * 绋庣巼锛岀櫨鍒嗘瘮
+ */
+ private BigDecimal taxPercent;
+
+ /**
+ * 绋庨
+ */
+ private BigDecimal taxPrice;
+
+ /**
+ * 闇�姹傛棩鏈�
+ */
+ private LocalDateTime demandTime;
+
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestItemMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestItemMapper.java
new file mode 100644
index 0000000..fe00f73
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestItemMapper.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.erp.dal.mysql.purchase;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * ERP 閲囪喘鐢宠鏄庣粏 Mapper
+ */
+@Mapper
+public interface ErpPurchaseRequestItemMapper extends BaseMapperX<ErpPurchaseRequestItemDO> {
+
+ default List<ErpPurchaseRequestItemDO> selectListByRequestId(Long requestId) {
+ return selectList(ErpPurchaseRequestItemDO::getRequestId, requestId);
+ }
+
+ default List<ErpPurchaseRequestItemDO> selectListByRequestIds(Collection<Long> requestIds) {
+ return selectList(ErpPurchaseRequestItemDO::getRequestId, requestIds);
+ }
+
+ default void deleteByRequestId(Long requestId) {
+ delete(new LambdaQueryWrapperX<ErpPurchaseRequestItemDO>().eq(ErpPurchaseRequestItemDO::getRequestId, requestId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestMapper.java
new file mode 100644
index 0000000..9fbd884
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseRequestMapper.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.erp.dal.mysql.purchase;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestPageReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * ERP 閲囪喘鐢宠 Mapper
+ */
+@Mapper
+public interface ErpPurchaseRequestMapper extends BaseMapperX<ErpPurchaseRequestDO> {
+
+ default ErpPurchaseRequestDO selectByNo(String no) {
+ return selectOne(ErpPurchaseRequestDO::getNo, no);
+ }
+
+ default List<ErpPurchaseRequestDO> selectRequestListByIds(Collection<Long> ids) {
+ return selectList(ErpPurchaseRequestDO::getId, ids);
+ }
+
+ default List<ErpPurchaseRequestDO> selectList(Long supplierId, Integer status) {
+ return selectList(new LambdaQueryWrapperX<ErpPurchaseRequestDO>()
+ .eqIfPresent(ErpPurchaseRequestDO::getSupplierId, supplierId)
+ .eqIfPresent(ErpPurchaseRequestDO::getStatus, status));
+ }
+
+ default PageResult<ErpPurchaseRequestDO> selectPage(ErpPurchaseRequestPageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX<ErpPurchaseRequestDO>()
+ .likeIfPresent(ErpPurchaseRequestDO::getNo, pageReqVO.getNo())
+ .eqIfPresent(ErpPurchaseRequestDO::getRequestUserId, pageReqVO.getRequestUserId())
+ .eqIfPresent(ErpPurchaseRequestDO::getRequestDeptId, pageReqVO.getRequestDeptId())
+ .eqIfPresent(ErpPurchaseRequestDO::getSupplierId, pageReqVO.getSupplierId())
+ .eqIfPresent(ErpPurchaseRequestDO::getStatus, pageReqVO.getStatus())
+ .betweenIfPresent(ErpPurchaseRequestDO::getRequestTime, pageReqVO.getRequestTime())
+ .orderByDesc(ErpPurchaseRequestDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
index 463b47c..fa36e91 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
@@ -30,6 +30,11 @@
public static final String PURCHASE_ORDER_NO_PREFIX = "CGDD";
/**
+ * 閲囪喘鐢宠 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO}
+ */
+ public static final String PURCHASE_REQUEST_NO_PREFIX = "CGSQ";
+
+ /**
* 浠樻鍗� {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO}
*/
public static final String FINANCE_PAYMENT_NO_PREFIX = "FKD";
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
index 81412f1..32aae4f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
@@ -17,7 +17,8 @@
@Getter
public enum ErpAuditStatus implements ArrayValuable<Integer> {
- PROCESS(10, "鏈鏍�"), // 瀹℃牳涓�
+ PROCESS(10, "鏈鏍�"), // 鏈鏍�/鑽夌
+ APPROVING(15, "瀹℃壒涓�"), // 瀹℃壒涓�
APPROVE(20, "宸插鏍�"), // 瀹℃牳閫氳繃
CANCEL(30, "宸蹭綔搴�"); // 宸蹭綔搴�/宸叉挙閿�
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseRequestStatusEnum.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseRequestStatusEnum.java
new file mode 100644
index 0000000..6eb1ad3
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchaseRequestStatusEnum.java
@@ -0,0 +1,38 @@
+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 閲囪喘鐢宠鐘舵�佹灇涓�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum ErpPurchaseRequestStatusEnum implements ArrayValuable<Integer> {
+
+ DRAFT(0, "鏈彁浜�"),
+ PROCESS(10, "瀹℃壒涓�"),
+ APPROVE(20, "瀹℃牳閫氳繃"),
+ REJECT(30, "瀹℃牳涓嶉�氳繃"),
+ CANCEL(40, "宸插彇娑�");
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpPurchaseRequestStatusEnum::getStatus).toArray(Integer[]::new);
+
+ /**
+ * 鐘舵��
+ */
+ private final Integer status;
+ /**
+ * 鐘舵�佸悕
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
index 7294529..f965eed 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -27,6 +27,7 @@
ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_030_101_010, "鍙嶅鏍稿け璐ワ紝宸插瓨鍦ㄥ搴旂殑閲囪喘閫�璐у崟");
ErrorCode PURCHASE_ORDER_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_101_011, "閲囪喘瀹℃牳鍒嗙被涓嶅瓨鍦紝璇峰厛鍦˙PM妯″潡鍒涘缓閲囪喘瀹℃牳娴佺▼鍒嗙被");
ErrorCode PURCHASE_ORDER_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_101_012, "閲囪喘瀹℃牳鍒嗙被涓嬫病鏈夊彲鐢ㄧ殑娴佺▼妯″瀷锛岃鍏堝垱寤哄苟鍙戝竷娴佺▼");
+ ErrorCode PURCHASE_ORDER_SUBMIT_FAIL_STATUS = new ErrorCode(1_030_101_013, "鎻愪氦瀹℃壒澶辫触锛屽彧鏈夋湭瀹℃牳鐘舵�佺殑閲囪喘璁㈠崟鎵嶈兘鎻愪氦");
// ========== ERP 閲囪喘鍏ュ簱锛�1-030-102-000锛� ==========
ErrorCode PURCHASE_IN_NOT_EXISTS = new ErrorCode(1_030_102_000, "閲囪喘鍏ュ簱鍗曚笉瀛樺湪");
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 0fe2151..a5fd3ad 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
@@ -43,6 +43,30 @@
void updatePurchaseOrderStatus(Long id, Integer status);
/**
+ * 鎻愪氦閲囪喘璁㈠崟瀹℃壒
+ *
+ * @param id 缂栧彿
+ * @param processDefinitionKey 娴佺▼瀹氫箟 Key
+ * @param userId 鐢ㄦ埛缂栧彿
+ */
+ void submitPurchaseOrder(Long id, String processDefinitionKey, Long userId);
+
+ /**
+ * 鑾峰彇閲囪喘璁㈠崟瀹℃壒娴佺▼鍒楄〃
+ *
+ * @return 娴佺▼瀹氫箟鍒楄〃
+ */
+ List<Map<String, Object>> getPurchaseOrderApproveProcessDefinitionList();
+
+ /**
+ * 鏇存柊閲囪喘璁㈠崟瀹℃壒缁撴灉锛圔PM 鍥炶皟锛�
+ *
+ * @param id 缂栧彿
+ * @param bpmResult BPM 瀹℃壒缁撴灉
+ */
+ void updatePurchaseOrderAuditStatus(Long id, Integer bpmResult);
+
+ /**
* 鏇存柊閲囪喘璁㈠崟鐨勫叆搴撴暟閲�
*
* @param id 缂栧彿
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 ec5cd18..e013f25 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
@@ -5,6 +5,13 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
@@ -16,6 +23,8 @@
import cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderInStatusEnum;
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -34,7 +43,13 @@
*/
@Service
@Validated
+@Slf4j
public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {
+
+ /**
+ * BPM 閲囪喘璁㈠崟瀹℃壒鍒嗙被缂栫爜
+ */
+ private static final String PURCHASE_ORDER_APPROVE_CATEGORY_CODE = "purchase_request_approve";
@Resource
private ErpPurchaseOrderMapper purchaseOrderMapper;
@@ -48,6 +63,13 @@
private ErpSupplierService supplierService;
@Resource
private ErpAccountService accountService;
+
+ @Resource
+ private BpmProcessInstanceApi processInstanceApi;
+ @Resource
+ private BpmCategoryService bpmCategoryService;
+ @Resource
+ private BpmProcessDefinitionService bpmProcessDefinitionService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -66,9 +88,10 @@
throw exception(PURCHASE_ORDER_NO_EXISTS);
}
- // 2.1 鎻掑叆璁㈠崟锛堥噰璐鍗曚笉闇�瑕佸鎵癸紝鐩存帴涓哄凡瀹℃牳鐘舵�侊級
+ // 2.1 鎻掑叆璁㈠崟锛堥噰璐鍗曞垱寤哄悗涓烘湭瀹℃牳鐘舵�侊紝闇�瑕佹彁浜ゅ鎵癸級
ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in
- .setNo(no).setStatus(ErpAuditStatus.APPROVE.getStatus()));
+ .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())
+ .setInCount(BigDecimal.ZERO).setInStatus(ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus()));
calculateTotalPrice(purchaseOrder, purchaseOrderItems);
purchaseOrderMapper.insert(purchaseOrder);
// 2.2 鎻掑叆璁㈠崟椤�
@@ -119,8 +142,6 @@
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePurchaseOrderStatus(Long id, Integer status) {
- // 閲囪喘璁㈠崟涓嶅啀鏀寔鐘舵�佹祦杞紙涓嶉渶瑕佸鎵癸級
- // 姝ゆ柟娉曚繚鐣欑敤浜庣壒娈婃儏鍐碉紙濡傚弽瀹℃牳鍚庨噸鏂板鏍革級
boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
// 1.1 鏍¢獙瀛樺湪
ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
@@ -147,6 +168,109 @@
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void submitPurchaseOrder(Long id, String processDefinitionKey, Long userId) {
+ // 1. 鏍¢獙瀛樺湪 + 鏈鏍哥姸鎬�
+ ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
+ if (!ErpAuditStatus.PROCESS.getStatus().equals(purchaseOrder.getStatus())) {
+ throw exception(PURCHASE_ORDER_SUBMIT_FAIL_STATUS);
+ }
+
+ // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥
+ String processInstanceId = processInstanceApi.createProcessInstance(userId,
+ new BpmProcessInstanceCreateReqDTO()
+ .setProcessDefinitionKey(processDefinitionKey)
+ .setBusinessKey(String.valueOf(id)));
+
+ // 3. 鏇存柊閲囪喘璁㈠崟鐘舵�佷负瀹℃壒涓�
+ purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
+ .setId(id)
+ .setStatus(ErpAuditStatus.APPROVING.getStatus())
+ .setProcessInstanceId(processInstanceId));
+ }
+
+ @Override
+ public List<Map<String, Object>> getPurchaseOrderApproveProcessDefinitionList() {
+ // 1. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ validatePurchaseOrderApproveCategoryAndProcessDefinition();
+
+ // 2. 鑾峰彇鍒嗙被涓嬬殑娴佺▼瀹氫箟淇℃伅
+ List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
+ .getProcessDefinitionInfoListByCategory(PURCHASE_ORDER_APPROVE_CATEGORY_CODE);
+ if (CollUtil.isEmpty(definitionInfoList)) {
+ return Collections.emptyList();
+ }
+
+ // 3. 閬嶅巻鑾峰彇娴佺▼瀹氫箟璇︽儏锛岃繃婊ゆ縺娲荤姸鎬侊紝淇濈暀鏈�鏂扮増鏈�
+ Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
+ for (BpmProcessDefinitionInfoDO info : definitionInfoList) {
+ ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId());
+ if (pd == null || pd.isSuspended()) {
+ continue;
+ }
+ ProcessDefinition existing = latestVersionMap.get(pd.getKey());
+ if (existing == null || pd.getVersion() > existing.getVersion()) {
+ latestVersionMap.put(pd.getKey(), pd);
+ }
+ }
+
+ // 4. 杩斿洖娴佺▼瀹氫箟鍒楄〃
+ List<Map<String, Object>> result = new ArrayList<>();
+ for (ProcessDefinition pd : latestVersionMap.values()) {
+ Map<String, Object> item = new HashMap<>();
+ item.put("id", pd.getId());
+ item.put("key", pd.getKey());
+ item.put("name", pd.getName());
+ result.add(item);
+ }
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updatePurchaseOrderAuditStatus(Long id, Integer bpmResult) {
+ // 1. 鏍¢獙瀛樺湪
+ ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
+ if (!ErpAuditStatus.APPROVING.getStatus().equals(purchaseOrder.getStatus())) {
+ log.warn("[updatePurchaseOrderAuditStatus] 閲囪喘璁㈠崟({}) 涓嶅浜庡鎵逛腑鐘舵��", id);
+ return;
+ }
+
+ // 2. 鏍规嵁瀹℃壒缁撴灉鏇存柊鐘舵��
+ Integer newStatus = convertBpmResultToAuditStatus(bpmResult);
+ if (newStatus != null) {
+ purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
+ .setId(id).setStatus(newStatus));
+ }
+ }
+
+ /**
+ * 鏍¢獙閲囪喘璁㈠崟瀹℃壒鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ */
+ private void validatePurchaseOrderApproveCategoryAndProcessDefinition() {
+ // 1. 鏍¢獙鍒嗙被鏄惁瀛樺湪
+ List<BpmCategoryDO> categories = bpmCategoryService.getCategoryListByCode(
+ Collections.singletonList(PURCHASE_ORDER_APPROVE_CATEGORY_CODE));
+ if (CollUtil.isEmpty(categories)) {
+ throw exception(PURCHASE_ORDER_BPM_CATEGORY_NOT_EXISTS);
+ }
+ }
+
+ /**
+ * 杞崲 BPM 瀹℃壒缁撴灉涓哄鏍哥姸鎬�
+ */
+ private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
+ if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
+ return ErpAuditStatus.APPROVE.getStatus();
+ } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
+ return ErpAuditStatus.PROCESS.getStatus();
+ } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
+ return ErpAuditStatus.CANCEL.getStatus();
+ }
+ return null;
+ }
+
private List<ErpPurchaseOrderItemDO> validatePurchaseOrderItems(List<ErpPurchaseOrderSaveReqVO.Item> list) {
// 杞寲涓� ErpPurchaseOrderItemDO 鍒楄〃锛堜骇鍝侀獙璇佸凡绉婚櫎锛�
return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestService.java
new file mode 100644
index 0000000..a761923
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestService.java
@@ -0,0 +1,114 @@
+package cn.iocoder.yudao.module.erp.service.purchase;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestSaveReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ERP 閲囪喘鐢宠 Service 鎺ュ彛
+ */
+public interface ErpPurchaseRequestService {
+
+ /**
+ * 鍒涘缓閲囪喘鐢宠
+ *
+ * @param createReqVO 鍒涘缓淇℃伅
+ * @return 缂栧彿
+ */
+ Long createPurchaseRequest(@Valid ErpPurchaseRequestSaveReqVO createReqVO);
+
+ /**
+ * 鏇存柊閲囪喘鐢宠
+ *
+ * @param updateReqVO 鏇存柊淇℃伅
+ */
+ void updatePurchaseRequest(@Valid ErpPurchaseRequestSaveReqVO updateReqVO);
+
+ /**
+ * 鍒犻櫎閲囪喘鐢宠
+ *
+ * @param ids 缂栧彿鏁扮粍
+ */
+ void deletePurchaseRequest(List<Long> ids);
+
+ /**
+ * 鑾峰緱閲囪喘鐢宠
+ *
+ * @param id 缂栧彿
+ * @return 閲囪喘鐢宠
+ */
+ ErpPurchaseRequestDO getPurchaseRequest(Long id);
+
+ /**
+ * 鑾峰緱閲囪喘鐢宠鍒嗛〉
+ *
+ * @param pageReqVO 鍒嗛〉鏌ヨ
+ * @return 閲囪喘鐢宠鍒嗛〉
+ */
+ PageResult<ErpPurchaseRequestDO> getPurchaseRequestPage(ErpPurchaseRequestPageReqVO pageReqVO);
+
+ /**
+ * 鑾峰緱閲囪喘鐢宠椤瑰垪琛�
+ *
+ * @param requestId 閲囪喘鐢宠缂栧彿
+ * @return 閲囪喘鐢宠椤瑰垪琛�
+ */
+ List<ErpPurchaseRequestItemDO> getPurchaseRequestItemListByRequestId(Long requestId);
+
+ /**
+ * 鑾峰緱閲囪喘鐢宠椤� List
+ *
+ * @param requestIds 閲囪喘鐢宠缂栧彿鏁扮粍
+ * @return 閲囪喘鐢宠椤� List
+ */
+ List<ErpPurchaseRequestItemDO> getPurchaseRequestItemListByRequestIds(Collection<Long> requestIds);
+
+ /**
+ * 鎻愪氦閲囪喘鐢宠瀹℃壒
+ *
+ * @param id 缂栧彿
+ * @param processDefinitionKey 娴佺▼瀹氫箟 Key
+ * @param userId 鐢ㄦ埛缂栧彿
+ */
+ void submitPurchaseRequest(Long id, String processDefinitionKey, Long userId);
+
+ /**
+ * 鑾峰彇閲囪喘鐢宠瀹℃壒娴佺▼鍒楄〃
+ *
+ * @return 娴佺▼瀹氫箟鍒楄〃
+ */
+ List<Map<String, Object>> getPurchaseRequestApproveProcessDefinitionList();
+
+ /**
+ * 鏇存柊閲囪喘鐢宠瀹℃壒缁撴灉锛圔PM 鍥炶皟锛�
+ *
+ * @param id 缂栧彿
+ * @param bpmResult BPM 瀹℃壒缁撴灉
+ */
+ void updatePurchaseRequestAuditStatus(Long id, Integer bpmResult);
+
+ /**
+ * 鐢熸垚閲囪喘璁㈠崟
+ *
+ * @param id 閲囪喘鐢宠缂栧彿
+ * @param supplierId 渚涘簲鍟嗙紪鍙�
+ * @return 閲囪喘璁㈠崟缂栧彿
+ */
+ Long generatePurchaseOrder(Long id, Long supplierId);
+
+ /**
+ * 鏍规嵁缂栧彿鍒楄〃鑾峰彇閲囪喘鐢宠鍒楄〃
+ *
+ * @param ids 缂栧彿鍒楄〃
+ * @return 閲囪喘鐢宠鍒楄〃
+ */
+ List<ErpPurchaseRequestDO> getPurchaseRequestList(List<Long> ids);
+
+}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java
new file mode 100644
index 0000000..1811e98
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java
@@ -0,0 +1,430 @@
+package cn.iocoder.yudao.module.erp.service.purchase;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestSaveReqVO;
+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.dal.dataobject.purchase.ErpPurchaseRequestDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO;
+import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderItemMapper;
+import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;
+import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseRequestItemMapper;
+import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseRequestMapper;
+import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
+import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
+import cn.iocoder.yudao.module.erp.enums.ErpPurchaseRequestStatusEnum;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
+
+/**
+ * ERP 閲囪喘鐢宠 Service 瀹炵幇绫�
+ */
+@Service
+@Validated
+@Slf4j
+public class ErpPurchaseRequestServiceImpl implements ErpPurchaseRequestService {
+
+ /**
+ * BPM 閲囪喘鐢宠瀹℃壒鍒嗙被缂栫爜
+ */
+ private static final String PURCHASE_REQUEST_APPROVE_CATEGORY_CODE = "purchase_request_approve";
+
+ @Resource
+ private ErpPurchaseRequestMapper purchaseRequestMapper;
+ @Resource
+ private ErpPurchaseRequestItemMapper purchaseRequestItemMapper;
+
+ @Resource
+ private ErpPurchaseOrderMapper purchaseOrderMapper;
+ @Resource
+ private ErpPurchaseOrderItemMapper purchaseOrderItemMapper;
+
+ @Resource
+ private ErpNoRedisDAO noRedisDAO;
+
+ @Resource
+ private ErpSupplierService supplierService;
+
+ @Resource
+ private BpmProcessInstanceApi processInstanceApi;
+ @Resource
+ private BpmCategoryService bpmCategoryService;
+ @Resource
+ private BpmProcessDefinitionService bpmProcessDefinitionService;
+
+ @Resource
+ private AdminUserApi adminUserApi;
+ @Resource
+ private DeptApi deptApi;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createPurchaseRequest(ErpPurchaseRequestSaveReqVO createReqVO) {
+ // 1. 鏍¢獙璁㈠崟椤圭殑鏈夋晥鎬�
+ List<ErpPurchaseRequestItemDO> requestItems = validatePurchaseRequestItems(createReqVO.getItems());
+ // 2. 鏍¢獙渚涘簲鍟嗭紙濡傛灉鎸囧畾锛�
+ if (createReqVO.getSupplierId() != null) {
+ supplierService.validateSupplier(createReqVO.getSupplierId());
+ }
+ // 3. 鐢熸垚鐢宠鍗曞彿
+ String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_REQUEST_NO_PREFIX);
+ if (purchaseRequestMapper.selectByNo(no) != null) {
+ throw exception(PURCHASE_REQUEST_NO_EXISTS);
+ }
+
+ // 4. 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅
+ Long userId = Long.parseLong(getCurrentUserId());
+ AdminUserRespDTO user = adminUserApi.getUser(userId);
+ Long deptId = user != null ? user.getDeptId() : null;
+
+ // 5. 鎻掑叆鐢宠鍗�
+ ErpPurchaseRequestDO purchaseRequest = BeanUtils.toBean(createReqVO, ErpPurchaseRequestDO.class, req -> req
+ .setNo(no)
+ .setStatus(ErpPurchaseRequestStatusEnum.DRAFT.getStatus())
+ .setRequestUserId(userId)
+ .setRequestDeptId(deptId));
+ calculateTotalPrice(purchaseRequest, requestItems);
+ purchaseRequestMapper.insert(purchaseRequest);
+
+ // 6. 鎻掑叆鐢宠鏄庣粏
+ requestItems.forEach(o -> o.setRequestId(purchaseRequest.getId()));
+ purchaseRequestItemMapper.insertBatch(requestItems);
+
+ return purchaseRequest.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updatePurchaseRequest(ErpPurchaseRequestSaveReqVO updateReqVO) {
+ // 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
+ ErpPurchaseRequestDO purchaseRequest = validatePurchaseRequestExistsAndDraft(updateReqVO.getId());
+ // 2. 鏍¢獙渚涘簲鍟嗭紙濡傛灉鎸囧畾锛�
+ if (updateReqVO.getSupplierId() != null) {
+ supplierService.validateSupplier(updateReqVO.getSupplierId());
+ }
+ // 3. 鏍¢獙璁㈠崟椤圭殑鏈夋晥鎬�
+ List<ErpPurchaseRequestItemDO> requestItems = validatePurchaseRequestItems(updateReqVO.getItems());
+
+ // 4. 鏇存柊鐢宠鍗�
+ ErpPurchaseRequestDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseRequestDO.class);
+ calculateTotalPrice(updateObj, requestItems);
+ purchaseRequestMapper.updateById(updateObj);
+
+ // 5. 鏇存柊鐢宠鏄庣粏
+ updatePurchaseRequestItemList(updateReqVO.getId(), requestItems);
+ }
+
+ private void calculateTotalPrice(ErpPurchaseRequestDO purchaseRequest, List<ErpPurchaseRequestItemDO> requestItems) {
+ purchaseRequest.setTotalCount(getSumValue(requestItems, ErpPurchaseRequestItemDO::getCount, BigDecimal::add));
+ purchaseRequest.setTotalProductPrice(getSumValue(requestItems, ErpPurchaseRequestItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
+ purchaseRequest.setTotalTaxPrice(getSumValue(requestItems, ErpPurchaseRequestItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
+ purchaseRequest.setTotalPrice(purchaseRequest.getTotalProductPrice().add(purchaseRequest.getTotalTaxPrice()));
+ // 璁$畻浼樻儬浠锋牸
+ if (purchaseRequest.getDiscountPercent() == null) {
+ purchaseRequest.setDiscountPercent(BigDecimal.ZERO);
+ }
+ purchaseRequest.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseRequest.getTotalPrice(), purchaseRequest.getDiscountPercent()));
+ purchaseRequest.setTotalPrice(purchaseRequest.getTotalPrice().subtract(purchaseRequest.getDiscountPrice()));
+ }
+
+ private List<ErpPurchaseRequestItemDO> validatePurchaseRequestItems(List<ErpPurchaseRequestSaveReqVO.Item> list) {
+ return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseRequestItemDO.class, item -> {
+ if (item.getProductPrice() != null && item.getCount() != null) {
+ item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));
+ if (item.getTaxPercent() != null) {
+ item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
+ }
+ }
+ }));
+ }
+
+ private void updatePurchaseRequestItemList(Long id, List<ErpPurchaseRequestItemDO> newList) {
+ List<ErpPurchaseRequestItemDO> oldList = purchaseRequestItemMapper.selectListByRequestId(id);
+ List<List<ErpPurchaseRequestItemDO>> diffList = diffList(oldList, newList,
+ (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
+
+ if (CollUtil.isNotEmpty(diffList.get(0))) {
+ diffList.get(0).forEach(o -> o.setRequestId(id));
+ purchaseRequestItemMapper.insertBatch(diffList.get(0));
+ }
+ if (CollUtil.isNotEmpty(diffList.get(1))) {
+ purchaseRequestItemMapper.updateBatch(diffList.get(1));
+ }
+ if (CollUtil.isNotEmpty(diffList.get(2))) {
+ purchaseRequestItemMapper.deleteByIds(convertList(diffList.get(2), ErpPurchaseRequestItemDO::getId));
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deletePurchaseRequest(List<Long> ids) {
+ List<ErpPurchaseRequestDO> purchaseRequests = purchaseRequestMapper.selectRequestListByIds(ids);
+ if (CollUtil.isEmpty(purchaseRequests)) {
+ return;
+ }
+ purchaseRequests.forEach(purchaseRequest -> {
+ // 鍙湁鑽夌鐘舵�佹墠鑳藉垹闄�
+ if (!ErpPurchaseRequestStatusEnum.DRAFT.getStatus().equals(purchaseRequest.getStatus())) {
+ throw exception(PURCHASE_REQUEST_DELETE_FAIL_NOT_DRAFT, purchaseRequest.getNo());
+ }
+ });
+
+ purchaseRequests.forEach(purchaseRequest -> {
+ purchaseRequestMapper.deleteById(purchaseRequest.getId());
+ purchaseRequestItemMapper.deleteByRequestId(purchaseRequest.getId());
+ });
+ }
+
+ @Override
+ public ErpPurchaseRequestDO getPurchaseRequest(Long id) {
+ return purchaseRequestMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult<ErpPurchaseRequestDO> getPurchaseRequestPage(ErpPurchaseRequestPageReqVO pageReqVO) {
+ return purchaseRequestMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public List<ErpPurchaseRequestItemDO> getPurchaseRequestItemListByRequestId(Long requestId) {
+ return purchaseRequestItemMapper.selectListByRequestId(requestId);
+ }
+
+ @Override
+ public List<ErpPurchaseRequestItemDO> getPurchaseRequestItemListByRequestIds(Collection<Long> requestIds) {
+ if (CollUtil.isEmpty(requestIds)) {
+ return Collections.emptyList();
+ }
+ return purchaseRequestItemMapper.selectListByRequestIds(requestIds);
+ }
+
+ @Override
+ public List<ErpPurchaseRequestDO> getPurchaseRequestList(List<Long> ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return Collections.emptyList();
+ }
+ return purchaseRequestMapper.selectRequestListByIds(ids);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void submitPurchaseRequest(Long id, String processDefinitionKey, Long userId) {
+ // 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
+ ErpPurchaseRequestDO purchaseRequest = validatePurchaseRequestExistsAndDraft(id);
+
+ // 2. 鏍¢獙鏄惁鏈夋槑缁�
+ List<ErpPurchaseRequestItemDO> items = purchaseRequestItemMapper.selectListByRequestId(id);
+ if (CollUtil.isEmpty(items)) {
+ throw exception(PURCHASE_REQUEST_NOT_EXISTS); // TODO: 娣诲姞鏄庣粏涓虹┖鐨勯敊璇爜
+ }
+
+ // 3. 鍒涘缓 BPM 娴佺▼瀹炰緥
+ String processInstanceId = processInstanceApi.createProcessInstance(userId,
+ new BpmProcessInstanceCreateReqDTO()
+ .setProcessDefinitionKey(processDefinitionKey)
+ .setBusinessKey(String.valueOf(id)));
+
+ // 4. 鏇存柊閲囪喘鐢宠鐘舵�佷负瀹℃壒涓�
+ purchaseRequestMapper.updateById(new ErpPurchaseRequestDO()
+ .setId(id)
+ .setStatus(ErpPurchaseRequestStatusEnum.PROCESS.getStatus())
+ .setProcessInstanceId(processInstanceId));
+ }
+
+ @Override
+ public List<Map<String, Object>> getPurchaseRequestApproveProcessDefinitionList() {
+ // 1. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ validatePurchaseRequestApproveCategoryAndProcessDefinition();
+
+ // 2. 鑾峰彇鍒嗙被涓嬬殑娴佺▼瀹氫箟淇℃伅
+ List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
+ .getProcessDefinitionInfoListByCategory(PURCHASE_REQUEST_APPROVE_CATEGORY_CODE);
+ if (CollUtil.isEmpty(definitionInfoList)) {
+ return Collections.emptyList();
+ }
+
+ // 3. 閬嶅巻鑾峰彇娴佺▼瀹氫箟璇︽儏锛岃繃婊ゆ縺娲荤姸鎬侊紝淇濈暀鏈�鏂扮増鏈�
+ Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
+ for (BpmProcessDefinitionInfoDO info : definitionInfoList) {
+ ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId());
+ if (pd == null || pd.isSuspended()) {
+ continue;
+ }
+ ProcessDefinition existing = latestVersionMap.get(pd.getKey());
+ if (existing == null || pd.getVersion() > existing.getVersion()) {
+ latestVersionMap.put(pd.getKey(), pd);
+ }
+ }
+
+ // 4. 杩斿洖娴佺▼瀹氫箟鍒楄〃
+ List<Map<String, Object>> result = new ArrayList<>();
+ for (ProcessDefinition pd : latestVersionMap.values()) {
+ Map<String, Object> item = new HashMap<>();
+ item.put("id", pd.getId());
+ item.put("key", pd.getKey());
+ item.put("name", pd.getName());
+ result.add(item);
+ }
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updatePurchaseRequestAuditStatus(Long id, Integer bpmResult) {
+ // 1. 鏍¢獙瀛樺湪
+ ErpPurchaseRequestDO purchaseRequest = validatePurchaseRequestExists(id);
+ if (!ErpPurchaseRequestStatusEnum.PROCESS.getStatus().equals(purchaseRequest.getStatus())) {
+ log.warn("[updatePurchaseRequestAuditStatus] 閲囪喘鐢宠({}) 涓嶅浜庡鎵逛腑鐘舵��", id);
+ return;
+ }
+
+ // 2. 鏍规嵁瀹℃壒缁撴灉鏇存柊鐘舵��
+ Integer newStatus = convertBpmResultToStatus(bpmResult);
+ if (newStatus != null) {
+ purchaseRequestMapper.updateById(new ErpPurchaseRequestDO()
+ .setId(id).setStatus(newStatus));
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long generatePurchaseOrder(Long id, Long supplierId) {
+ // 1. 鏍¢獙瀛樺湪 + 瀹℃牳閫氳繃鐘舵��
+ ErpPurchaseRequestDO purchaseRequest = validatePurchaseRequestExists(id);
+ if (!ErpPurchaseRequestStatusEnum.APPROVE.getStatus().equals(purchaseRequest.getStatus())) {
+ throw exception(PURCHASE_REQUEST_NOT_APPROVE);
+ }
+ // 2. 鏍¢獙鏄惁宸茬敓鎴愯鍗�
+ if (purchaseRequest.getOrderId() != null) {
+ throw exception(PURCHASE_REQUEST_ORDER_EXISTS);
+ }
+ // 3. 鏍¢獙渚涘簲鍟�
+ supplierService.validateSupplier(supplierId);
+
+ // 4. 鑾峰彇鐢宠鏄庣粏
+ List<ErpPurchaseRequestItemDO> requestItems = purchaseRequestItemMapper.selectListByRequestId(id);
+ if (CollUtil.isEmpty(requestItems)) {
+ throw exception(PURCHASE_REQUEST_NOT_EXISTS);
+ }
+
+ // 5. 鐢熸垚閲囪喘璁㈠崟鍙�
+ String orderNo = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX);
+ if (purchaseOrderMapper.selectByNo(orderNo) != null) {
+ throw exception(PURCHASE_ORDER_NO_EXISTS);
+ }
+
+ // 6. 鍒涘缓閲囪喘璁㈠崟
+ ErpPurchaseOrderDO purchaseOrder = new ErpPurchaseOrderDO();
+ purchaseOrder.setNo(orderNo);
+ purchaseOrder.setStatus(ErpAuditStatus.APPROVE.getStatus()); // 閲囪喘璁㈠崟鐩存帴涓哄凡瀹℃牳鐘舵��
+ purchaseOrder.setSupplierId(supplierId);
+ purchaseOrder.setOrderTime(purchaseRequest.getRequestTime());
+ purchaseOrder.setTotalCount(purchaseRequest.getTotalCount());
+ purchaseOrder.setTotalPrice(purchaseRequest.getTotalPrice());
+ purchaseOrder.setTotalProductPrice(purchaseRequest.getTotalProductPrice());
+ purchaseOrder.setTotalTaxPrice(purchaseRequest.getTotalTaxPrice());
+ purchaseOrder.setDiscountPercent(purchaseRequest.getDiscountPercent());
+ purchaseOrder.setDiscountPrice(purchaseRequest.getDiscountPrice());
+ purchaseOrder.setRemark(purchaseRequest.getRemark());
+ purchaseOrder.setInCount(BigDecimal.ZERO);
+ purchaseOrder.setInStatus(0);
+ purchaseOrderMapper.insert(purchaseOrder);
+
+ // 7. 鍒涘缓閲囪喘璁㈠崟鏄庣粏
+ List<ErpPurchaseOrderItemDO> orderItems = convertList(requestItems, item -> {
+ ErpPurchaseOrderItemDO orderItem = new ErpPurchaseOrderItemDO();
+ orderItem.setOrderId(purchaseOrder.getId());
+ orderItem.setProductId(item.getProductId());
+ orderItem.setProductUnitId(item.getProductUnitId());
+ orderItem.setProductPrice(item.getProductPrice());
+ orderItem.setCount(item.getCount());
+ orderItem.setTotalPrice(item.getTotalPrice());
+ orderItem.setTaxPercent(item.getTaxPercent());
+ orderItem.setTaxPrice(item.getTaxPrice());
+ orderItem.setRemark(item.getRemark());
+ orderItem.setInCount(BigDecimal.ZERO);
+ orderItem.setQcCheckFlag(false); // 榛樿涓嶉渶瑕佽川妫�
+ return orderItem;
+ });
+ purchaseOrderItemMapper.insertBatch(orderItems);
+
+ // 8. 鏇存柊閲囪喘鐢宠鐨勮鍗曚俊鎭�
+ purchaseRequestMapper.updateById(new ErpPurchaseRequestDO()
+ .setId(id)
+ .setOrderId(purchaseOrder.getId())
+ .setOrderNo(orderNo));
+
+ return purchaseOrder.getId();
+ }
+
+ /**
+ * 鏍¢獙閲囪喘鐢宠瀹℃壒鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ */
+ private void validatePurchaseRequestApproveCategoryAndProcessDefinition() {
+ List<BpmCategoryDO> categories = bpmCategoryService.getCategoryListByCode(
+ Collections.singletonList(PURCHASE_REQUEST_APPROVE_CATEGORY_CODE));
+ if (CollUtil.isEmpty(categories)) {
+ throw exception(PURCHASE_REQUEST_BPM_CATEGORY_NOT_EXISTS);
+ }
+ }
+
+ /**
+ * 杞崲 BPM 瀹℃壒缁撴灉涓虹姸鎬�
+ */
+ private Integer convertBpmResultToStatus(Integer bpmResult) {
+ if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
+ return ErpPurchaseRequestStatusEnum.APPROVE.getStatus();
+ } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
+ return ErpPurchaseRequestStatusEnum.REJECT.getStatus();
+ } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
+ return ErpPurchaseRequestStatusEnum.CANCEL.getStatus();
+ }
+ return null;
+ }
+
+ private ErpPurchaseRequestDO validatePurchaseRequestExists(Long id) {
+ ErpPurchaseRequestDO purchaseRequest = purchaseRequestMapper.selectById(id);
+ if (purchaseRequest == null) {
+ throw exception(PURCHASE_REQUEST_NOT_EXISTS);
+ }
+ return purchaseRequest;
+ }
+
+ private ErpPurchaseRequestDO validatePurchaseRequestExistsAndDraft(Long id) {
+ ErpPurchaseRequestDO purchaseRequest = validatePurchaseRequestExists(id);
+ if (!ErpPurchaseRequestStatusEnum.DRAFT.getStatus().equals(purchaseRequest.getStatus())) {
+ throw exception(PURCHASE_REQUEST_UPDATE_FAIL_NOT_DRAFT, purchaseRequest.getNo());
+ }
+ return purchaseRequest;
+ }
+
+ private String getCurrentUserId() {
+ return cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId().toString();
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
index e9c80db..20d33fd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
import jakarta.validation.Valid;
import java.util.Collection;
@@ -95,4 +96,15 @@
*/
Long getIqcCountByVendorId(Long vendorId);
+ /**
+ * 鏍规嵁鍒拌揣閫氱煡鍗曡鑷姩鍒涘缓鏉ユ枡妫�楠屽崟
+ *
+ * @param noticeId 鍒拌揣閫氱煡鍗� ID
+ * @param line 鍒拌揣閫氱煡鍗曡
+ * @param noticeCode 鍒拌揣閫氱煡鍗曠紪鍙�
+ * @param vendorId 渚涘簲鍟� ID
+ * @return IQC 妫�楠屽崟 ID
+ */
+ Long createIqcFromArrivalNotice(Long noticeId, MesWmArrivalNoticeLineDO line, String noticeCode, Long vendorId);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
index b6715ff..2b7ddd6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -10,6 +10,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
@@ -308,4 +309,40 @@
return iqcMapper.selectCountByVendorId(vendorId);
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createIqcFromArrivalNotice(Long noticeId, MesWmArrivalNoticeLineDO line, String noticeCode, Long vendorId) {
+ // 1. 鏍¢獙鐗╂枡鏄惁閰嶇疆浜� IQC 妫�楠屾ā鏉�
+ MesQcTemplateItemDO templateItem = templateItemService.getRequiredTemplateByItemIdAndType(
+ line.getItemId(), MesQcTypeEnum.IQC.getType());
+ Long templateId = templateItem.getTemplateId();
+
+ // 2. 鐢熸垚妫�楠屽崟缂栫爜
+ String code = "IQC" + System.currentTimeMillis();
+
+ // 3. 鍒涘缓 IQC 涓昏〃锛堝緟妫�楠岀姸鎬侊紝鍒濆鍚堟牸鏁伴噺涓� 0锛�
+ MesQcIqcDO iqc = new MesQcIqcDO();
+ iqc.setCode(code);
+ iqc.setName(noticeCode + "-鐗╂枡妫�楠�");
+ iqc.setTemplateId(templateId);
+ iqc.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE);
+ iqc.setSourceDocId(noticeId);
+ iqc.setSourceLineId(line.getId());
+ iqc.setSourceDocCode(noticeCode);
+ iqc.setVendorId(vendorId);
+ iqc.setItemId(line.getItemId());
+ iqc.setReceivedQuantity(line.getArrivalQuantity());
+ iqc.setCheckQuantity(BigDecimal.ZERO); // 寰呭~鍐�
+ iqc.setQualifiedQuantity(BigDecimal.ZERO); // 寰呭~鍐�
+ iqc.setUnqualifiedQuantity(BigDecimal.ZERO); // 寰呭~鍐�
+ iqc.setReceiveDate(java.time.LocalDateTime.now());
+ iqc.setStatus(MesQcStatusEnum.DRAFT.getStatus());
+ iqcMapper.insert(iqc);
+
+ // 4. 浠庢ā鏉挎寚鏍囪嚜鍔ㄧ敓鎴愭楠岃
+ iqcLineService.createLinesFromTemplate(iqc.getId(), templateId);
+
+ return iqc.getId();
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineService.java
index 8d68a85..fb87c62 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineService.java
@@ -100,4 +100,12 @@
*/
void createArrivalNoticeLineFromPurchaseOrderItem(MesWmArrivalNoticeLineDO line);
+ /**
+ * 鏇存柊鍒拌揣閫氱煡鍗曡鐨� IQC 妫�楠屽崟 ID
+ *
+ * @param lineId 琛岀紪鍙�
+ * @param iqcId IQC 妫�楠屽崟缂栧彿
+ */
+ void updateArrivalNoticeLineIqcId(Long lineId, Long iqcId);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineServiceImpl.java
index c71717e..b90ea8c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeLineServiceImpl.java
@@ -146,4 +146,10 @@
arrivalNoticeLineMapper.insert(line);
}
+ @Override
+ public void updateArrivalNoticeLineIqcId(Long lineId, Long iqcId) {
+ arrivalNoticeLineMapper.updateById(new MesWmArrivalNoticeLineDO()
+ .setId(lineId).setIqcId(iqcId));
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
index 29db064..2f5f920 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -15,8 +15,10 @@
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -50,6 +52,10 @@
@Resource
private MesMdmItemService mdmItemService;
+
+ @Resource
+ @Lazy
+ private MesQcIqcService iqcService;
@Override
public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) {
@@ -115,7 +121,7 @@
@Transactional(rollbackFor = Exception.class)
public void submitArrivalNotice(Long id) {
// 1.1 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
- validateArrivalNoticeExistsAndDraft(id);
+ MesWmArrivalNoticeDO notice = validateArrivalNoticeExistsAndDraft(id);
// 1.2 妫�鏌ユ槸鍚︽湁琛岄」鐩�
List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id);
if (CollUtil.isEmpty(lines)) {
@@ -126,9 +132,26 @@
boolean needCheck = CollectionUtils.anyMatch(lines,
line -> Boolean.TRUE.equals(line.getIqcCheckFlag()));
if (needCheck) {
- // 闇�瑕佹楠岋紝杩涘叆寰呰川妫�
+ // 2.1 闇�瑕佹楠岋紝杩涘叆寰呰川妫�
arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO()
.setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()));
+ // 2.2 涓洪渶瑕佽川妫�鐨勮鑷姩鍒涘缓 IQC 妫�楠屽崟
+ for (MesWmArrivalNoticeLineDO line : lines) {
+ if (Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null) {
+ try {
+ Long iqcId = iqcService.createIqcFromArrivalNotice(
+ id, line, notice.getCode(), notice.getVendorId());
+ // 鏇存柊琛屼笂鐨� iqcId
+ arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId);
+ log.info("[submitArrivalNotice][鍒拌揣閫氱煡鍗�({}) 琛�({}) 鑷姩鍒涘缓 IQC 妫�楠屽崟({})]",
+ id, line.getId(), iqcId);
+ } catch (Exception e) {
+ log.warn("[submitArrivalNotice][鍒拌揣閫氱煡鍗�({}) 琛�({}) 鍒涘缓 IQC 澶辫触: {}]",
+ id, line.getId(), e.getMessage());
+ // 缁х画澶勭悊鍏朵粬琛岋紝涓嶄腑鏂祦绋�
+ }
+ }
+ }
return;
}
// 涓嶉渶瑕佹楠岋紝鐩存帴杩涘叆寰呭叆搴�
@@ -295,7 +318,8 @@
// 娉ㄦ剰锛氳繖閲岄渶瑕佹牴鎹� ERP 浜у搧 ID 鏄犲皠鍒� MES 鐗╂枡 ID
// 瀹為檯椤圭洰涓渶瑕侀�氳繃鏄犲皠鏈嶅姟澶勭悊锛岃繖閲屾殏鏃剁暀绌鸿鐢ㄦ埛鎵嬪姩閫夋嫨
line.setArrivalQuantity(item.getCount() != null ? item.getCount().subtract(item.getInCount() != null ? item.getInCount() : BigDecimal.ZERO) : BigDecimal.ZERO);
- line.setIqcCheckFlag(false); // 榛樿涓嶉渶瑕佹楠岋紝鐢ㄦ埛鍙慨鏀�
+ // 浠庨噰璐鍗曟槑缁嗗甫鍏ヨ川妫�鏍囪瘑
+ line.setIqcCheckFlag(Boolean.TRUE.equals(item.getQcCheckFlag()));
arrivalNoticeLineService.createArrivalNoticeLineFromPurchaseOrderItem(line);
}
--
Gitblit v1.9.3