From 25a40b9c4e6a1b650610b8b13088d486699532ce Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 05 十一月 2026 20:23:21 +0800
Subject: [PATCH] 采购申请批量提交、订单导入重复校验;修复考勤异常申请字段不匹配
---
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java | 755 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 672 insertions(+), 83 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 ccd1dd1..c2ab475 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
@@ -2,42 +2,66 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.product.ErpProductDO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportDuplicateVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportExcelVO;
+import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportRespVO;
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.ErpPurchaseInvoiceOrderMapper;
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.ErpPurchaseOrderInStatusEnum;
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
-import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
+import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
+import cn.iocoder.yudao.module.mes.api.arrivalnotice.MesArrivalNoticeApi;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
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.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
/**
* ERP 閲囪喘璁㈠崟 Service 瀹炵幇绫�
*
- * @author 鑺嬮亾婧愮爜
+ * @author 瓒呯骇绠$悊鍛�
*/
@Service
@Validated
@@ -45,27 +69,52 @@
public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {
/**
- * BPM 閲囪喘璁㈠崟瀹℃壒娴佺▼鏍囪瘑
+ * BPM 閲囪喘璁㈠崟瀹℃壒鍒嗙被缂栫爜
*/
- public static final String BPM_PROCESS_DEFINITION_KEY = "erp-purchase-order-audit";
+ private static final String PURCHASE_ORDER_APPROVE_CATEGORY_CODE = "purchase_order_approve";
+ /**
+ * BPM 閲囪喘璁㈠崟瀹℃壒娴佺▼瀹氫箟 Key锛堝垱寤鸿鍗曞悗鑷姩鎻愪氦鍒板崗鍚屽姙鍏鎵癸級
+ */
+ private static final String PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY = "purchase_order_approve";
@Resource
private ErpPurchaseOrderMapper purchaseOrderMapper;
@Resource
private ErpPurchaseOrderItemMapper purchaseOrderItemMapper;
+ @Resource
+ private ErpPurchaseInvoiceOrderMapper invoiceOrderMapper;
@Resource
private ErpNoRedisDAO noRedisDAO;
- @Resource
- private ErpProductService productService;
@Resource
private ErpSupplierService supplierService;
@Resource
private ErpAccountService accountService;
@Resource
- private BpmProcessInstanceApi bpmProcessInstanceApi;
+ private BpmProcessInstanceApi processInstanceApi;
+ @Resource
+ private BpmCategoryService bpmCategoryService;
+ @Resource
+ private BpmProcessDefinitionService bpmProcessDefinitionService;
+
+ @Resource
+ @Lazy
+ private MesArrivalNoticeApi arrivalNoticeApi;
+
+ @Resource
+ private ErpPurchaseRequestItemMapper purchaseRequestItemMapper;
+ @Resource
+ private ErpPurchaseRequestMapper purchaseRequestMapper;
+
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
+
+ @Resource
+ private SrmSupplierApi srmSupplierApi;
+ @Resource
+ private MdmItemApi mdmItemApi;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -78,20 +127,30 @@
if (createReqVO.getAccountId() != null) {
accountService.validateAccount(createReqVO.getAccountId());
}
- // 1.4 鐢熸垚璁㈠崟鍙凤紝骞舵牎楠屽敮涓�鎬�
- String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX);
+ // 1.4 纭畾璁㈠崟鍙凤紙鍓嶇濉啓鍒欑敤鑷畾涔夊崟鍙凤紝鐣欑┖鍒欒嚜鍔ㄧ敓鎴愶級锛屽苟鏍¢獙鍞竴鎬�
+ String customNo = StrUtil.trim(createReqVO.getNo());
+ String no = StrUtil.isNotBlank(customNo) ? customNo
+ : noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX);
if (purchaseOrderMapper.selectByNo(no) != null) {
throw exception(PURCHASE_ORDER_NO_EXISTS);
}
- // 2.1 鎻掑叆璁㈠崟
+ // 2.1 鎻掑叆璁㈠崟锛堥噰璐鍗曞垱寤哄悗涓烘湭瀹℃牳鐘舵�侊紝闇�瑕佹彁浜ゅ鎵癸級
ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in
- .setNo(no).setStatus(ErpAuditStatus.DRAFT.getStatus()));
+ .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())
+ .setInCount(BigDecimal.ZERO).setInStatus(ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus()));
calculateTotalPrice(purchaseOrder, purchaseOrderItems);
purchaseOrderMapper.insert(purchaseOrder);
// 2.2 鎻掑叆璁㈠崟椤�
purchaseOrderItems.forEach(o -> o.setOrderId(purchaseOrder.getId()));
purchaseOrderItemMapper.insertBatch(purchaseOrderItems);
+ // 2.3 缁戝畾闄勪欢
+ storageAttachmentApi.bindAttachments("file", "erp_purchase_order",
+ purchaseOrder.getId(), createReqVO.getBlobIds());
+
+ // 3. 鍒涘缓鍚庤嚜鍔ㄦ彁浜ゅ鎵瑰埌鍗忓悓鍔炲叕锛堝鎵瑰湪鍗忓悓鍔炲叕瀹屾垚锛屼笉鍦ㄦ湰椤甸潰瀹℃壒锛�
+ submitPurchaseOrder(purchaseOrder.getId(), PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY, getLoginUserId());
+
return purchaseOrder.getId();
}
@@ -100,6 +159,7 @@
public void updatePurchaseOrder(ErpPurchaseOrderSaveReqVO updateReqVO) {
// 1.1 鏍¢獙瀛樺湪
ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(updateReqVO.getId());
+ // 閲囪喘璁㈠崟涓嶅厑璁镐慨鏀癸紙宸插鏍哥姸鎬侊級
if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) {
throw exception(PURCHASE_ORDER_UPDATE_FAIL_APPROVE, purchaseOrder.getNo());
}
@@ -118,6 +178,339 @@
purchaseOrderMapper.updateById(updateObj);
// 2.2 鏇存柊璁㈠崟椤�
updatePurchaseOrderItemList(updateReqVO.getId(), purchaseOrderItems);
+ // 2.3 鏇存柊闄勪欢
+ storageAttachmentApi.updateAttachments("file", "erp_purchase_order", updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public ErpPurchaseOrderImportRespVO importPurchaseOrderList(List<ErpPurchaseOrderImportExcelVO> importList,
+ Boolean directApprove, Boolean overwrite) {
+ ErpPurchaseOrderImportRespVO respVO = ErpPurchaseOrderImportRespVO.builder()
+ .createList(new ArrayList<>())
+ .updateList(new ArrayList<>())
+ .failureList(new LinkedHashMap<>())
+ .duplicateList(new ArrayList<>())
+ .build();
+ if (CollUtil.isEmpty(importList)) {
+ return respVO;
+ }
+
+ // 1. 棰勭儹鈥滀緵搴斿晢鍚嶇О/缂栫爜 -> 渚涘簲鍟嗙紪鍙封�濇槧灏勶紙ERP 渚涘簲鍟嗕富鏁版嵁鍦� SRM 妯″潡锛�
+ Map<String, Long> supplierIdMap = new HashMap<>();
+ CommonResult<List<SrmSupplierRespDTO>> supplierResult = srmSupplierApi.getSupplierSimpleList();
+ if (supplierResult != null && CollUtil.isNotEmpty(supplierResult.getData())) {
+ supplierResult.getData().forEach(supplier -> {
+ if (StrUtil.isNotBlank(supplier.getName())) {
+ supplierIdMap.put(supplier.getName().trim(), supplier.getId());
+ }
+ if (StrUtil.isNotBlank(supplier.getCode())) {
+ supplierIdMap.putIfAbsent(supplier.getCode().trim(), supplier.getId());
+ }
+ });
+ }
+
+ // 2. 棰勭儹绯荤粺鍐呮湁鏁堥噰璐鍗曪細鎸夊崟鍙枫�佹寜鈥滀緵搴斿晢 + 鏄庣粏鈥濇寚绾瑰缓绱㈠紩锛岀敤浜庤瘑鍒噸澶嶅鍏�
+ List<ErpPurchaseOrderDO> existingOrders = purchaseOrderMapper.selectList();
+ Map<Long, List<ErpPurchaseOrderItemDO>> existingItemsMap = new HashMap<>();
+ if (CollUtil.isNotEmpty(existingOrders)) {
+ purchaseOrderItemMapper.selectListByOrderIds(
+ existingOrders.stream().map(ErpPurchaseOrderDO::getId).toList())
+ .forEach(item -> existingItemsMap.computeIfAbsent(item.getOrderId(), k -> new ArrayList<>()).add(item));
+ }
+ Map<String, ErpPurchaseOrderDO> existingNoMap = new HashMap<>();
+ Map<String, List<ErpPurchaseOrderDO>> existingFingerprintMap = new HashMap<>();
+ for (ErpPurchaseOrderDO order : existingOrders) {
+ if (StrUtil.isNotBlank(order.getNo())) {
+ existingNoMap.put(order.getNo(), order);
+ }
+ String fingerprint = buildOrderFingerprint(order.getSupplierId(), existingItemsMap.get(order.getId()));
+ if (fingerprint != null) {
+ existingFingerprintMap.computeIfAbsent(fingerprint, k -> new ArrayList<>()).add(order);
+ }
+ }
+
+ // 3. 鎸夆�滈噰璐崟缂栧彿鈥濆垎缁勶細鍚岀紪鍙峰琛� = 涓�寮犺鍗曠殑澶氭潯鏄庣粏锛涚紪鍙蜂负绌哄垯姣忚鐙珛鎴愬崟
+ Map<String, List<ErpPurchaseOrderImportExcelVO>> orderGroups = new LinkedHashMap<>();
+ for (int i = 0; i < importList.size(); i++) {
+ ErpPurchaseOrderImportExcelVO row = importList.get(i);
+ if (row == null || (StrUtil.isBlank(row.getSupplierName()) && StrUtil.isBlank(row.getProductCode())
+ && StrUtil.isBlank(row.getNo()))) {
+ continue; // 璺宠繃绌鸿
+ }
+ String key = StrUtil.isNotBlank(row.getNo()) ? row.getNo().trim() : "__row_" + (i + 2);
+ orderGroups.computeIfAbsent(key, k -> new ArrayList<>()).add(row);
+ }
+
+ // 4. 閫愪釜璁㈠崟瀵煎叆锛堝崟寮犺鍗曞け璐ヤ笉褰卞搷鍏跺畠锛�
+ for (Map.Entry<String, List<ErpPurchaseOrderImportExcelVO>> entry : orderGroups.entrySet()) {
+ List<ErpPurchaseOrderImportExcelVO> rows = entry.getValue();
+ String label = StrUtil.isNotBlank(rows.get(0).getNo()) ? rows.get(0).getNo().trim()
+ : "绗� " + entry.getKey().substring("__row_".length()) + " 琛�";
+ try {
+ ImportOrderData importData = prepareImportOrder(rows, supplierIdMap);
+
+ // 4.1 鍏堟寜鍗曞彿鎵撅紱鍙湁鈥滃崟鍙蜂负绌恒�佺敱绯荤粺鐢熸垚缂栧彿鈥濈殑琛屾墠鎸夆�滀緵搴斿晢 + 瀹屾暣鏄庣粏鈥濇寚绾规壘銆�
+ // 鏂囦欢閲屽啓浜嗘槑纭崟鍙风殑锛岃涓虹敤鎴锋槑纭鏂板璇ュ崟鍙凤紝鎸囩汗鐩稿悓涔熶笉鎷︽埅锛岄伩鍏嶆甯哥殑閲嶅閲囪喘琚鍒ゃ��
+ ErpPurchaseOrderDO existing = importData.order.getNo() == null ? null
+ : existingNoMap.get(importData.order.getNo());
+ String duplicateReason = existing != null ? "閲囪喘鍗曠紪鍙峰凡瀛樺湪" : null;
+ boolean matchedByFingerprint = false;
+ List<ErpPurchaseOrderDO> fingerprintCandidates = null;
+ if (existing == null && importData.generatedNo && importData.fingerprint != null) {
+ fingerprintCandidates = existingFingerprintMap.get(importData.fingerprint);
+ if (CollUtil.isNotEmpty(fingerprintCandidates)) {
+ existing = fingerprintCandidates.get(0);
+ matchedByFingerprint = true;
+ duplicateReason = "渚涘簲鍟嗐�佹槑缁嗐�佹暟閲忋�佸崟浠枫�侀噾棰濅笌宸叉湁璁㈠崟涓�鑷�";
+ }
+ }
+
+ // 4.2 鍙戠幇閲嶅锛氶粯璁や笉瀵煎叆锛岃繑鍥炵粰鍓嶇寮规鎻愮ず锛涘嬀閫夎鐩栧悗鎵嶆洿鏂板凡鏈夎鍗�
+ if (existing != null) {
+ if (!Boolean.TRUE.equals(overwrite)) {
+ respVO.getDuplicateList().add(buildImportDuplicate(label, existing, duplicateReason));
+ continue;
+ }
+ if (matchedByFingerprint && fingerprintCandidates.size() > 1) {
+ String nos = fingerprintCandidates.stream().map(ErpPurchaseOrderDO::getNo)
+ .collect(Collectors.joining("銆�"));
+ throw new IllegalArgumentException("鎵惧埌澶氬紶鐩稿悓璁㈠崟锛�" + nos
+ + "锛夛紝鏃犳硶鑷姩瑕嗙洊锛岃鍏堜繚鐣欎竴寮犲悗鍐嶅鍏�");
+ }
+ updatePurchaseOrderByImport(existing, importData, directApprove);
+ respVO.getUpdateList().add(existing.getNo());
+ continue;
+ }
+
+ // 4.3 娌℃湁閲嶅锛屾甯告柊澧�
+ createPurchaseOrderByImport(importData, directApprove);
+ respVO.getCreateList().add(importData.order.getNo());
+ } catch (Exception ex) {
+ respVO.getFailureList().put(label, resolveImportErrorMessage(ex));
+ }
+ }
+ return respVO;
+ }
+
+ /**
+ * 鏍¢獙骞剁粍瑁呬竴寮犲鍏ヨ鍗曪紙涓嶈惤搴擄級銆�
+ */
+ private ImportOrderData prepareImportOrder(List<ErpPurchaseOrderImportExcelVO> rows,
+ Map<String, Long> supplierIdMap) {
+ // 1. 渚涘簲鍟嗭紙鍙栬缁勯琛岋級
+ ErpPurchaseOrderImportExcelVO head = rows.get(0);
+ if (StrUtil.isBlank(head.getSupplierName())) {
+ throw new IllegalArgumentException("渚涘簲鍟嗗悕绉颁笉鑳戒负绌�");
+ }
+ Long supplierId = supplierIdMap.get(head.getSupplierName().trim());
+ if (supplierId == null) {
+ throw new IllegalArgumentException("渚涘簲鍟嗕笉瀛樺湪锛�" + head.getSupplierName());
+ }
+ supplierService.validateSupplier(supplierId);
+
+ // 2. 鏄庣粏
+ List<ErpPurchaseOrderSaveReqVO.Item> items = new ArrayList<>();
+ for (ErpPurchaseOrderImportExcelVO row : rows) {
+ if (StrUtil.isBlank(row.getProductCode())) {
+ throw new IllegalArgumentException("浜у搧缂栫爜涓嶈兘涓虹┖");
+ }
+ CommonResult<MdmItemRespDTO> itemResult = mdmItemApi.getItemByCode(row.getProductCode().trim());
+ MdmItemRespDTO item = itemResult == null ? null : itemResult.getData();
+ if (item == null) {
+ throw new IllegalArgumentException("浜у搧涓嶅瓨鍦細" + row.getProductCode());
+ }
+ if (row.getCount() == null) {
+ throw new IllegalArgumentException("浜у搧[" + row.getProductCode() + "]鐨勬暟閲忎笉鑳戒负绌�");
+ }
+ if (item.getUnitMeasureId() == null) {
+ throw new IllegalArgumentException("浜у搧[" + row.getProductCode() + "]鏈厤缃閲忓崟浣嶏紝璇峰厛鍦ㄧ墿鏂欎腑缁存姢");
+ }
+ ErpPurchaseOrderSaveReqVO.Item itemVO = new ErpPurchaseOrderSaveReqVO.Item();
+ itemVO.setProductId(item.getId());
+ itemVO.setProductUnitId(item.getUnitMeasureId());
+ itemVO.setProductPrice(row.getProductPrice() == null ? BigDecimal.ZERO : row.getProductPrice());
+ itemVO.setCount(row.getCount());
+ itemVO.setTaxPercent(row.getTaxPercent());
+ itemVO.setRemark(row.getRemark());
+ itemVO.setQcCheckFlag("鏄�".equals(StrUtil.trim(row.getQcCheckFlag())));
+ items.add(itemVO);
+ }
+
+ // 3. 鍗曞彿锛氬~浜嗗氨鐢紱鐣欑┖鏃跺厛涓嶇敓鎴愶紝绛夌‘璁よ鏂板鏃跺啀鐢熸垚锛岄伩鍏嶉噸澶嶈琚烦杩囦篃娑堣�楁祦姘村彿
+ boolean generatedNo = StrUtil.isBlank(head.getNo());
+
+ // 4. 缁勮涓昏〃锛堢姸鎬佺敱璋冪敤鏂瑰喅瀹氾級
+ ErpPurchaseOrderDO purchaseOrder = new ErpPurchaseOrderDO();
+ purchaseOrder.setNo(generatedNo ? null : head.getNo().trim());
+ purchaseOrder.setSupplierId(supplierId);
+ purchaseOrder.setOrderTime(parseImportOrderTime(head.getOrderTime()));
+ purchaseOrder.setRemark(head.getRemark());
+ purchaseOrder.setDepositPrice(BigDecimal.ZERO); // deposit_price 涓� NOT NULL 涓旀棤榛樿鍊硷紝蹇呴』鏄惧紡璧嬪��
+
+ List<ErpPurchaseOrderItemDO> itemDOs = validatePurchaseOrderItems(items);
+ calculateTotalPrice(purchaseOrder, itemDOs);
+ return new ImportOrderData(purchaseOrder, itemDOs,
+ buildOrderFingerprint(supplierId, itemDOs), generatedNo,
+ StrUtil.isNotBlank(head.getOrderTime()));
+ }
+
+ /**
+ * 鏂板瀵煎叆璁㈠崟锛堝惈鏄庣粏锛夈��
+ */
+ private void createPurchaseOrderByImport(ImportOrderData importData, Boolean directApprove) {
+ ErpPurchaseOrderDO purchaseOrder = importData.order;
+ if (StrUtil.isBlank(purchaseOrder.getNo())) {
+ purchaseOrder.setNo(noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX));
+ }
+ purchaseOrder.setStatus(Boolean.TRUE.equals(directApprove)
+ ? ErpAuditStatus.APPROVE.getStatus() : ErpAuditStatus.PROCESS.getStatus());
+ purchaseOrder.setInCount(BigDecimal.ZERO);
+ purchaseOrder.setInStatus(ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus());
+ purchaseOrderMapper.insert(purchaseOrder);
+ importData.items.forEach(item -> item.setOrderId(purchaseOrder.getId()));
+ purchaseOrderItemMapper.insertBatch(importData.items);
+ }
+
+ /**
+ * 瑕嗙洊宸叉湁瀵煎叆璁㈠崟锛堝惈鏄庣粏锛夈��
+ */
+ private void updatePurchaseOrderByImport(ErpPurchaseOrderDO existing, ImportOrderData importData,
+ Boolean directApprove) {
+ // 1. 鏈変笅娓稿崟鎹殑璁㈠崟涓嶅厑璁歌鐩栵紝閬垮厤鎶婂凡鍏ュ簱/宸插紑绁ㄧ殑鏁版嵁鏀规帀
+ if (existing.getInCount() != null && existing.getInCount().compareTo(BigDecimal.ZERO) > 0) {
+ throw new IllegalArgumentException("閲囪喘璁㈠崟[" + existing.getNo() + "]宸插叆搴擄紙宸插叆搴撴暟閲� "
+ + normalizeNumber(existing.getInCount()) + "锛夛紝涓嶈兘瑕嗙洊锛涜鍏堝垹闄ゅ搴斿叆搴撹褰曞悗鍐嶅鍏�");
+ }
+ if (existing.getReturnCount() != null && existing.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
+ throw new IllegalArgumentException("閲囪喘璁㈠崟[" + existing.getNo() + "]宸查��璐э紙宸查��璐ф暟閲� "
+ + normalizeNumber(existing.getReturnCount()) + "锛夛紝涓嶈兘瑕嗙洊锛涜鍏堝垹闄ゅ搴旈��璐ц褰曞悗鍐嶅鍏�");
+ }
+ if (CollUtil.isNotEmpty(invoiceOrderMapper.selectListByPurchaseOrderId(existing.getId()))) {
+ throw new IllegalArgumentException("閲囪喘璁㈠崟[" + existing.getNo()
+ + "]宸插叧鑱斿彂绁紝涓嶈兘瑕嗙洊锛涜鍏堣В闄ゅ彂绁ㄥ叧鑱斿悗鍐嶅鍏�");
+ }
+ if (StrUtil.isNotBlank(existing.getProcessInstanceId())) {
+ throw new IllegalArgumentException("閲囪喘璁㈠崟[" + existing.getNo()
+ + "]宸叉彁浜ゅ鎵规祦绋嬶紝涓嶈兘瑕嗙洊锛涜鍏堝湪鍗忓悓鍔炲叕鎾ゅ洖鎴栦綔搴熻瀹℃壒鍚庡啀瀵煎叆");
+ }
+ List<ErpPurchaseOrderItemDO> oldItems = purchaseOrderItemMapper.selectListByOrderId(existing.getId());
+ boolean linkedRequest = oldItems.stream().anyMatch(item -> item.getRequestItemId() != null);
+ if (linkedRequest) {
+ throw new IllegalArgumentException("閲囪喘璁㈠崟[" + existing.getNo()
+ + "]宸插叧鑱旈噰璐敵璇凤紝涓嶈兘瑕嗙洊锛涜鍏堝湪閲囪喘鐢宠涓挙閿�鎴栧垹闄ゅ搴旂敵璇峰悗鍐嶅鍏�");
+ }
+
+ // 2. 鏇存柊涓昏〃锛堜繚鐣欏師鍗曞彿銆佸師鍏ュ簱鐘舵�侊級
+ ErpPurchaseOrderDO updateObj = importData.order;
+ updateObj.setId(existing.getId());
+ updateObj.setNo(existing.getNo());
+ // 鏂囦欢娌″~閲囪喘鏃堕棿鏃朵繚鐣欏師璁㈠崟鏃堕棿锛岄伩鍏嶈鐩栨椂鎶婃棩鏈熸敼鎴愬綋鍓嶆椂闂�
+ if (!importData.orderTimeProvided) {
+ updateObj.setOrderTime(existing.getOrderTime());
+ }
+ updateObj.setStatus(Boolean.TRUE.equals(directApprove)
+ ? ErpAuditStatus.APPROVE.getStatus() : ErpAuditStatus.PROCESS.getStatus());
+ updateObj.setInCount(existing.getInCount() == null ? BigDecimal.ZERO : existing.getInCount());
+ updateObj.setInStatus(existing.getInStatus());
+ purchaseOrderMapper.updateById(updateObj);
+
+ // 3. 鏄庣粏鏁寸粍鏇挎崲
+ purchaseOrderItemMapper.deleteByOrderId(existing.getId());
+ importData.items.forEach(item -> item.setOrderId(existing.getId()));
+ purchaseOrderItemMapper.insertBatch(importData.items);
+ }
+
+ private ErpPurchaseOrderImportDuplicateVO buildImportDuplicate(String label, ErpPurchaseOrderDO existing,
+ String reason) {
+ return ErpPurchaseOrderImportDuplicateVO.builder()
+ .key(label)
+ .existingNo(existing.getNo())
+ .existingId(existing.getId())
+ .reason(reason)
+ .build();
+ }
+
+ /**
+ * 鐢熸垚鈥滀緵搴斿晢 + 瀹屾暣鏄庣粏鈥濈殑璁㈠崟鎸囩汗锛岀敤浜庤瘑鍒噸澶嶅鍏ャ��
+ * 鏄庣粏鎸夌墿鏂欍�佹暟閲忋�佸崟浠枫�佺◣鐜囨帓搴忓悗姣旇緝锛屼笉鍙楄椤哄簭褰卞搷銆�
+ */
+ private String buildOrderFingerprint(Long supplierId, List<ErpPurchaseOrderItemDO> items) {
+ if (supplierId == null || CollUtil.isEmpty(items)) {
+ return null;
+ }
+ List<String> parts = items.stream()
+ .map(item -> String.join(":",
+ String.valueOf(item.getProductId()),
+ normalizeNumber(item.getCount()),
+ normalizeNumber(item.getProductPrice()),
+ normalizeNumber(item.getTaxPercent())))
+ .sorted()
+ .toList();
+ return supplierId + "|" + String.join(",", parts);
+ }
+
+ private String normalizeNumber(BigDecimal value) {
+ return value == null ? "" : value.stripTrailingZeros().toPlainString();
+ }
+
+ private static class ImportOrderData {
+
+ private final ErpPurchaseOrderDO order;
+ private final List<ErpPurchaseOrderItemDO> items;
+ private final String fingerprint;
+ /**
+ * 閲囪喘鍗曠紪鍙锋槸鍚︾敱绯荤粺鑷姩鐢熸垚锛堟枃浠堕噷鐣欑┖锛�
+ */
+ private final boolean generatedNo;
+ /**
+ * 鏂囦欢閲屾槸鍚﹀~鍐欎簡閲囪喘鏃堕棿
+ */
+ private final boolean orderTimeProvided;
+
+ private ImportOrderData(ErpPurchaseOrderDO order, List<ErpPurchaseOrderItemDO> items, String fingerprint,
+ boolean generatedNo, boolean orderTimeProvided) {
+ this.order = order;
+ this.items = items;
+ this.fingerprint = fingerprint;
+ this.generatedNo = generatedNo;
+ this.orderTimeProvided = orderTimeProvided;
+ }
+ }
+
+ /**
+ * 瑙f瀽瀵煎叆鐨勯噰璐椂闂达紝鍏煎澶氱甯歌鏍煎紡锛涗负绌哄彇褰撳墠鏃堕棿
+ */
+ private LocalDateTime parseImportOrderTime(String text) {
+ if (StrUtil.isBlank(text)) {
+ return LocalDateTime.now();
+ }
+ 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"}) {
+ try {
+ return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern));
+ } catch (Exception ignored) {
+ // 缁х画灏濊瘯涓嬩竴涓牸寮�
+ }
+ }
+ for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) {
+ try {
+ return LocalDate.parse(value, DateTimeFormatter.ofPattern(pattern)).atStartOfDay();
+ } catch (Exception ignored) {
+ // 缁х画灏濊瘯涓嬩竴涓牸寮�
+ }
+ }
+ throw new IllegalArgumentException("閲囪喘鏃堕棿鏍煎紡涓嶆纭細" + text + "锛岀ず渚� 2026-01-01 10:00:00");
+ }
+
+ /**
+ * 鎻愬彇瀵煎叆澶辫触鍘熷洜锛圫erviceException 鍙栦笟鍔℃彁绀猴級
+ */
+ private String resolveImportErrorMessage(Exception ex) {
+ if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) {
+ return serviceException.getMessage();
+ }
+ return StrUtil.blankToDefault(ex.getMessage(), "瀵煎叆澶辫触");
}
private void calculateTotalPrice(ErpPurchaseOrderDO purchaseOrder, List<ErpPurchaseOrderItemDO> purchaseOrderItems) {
@@ -143,12 +536,14 @@
if (purchaseOrder.getStatus().equals(status)) {
throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL);
}
- // 1.3 瀛樺湪閲囪喘鍏ュ崟锛屾棤娉曞弽瀹℃牳
- if (!approve && purchaseOrder.getInCount().compareTo(BigDecimal.ZERO) > 0) {
+ // 1.3 瀛樺湪閲囪喘鍏ュ簱鍗曪紝鏃犳硶鍙嶅鏍�
+ BigDecimal inCount = purchaseOrder.getInCount();
+ if (!approve && inCount != null && inCount.compareTo(BigDecimal.ZERO) > 0) {
throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN);
}
// 1.4 瀛樺湪閲囪喘閫�璐у崟锛屾棤娉曞弽瀹℃牳
- if (!approve && purchaseOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
+ BigDecimal returnCount = purchaseOrder.getReturnCount();
+ if (!approve && returnCount != null && returnCount.compareTo(BigDecimal.ZERO) > 0) {
throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN);
}
@@ -160,28 +555,130 @@
}
}
+ @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.REJECT.getStatus();
+ } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
+ return ErpAuditStatus.CANCEL.getStatus();
+ }
+ return null;
+ }
+
private List<ErpPurchaseOrderItemDO> validatePurchaseOrderItems(List<ErpPurchaseOrderSaveReqVO.Item> list) {
- // 1. 鏍¢獙浜у搧瀛樺湪
- List<ErpProductDO> productList = productService.validProductList(
- convertSet(list, ErpPurchaseOrderSaveReqVO.Item::getProductId));
- Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);
- // 2. 杞寲涓� ErpPurchaseOrderItemDO 鍒楄〃
+ // 杞寲涓� ErpPurchaseOrderItemDO 鍒楄〃锛堜骇鍝侀獙璇佸凡绉婚櫎锛�
return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {
- item.setProductUnitId(productMap.get(item.getProductId()).getUnitId());
- item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()));
- if (item.getTotalPrice() == null) {
+ // 鍗曚环涓哄惈绋庡崟浠凤細鍚◣閲戦 = 鍗曚环 脳 鏁伴噺锛涚◣棰� = 鍚◣閲戦 脳 绋庣巼锛涗笉鍚◣閲戦 = 鍚◣閲戦 - 绋庨
+ BigDecimal taxInclusivePrice = MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount());
+ if (taxInclusivePrice == null) {
return;
}
+ BigDecimal taxPrice = null;
if (item.getTaxPercent() != null) {
- item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
+ taxPrice = MoneyUtils.priceMultiplyPercent(taxInclusivePrice, item.getTaxPercent());
}
+ item.setTaxPrice(taxPrice);
+ item.setTotalPrice(taxPrice != null ? taxInclusivePrice.subtract(taxPrice) : taxInclusivePrice);
}));
}
private void updatePurchaseOrderItemList(Long id, List<ErpPurchaseOrderItemDO> newList) {
// 绗竴姝ワ紝瀵规瘮鏂拌�佹暟鎹紝鑾峰緱娣诲姞銆佷慨鏀广�佸垹闄ょ殑鍒楄〃
List<ErpPurchaseOrderItemDO> oldList = purchaseOrderItemMapper.selectListByOrderId(id);
- List<List<ErpPurchaseOrderItemDO>> diffList = diffList(oldList, newList, // id 涓嶅悓锛屽氨璁や负鏄笉鍚岀殑璁板綍
+ List<List<ErpPurchaseOrderItemDO>> diffList = diffList(oldList, newList,
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
// 绗簩姝ワ紝鎵归噺娣诲姞銆佷慨鏀广�佸垹闄�
@@ -208,7 +705,7 @@
}
if (inCount.compareTo(item.getCount()) > 0) {
throw exception(PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED,
- productService.getProduct(item.getProductId()).getName(), item.getCount());
+ "浜у搧ID:" + item.getProductId(), item.getCount());
}
purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setInCount(inCount));
});
@@ -228,7 +725,7 @@
}
if (returnCount.compareTo(item.getInCount()) > 0) {
throw exception(PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED,
- productService.getProduct(item.getProductId()).getName(), item.getInCount());
+ "浜у搧ID:" + item.getProductId(), item.getInCount());
}
purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setReturnCount(returnCount));
});
@@ -251,11 +748,13 @@
}
});
- // 2. 閬嶅巻鍒犻櫎锛屽苟璁板綍鎿嶄綔鏃ュ織
+ // 2. 閬嶅巻鍒犻櫎
purchaseOrders.forEach(purchaseOrder -> {
- // 2.1 鍒犻櫎璁㈠崟
+ // 2.1 鍒犻櫎闄勪欢
+ storageAttachmentApi.deleteAttachmentsByRecord("erp_purchase_order", purchaseOrder.getId());
+ // 2.2 鍒犻櫎璁㈠崟
purchaseOrderMapper.deleteById(purchaseOrder.getId());
- // 2.2 鍒犻櫎璁㈠崟椤�
+ // 2.3 鍒犻櫎璁㈠崟椤�
purchaseOrderItemMapper.deleteByOrderId(purchaseOrder.getId());
});
}
@@ -271,6 +770,11 @@
@Override
public ErpPurchaseOrderDO getPurchaseOrder(Long id) {
return purchaseOrderMapper.selectById(id);
+ }
+
+ @Override
+ public ErpPurchaseOrderDO getPurchaseOrderByProcessInstanceId(String processInstanceId) {
+ return purchaseOrderMapper.selectByProcessInstanceId(processInstanceId);
}
@Override
@@ -302,67 +806,152 @@
return purchaseOrderItemMapper.selectListByOrderIds(orderIds);
}
- // ==================== BPM 娴佺▼瀹℃壒 ====================
-
@Override
- @Transactional(rollbackFor = Exception.class)
- public void submitPurchaseOrder(Long id, Long userId) {
- // 1. 鏍¢獙閲囪喘璁㈠崟澶勪簬鑽夌鐘舵��
- ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
- if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.DRAFT.getStatus())) {
- throw exception(PURCHASE_ORDER_SUBMIT_FAIL_NOT_DRAFT);
+ public List<ErpPurchaseOrderDO> getPurchaseOrderListBySupplierIdAndStatus(Long supplierId, Integer status) {
+ if (supplierId == null) {
+ return Collections.emptyList();
}
-
- // 2. 鍒涘缓閲囪喘璁㈠崟瀹℃壒娴佺▼瀹炰緥
- String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
- .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY)
- .setBusinessKey(String.valueOf(id)));
-
- // 3. 鏇存柊閲囪喘璁㈠崟宸ヤ綔娴佺紪鍙枫�佺姸鎬�
- purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
- .setId(id)
- .setProcessInstanceId(processInstanceId)
- .setStatus(ErpAuditStatus.PROCESS.getStatus()));
+ return purchaseOrderMapper.selectList(supplierId, status);
}
@Override
- @Transactional(rollbackFor = Exception.class)
- public void updatePurchaseOrderAuditStatus(Long id, Integer bpmResult) {
- // 1. 鏍¢獙閲囪喘璁㈠崟瀛樺湪
- ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
-
- // 2. 鏍¢獙閲囪喘璁㈠崟澶勪簬瀹℃壒涓�
- if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.PROCESS.getStatus())) {
- log.error("[updatePurchaseOrderAuditStatus][purchaseOrder({}) 涓嶅浜庡鎵逛腑锛屾棤娉曟洿鏂板鎵圭粨鏋�({})]",
- purchaseOrder.getId(), bpmResult);
- throw exception(PURCHASE_ORDER_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
+ public List<ErpPurchaseOrderDO> getPurchaseOrderList(List<Long> ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return Collections.emptyList();
}
+ return purchaseOrderMapper.selectByIds(ids);
+ }
- // 3. 鏇存柊閲囪喘璁㈠崟瀹℃壒缁撴灉
- Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
+ @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);
+ // 2. 鑾峰彇閲囪喘璁㈠崟鏄庣粏
+ List<ErpPurchaseOrderItemDO> items = purchaseOrderItemMapper.selectListByOrderId(orderId);
+ if (CollUtil.isEmpty(items)) {
+ return;
+ }
+ // 3. 璁$畻鍏ュ簱鐘舵�佸拰鎬诲叆搴撴暟閲�
+ int inStatus = calculateInStatus(items);
+ BigDecimal totalInCount = calculateTotalInCount(items);
+ // 4. 鏇存柊閲囪喘璁㈠崟鍏ュ簱鐘舵�佸拰鍏ュ簱鏁伴噺
purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
- .setId(id)
- .setStatus(auditStatus));
+ .setId(orderId).setInStatus(inStatus).setInCount(totalInCount));
+
+ // 5. 鍥炲啓閲囪喘鐢宠鏄庣粏鐨勫叆搴撴暟閲忓拰閲囪喘鐢宠鐨勫叆搴撶姸鎬�
+ updatePurchaseRequestInStatus(items, inStatus);
}
/**
- * 灏� BPM 瀹℃壒缁撴灉杞崲涓� ERP 瀹℃壒鐘舵��
- *
- * @param bpmResult BPM 瀹℃壒缁撴灉
- * @return ERP 瀹℃壒鐘舵��
+ * 璁$畻鎬诲叆搴撴暟閲�
*/
- private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
- // BPM 瀹℃壒缁撴灉锛�2=閫氳繃锛�3=涓嶉�氳繃锛�4=宸插彇娑�
- if (bpmResult == 2) {
- return ErpAuditStatus.APPROVE.getStatus();
- } else if (bpmResult == 3) {
- return ErpAuditStatus.REJECT.getStatus();
- } else if (bpmResult == 4) {
- return ErpAuditStatus.DRAFT.getStatus();
- } else {
- log.warn("[convertBpmResultToAuditStatus][鏈煡鐨� BPM 瀹℃壒缁撴灉({})]", bpmResult);
- return ErpAuditStatus.PROCESS.getStatus();
+ private BigDecimal calculateTotalInCount(List<ErpPurchaseOrderItemDO> items) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (ErpPurchaseOrderItemDO item : items) {
+ if (item.getInCount() != null) {
+ total = total.add(item.getInCount());
+ }
+ }
+ return total;
+ }
+
+ /**
+ * 鍥炲啓閲囪喘鐢宠鍏ュ簱鏁伴噺鍜屽叆搴撶姸鎬�
+ *
+ * @param orderItems 閲囪喘璁㈠崟鏄庣粏鍒楄〃
+ * @param orderInStatus 閲囪喘璁㈠崟鍏ュ簱鐘舵��
+ */
+ private void updatePurchaseRequestInStatus(List<ErpPurchaseOrderItemDO> orderItems, int orderInStatus) {
+ // 鑾峰彇鎵�鏈夊叧鑱旂殑閲囪喘鐢宠鏄庣粏 ID
+ Set<Long> requestItemIds = new HashSet<>();
+ for (ErpPurchaseOrderItemDO item : orderItems) {
+ if (item.getRequestItemId() != null) {
+ requestItemIds.add(item.getRequestItemId());
+ }
+ }
+ if (requestItemIds.isEmpty()) {
+ return;
+ }
+
+ // 鏇存柊閲囪喘鐢宠鏄庣粏鐨勫叆搴撴暟閲�
+ for (ErpPurchaseOrderItemDO orderItem : orderItems) {
+ if (orderItem.getRequestItemId() != null && orderItem.getInCount() != null) {
+ purchaseRequestItemMapper.updateById(new ErpPurchaseRequestItemDO()
+ .setId(orderItem.getRequestItemId())
+ .setInCount(orderItem.getInCount()));
+ }
+ }
+
+ // 鑾峰彇閲囪喘鐢宠鏄庣粏锛屾壘鍒板搴旂殑閲囪喘鐢宠 ID
+ List<ErpPurchaseRequestItemDO> requestItems = purchaseRequestItemMapper.selectBatchIds(requestItemIds);
+ if (CollUtil.isEmpty(requestItems)) {
+ return;
+ }
+ Set<Long> requestIds = convertSet(requestItems, ErpPurchaseRequestItemDO::getRequestId);
+
+ // 鏇存柊閲囪喘鐢宠鐨勫叆搴撶姸鎬�
+ for (Long requestId : requestIds) {
+ purchaseRequestMapper.updateById(new ErpPurchaseRequestDO()
+ .setId(requestId)
+ .setInStatus(orderInStatus));
}
}
-}
+ /**
+ * 璁$畻鍏ュ簱鐘舵��
+ *
+ * @param items 閲囪喘璁㈠崟鏄庣粏鍒楄〃
+ * @return 鍏ュ簱鐘舵��
+ */
+ private int calculateInStatus(List<ErpPurchaseOrderItemDO> items) {
+ boolean allIn = true;
+ boolean anyIn = false;
+
+ for (ErpPurchaseOrderItemDO item : items) {
+ BigDecimal count = item.getCount() != null ? item.getCount() : BigDecimal.ZERO;
+ BigDecimal inCount = item.getInCount() != null ? item.getInCount() : BigDecimal.ZERO;
+
+ if (inCount.compareTo(BigDecimal.ZERO) > 0) {
+ anyIn = true;
+ }
+ if (inCount.compareTo(count) < 0) {
+ allIn = false;
+ }
+ }
+
+ if (allIn) {
+ return ErpPurchaseOrderInStatusEnum.ALL_IN.getStatus();
+ } else if (anyIn) {
+ return ErpPurchaseOrderInStatusEnum.PART_IN.getStatus();
+ } else {
+ return ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus();
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long confirmReceipt(Long id) {
+ // 1. 鏍¢獙閲囪喘璁㈠崟瀛樺湪涓斿凡瀹℃牳
+ ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrder(id);
+
+ // 2. 鏍¢獙閲囪喘璁㈠崟鏈叏閮ㄥ叆搴�
+ if (ErpPurchaseOrderInStatusEnum.ALL_IN.getStatus().equals(purchaseOrder.getInStatus())) {
+ throw exception(PURCHASE_ORDER_ALL_IN);
+ }
+
+ // 3. 璋冪敤 MES 鎺ュ彛鐢熸垚鍒拌揣閫氱煡鍗�
+ Long arrivalNoticeId = arrivalNoticeApi.generateArrivalNoticeFromPurchaseOrder(id);
+ log.info("[confirmReceipt] 閲囪喘璁㈠崟({}) 纭鏀惰揣锛岀敓鎴愬埌璐ч�氱煡鍗�({})", id, arrivalNoticeId);
+
+ return arrivalNoticeId;
+ }
+
+}
\ No newline at end of file
--
Gitblit v1.9.3