From d39e4f7d839167c16425086dfa2f8747bccfdff9 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 05 十一月 2026 17:39:01 +0800
Subject: [PATCH] feat: 打通销售采购财务闭环并完成累计业务优化
---
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java | 131 ++++++++++++++++++++++++++++++++++++++++---
1 files changed, 120 insertions(+), 11 deletions(-)
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 10cef64..e1f91d4 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
@@ -29,7 +29,9 @@
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.ErpPaymentMethodEnum;
import cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderInStatusEnum;
+import cn.iocoder.yudao.module.erp.enums.ErpPurchaseTypeEnum;
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -117,8 +119,16 @@
@Override
@Transactional(rollbackFor = Exception.class)
public Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO) {
+ return createPurchaseOrder(createReqVO, true);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO, boolean submit) {
// 1.1 鏍¢獙璁㈠崟椤圭殑鏈夋晥鎬�
List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(createReqVO.getItems());
+ // 1.1.1 鏍¢獙閲囪喘鍙拌处瀛楁
+ validateLedgerFields(createReqVO);
// 1.2 鏍¢獙渚涘簲鍟�
supplierService.validateSupplier(createReqVO.getSupplierId());
// 1.3 鏍¢獙缁撶畻璐︽埛
@@ -131,9 +141,9 @@
throw exception(PURCHASE_ORDER_NO_EXISTS);
}
- // 2.1 鎻掑叆璁㈠崟锛堥噰璐鍗曞垱寤哄悗涓烘湭瀹℃牳鐘舵�侊紝闇�瑕佹彁浜ゅ鎵癸級
+ // 2.1 鎻掑叆璁㈠崟锛坰ubmit=false 鏃朵繚瀛樹负鑽夌锛岀瓑寰呬笟鍔$‘璁ゅ悗鎵嬪姩鎻愪氦瀹℃壒锛�
ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in
- .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())
+ .setNo(no).setStatus(submit ? ErpAuditStatus.PROCESS.getStatus() : ErpAuditStatus.DRAFT.getStatus())
.setInCount(BigDecimal.ZERO).setInStatus(ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus()));
calculateTotalPrice(purchaseOrder, purchaseOrderItems);
purchaseOrderMapper.insert(purchaseOrder);
@@ -145,7 +155,9 @@
purchaseOrder.getId(), createReqVO.getBlobIds());
// 3. 鍒涘缓鍚庤嚜鍔ㄦ彁浜ゅ鎵瑰埌鍗忓悓鍔炲叕锛堝鎵瑰湪鍗忓悓鍔炲叕瀹屾垚锛屼笉鍦ㄦ湰椤甸潰瀹℃壒锛�
- submitPurchaseOrder(purchaseOrder.getId(), PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY, getLoginUserId());
+ if (submit) {
+ submitPurchaseOrder(purchaseOrder.getId(), PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY, getLoginUserId());
+ }
return purchaseOrder.getId();
}
@@ -167,6 +179,8 @@
}
// 1.4 鏍¢獙璁㈠崟椤圭殑鏈夋晥鎬�
List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(updateReqVO.getItems());
+ // 1.5 鏍¢獙閲囪喘鍙拌处瀛楁
+ validateLedgerFields(updateReqVO);
// 2.1 鏇存柊璁㈠崟
ErpPurchaseOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseOrderDO.class);
@@ -276,6 +290,7 @@
}
ErpPurchaseOrderSaveReqVO.Item itemVO = new ErpPurchaseOrderSaveReqVO.Item();
itemVO.setProductId(item.getId());
+ itemVO.setProductCode(item.getCode());
itemVO.setProductUnitId(item.getUnitMeasureId());
itemVO.setProductPrice(row.getProductPrice() == null ? BigDecimal.ZERO : row.getProductPrice());
itemVO.setCount(row.getCount());
@@ -297,6 +312,11 @@
purchaseOrder.setNo(no);
purchaseOrder.setSupplierId(supplierId);
purchaseOrder.setOrderTime(parseImportOrderTime(head.getOrderTime()));
+ purchaseOrder.setPurchaseType(parsePurchaseType(head.getPurchaseType()));
+ purchaseOrder.setContractNo(StrUtil.trimToNull(head.getContractNo()));
+ purchaseOrder.setProjectName(StrUtil.trimToNull(head.getProjectName()));
+ purchaseOrder.setPaymentDate(parseImportDateTime(head.getPaymentDate(), "浠樻鏃ユ湡"));
+ purchaseOrder.setPaymentMethod(parsePaymentMethod(head.getPaymentMethod()));
purchaseOrder.setRemark(head.getRemark());
purchaseOrder.setStatus(Boolean.TRUE.equals(directApprove)
? ErpAuditStatus.APPROVE.getStatus() : ErpAuditStatus.PROCESS.getStatus());
@@ -316,8 +336,16 @@
* 瑙f瀽瀵煎叆鐨勯噰璐椂闂达紝鍏煎澶氱甯歌鏍煎紡锛涗负绌哄彇褰撳墠鏃堕棿
*/
private LocalDateTime parseImportOrderTime(String text) {
+ LocalDateTime value = parseImportDateTime(text, "閲囪喘鏃堕棿");
+ return value != null ? value : LocalDateTime.now();
+ }
+
+ /**
+ * 瑙f瀽瀵煎叆鐨勬棩鏈熸椂闂达紝鍏煎澶氱甯歌鏍煎紡锛涗负绌鸿繑鍥� null
+ */
+ private LocalDateTime parseImportDateTime(String text, String fieldName) {
if (StrUtil.isBlank(text)) {
- return LocalDateTime.now();
+ return null;
}
String value = text.trim();
for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss"}) {
@@ -334,7 +362,33 @@
// 缁х画灏濊瘯涓嬩竴涓牸寮�
}
}
- throw new IllegalArgumentException("閲囪喘鏃堕棿鏍煎紡涓嶆纭細" + text + "锛岀ず渚� 2026-01-01 10:00:00");
+ throw new IllegalArgumentException(fieldName + "鏍煎紡涓嶆纭細" + text + "锛岀ず渚� 2026-01-01 10:00:00");
+ }
+
+ /**
+ * 瑙f瀽瀵煎叆鐨勯噰璐被鍨嬪悕绉帮紱涓虹┖鎴栨棤娉曡瘑鍒繑鍥� null
+ */
+ private Integer parsePurchaseType(String text) {
+ if (StrUtil.isBlank(text)) {
+ return null;
+ }
+ String value = text.trim();
+ return Arrays.stream(ErpPurchaseTypeEnum.values())
+ .filter(item -> item.getName().equals(value))
+ .map(ErpPurchaseTypeEnum::getType).findFirst().orElse(null);
+ }
+
+ /**
+ * 瑙f瀽瀵煎叆鐨勪粯娆炬柟寮忓悕绉帮紱涓虹┖鎴栨棤娉曡瘑鍒繑鍥� null
+ */
+ private Integer parsePaymentMethod(String text) {
+ if (StrUtil.isBlank(text)) {
+ return null;
+ }
+ String value = text.trim();
+ return Arrays.stream(ErpPaymentMethodEnum.values())
+ .filter(item -> item.getName().equals(value))
+ .map(ErpPaymentMethodEnum::getType).findFirst().orElse(null);
}
/**
@@ -351,7 +405,8 @@
purchaseOrder.setTotalCount(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getCount, BigDecimal::add));
purchaseOrder.setTotalProductPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
purchaseOrder.setTotalTaxPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
- purchaseOrder.setTotalPrice(purchaseOrder.getTotalProductPrice().add(purchaseOrder.getTotalTaxPrice()));
+ // 鍗曚环涓哄惈绋庡崟浠凤細浠风◣鍚堣 = 鍗曚环 * 鏁伴噺锛岀◣棰濆凡鍖呭惈鍦ㄥ崟浠蜂腑锛屼笉鍐嶉澶栧姞绋�
+ purchaseOrder.setTotalPrice(purchaseOrder.getTotalProductPrice());
// 璁$畻浼樻儬浠锋牸
if (purchaseOrder.getDiscountPercent() == null) {
purchaseOrder.setDiscountPercent(BigDecimal.ZERO);
@@ -392,13 +447,29 @@
@Override
@Transactional(rollbackFor = Exception.class)
public void submitPurchaseOrder(Long id, String processDefinitionKey, Long userId) {
- // 1. 鏍¢獙瀛樺湪 + 鏈鏍哥姸鎬�
+ // 1. 鏍¢獙瀛樺湪 + 鏈鏍哥姸鎬侊紙鑽夌鍜屾湭瀹℃牳鍧囧彲鎻愪氦锛�
ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
- if (!ErpAuditStatus.PROCESS.getStatus().equals(purchaseOrder.getStatus())) {
+ boolean submitable = ErpAuditStatus.DRAFT.getStatus().equals(purchaseOrder.getStatus())
+ || ErpAuditStatus.PROCESS.getStatus().equals(purchaseOrder.getStatus());
+ if (!submitable) {
throw exception(PURCHASE_ORDER_SUBMIT_FAIL_STATUS);
}
+ // 2. 浠樻鏃ユ湡蹇呴�夛細閲囪喘鐢宠鑷姩鐢熸垚鐨勮崏绋胯鍗曟病鏈変粯娆炬棩鏈燂紝鎻愪氦鍓嶅繀椤诲厛缂栬緫琛ラ綈
+ if (purchaseOrder.getPaymentDate() == null) {
+ throw exception(PURCHASE_ORDER_PAYMENT_DATE_REQUIRED);
+ }
- // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥
+ // 3. 鍒涘缓 BPM 娴佺▼瀹炰緥
+ // 鍏ㄥ眬鍏嶅鎵癸細涓氬姟鏍¢獙閫氳繃鍚庣洿鎺ユ墽琛屽鎵归�氳繃閫昏緫锛屾棤闇�娴佺▼妯″瀷銆�
+ if (processInstanceApi.isGlobalAutoApprove()) {
+ purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
+ .setId(id)
+ .setStatus(ErpAuditStatus.APPROVING.getStatus())
+ .setProcessInstanceId(""));
+ updatePurchaseOrderAuditStatus(id, cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus());
+ return;
+ }
+
String processInstanceId = processInstanceApi.createProcessInstance(userId,
new BpmProcessInstanceCreateReqDTO()
.setProcessDefinitionKey(processDefinitionKey)
@@ -494,15 +565,53 @@
private List<ErpPurchaseOrderItemDO> validatePurchaseOrderItems(List<ErpPurchaseOrderSaveReqVO.Item> list) {
// 杞寲涓� ErpPurchaseOrderItemDO 鍒楄〃锛堜骇鍝侀獙璇佸凡绉婚櫎锛�
- return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {
+ List<ErpPurchaseOrderItemDO> items = convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {
item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));
if (item.getTotalPrice() == null) {
return;
}
if (item.getTaxPercent() != null) {
- item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
+ // 鍗曚环涓哄惈绋庡崟浠凤細绋庨浠庝环绋庡悎璁′腑鍙嶇畻锛岃�屼笉鏄湪鍚堣涓婂啀鍔犵◣
+ item.setTaxPrice(MoneyUtils.taxPriceIncluded(item.getTotalPrice(), item.getTaxPercent()));
}
}));
+ // 閲囪喘鍙拌处锛氫骇鍝佺紪鍙峰彇 MDM 鐗╂枡缂栫爜蹇収锛屼繚璇佸垪琛�/瀵煎嚭鍙洿鎺ュ睍绀�
+ fillProductCode(items);
+ return items;
+ }
+
+ /**
+ * 鍥炲~鏄庣粏琛岀殑浜у搧缂栧彿锛圡DM 鐗╂枡缂栫爜锛�
+ */
+ private void fillProductCode(List<ErpPurchaseOrderItemDO> items) {
+ if (CollUtil.isEmpty(items)) {
+ return;
+ }
+ Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
+ convertSet(items, ErpPurchaseOrderItemDO::getProductId));
+ items.forEach(item -> {
+ MdmItemRespDTO mdmItem = itemMap.get(item.getProductId());
+ if (mdmItem == null) {
+ return;
+ }
+ if (StrUtil.isBlank(mdmItem.getCode())) {
+ throw exception(PURCHASE_ORDER_ITEM_PRODUCT_CODE_MISSING,
+ StrUtil.blankToDefault(mdmItem.getName(), "ID=" + mdmItem.getId()));
+ }
+ item.setProductCode(mdmItem.getCode());
+ });
+ }
+
+ /**
+ * 鏍¢獙閲囪喘鍙拌处瀛楁锛堥噰璐被鍨嬨�佷粯娆炬柟寮忥級
+ */
+ private void validateLedgerFields(ErpPurchaseOrderSaveReqVO reqVO) {
+ if (!ErpPurchaseTypeEnum.isValid(reqVO.getPurchaseType())) {
+ throw exception(PURCHASE_ORDER_PURCHASE_TYPE_INVALID, reqVO.getPurchaseType());
+ }
+ if (!ErpPaymentMethodEnum.isValid(reqVO.getPaymentMethod())) {
+ throw exception(PURCHASE_ORDER_PAYMENT_METHOD_INVALID, reqVO.getPaymentMethod());
+ }
}
private void updatePurchaseOrderItemList(Long id, List<ErpPurchaseOrderItemDO> newList) {
--
Gitblit v1.9.3